1
# Copyright (C) 2005, 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
87
from bzrlib.util.configobj import configobj
103
POLICY_APPENDPATH = 2
107
POLICY_NORECURSE: 'norecurse',
108
POLICY_APPENDPATH: 'appendpath',
113
'norecurse': POLICY_NORECURSE,
114
'appendpath': POLICY_APPENDPATH,
118
STORE_LOCATION = POLICY_NONE
119
STORE_LOCATION_NORECURSE = POLICY_NORECURSE
120
STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH
125
def ConfigObj(*args, **kwargs):
127
if _ConfigObj is None:
128
class ConfigObj(configobj.ConfigObj):
130
def get_bool(self, section, key):
131
return self[section].as_bool(key)
133
def get_value(self, section, name):
134
# Try [] for the old DEFAULT section.
135
if section == "DEFAULT":
140
return self[section][name]
141
_ConfigObj = ConfigObj
142
return _ConfigObj(*args, **kwargs)
145
class Config(object):
146
"""A configuration policy - what username, editor, gpg needs etc."""
148
def get_editor(self):
149
"""Get the users pop up editor."""
150
raise NotImplementedError
152
def get_mail_client(self):
153
"""Get a mail client to use"""
154
selected_client = self.get_user_option('mail_client')
156
mail_client_class = {
157
None: mail_client.DefaultMail,
159
'evolution': mail_client.Evolution,
160
'kmail': mail_client.KMail,
161
'mutt': mail_client.Mutt,
162
'thunderbird': mail_client.Thunderbird,
164
'default': mail_client.DefaultMail,
165
'editor': mail_client.Editor,
166
'mapi': mail_client.MAPIClient,
167
'emacs-mailmode': mail_client.EmacsMailMode,
168
'xdg-email': mail_client.XDGEmail,
171
raise errors.UnknownMailClient(selected_client)
172
return mail_client_class(self)
174
def _get_signature_checking(self):
175
"""Template method to override signature checking policy."""
177
def _get_signing_policy(self):
178
"""Template method to override signature creation policy."""
180
def _get_user_option(self, option_name):
181
"""Template method to provide a user option."""
184
def get_user_option(self, option_name):
185
"""Get a generic option - no special process, no default."""
186
return self._get_user_option(option_name)
188
def gpg_signing_command(self):
189
"""What program should be used to sign signatures?"""
190
result = self._gpg_signing_command()
195
def _gpg_signing_command(self):
196
"""See gpg_signing_command()."""
199
def log_format(self):
200
"""What log format should be used"""
201
result = self._log_format()
206
def _log_format(self):
207
"""See log_format()."""
211
super(Config, self).__init__()
213
def post_commit(self):
214
"""An ordered list of python functions to call.
216
Each function takes branch, rev_id as parameters.
218
return self._post_commit()
220
def _post_commit(self):
221
"""See Config.post_commit."""
224
def user_email(self):
225
"""Return just the email component of a username."""
226
return extract_email_address(self.username())
229
"""Return email-style username.
231
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
233
$BZR_EMAIL can be set to override this (as well as the
234
deprecated $BZREMAIL), then
235
the concrete policy type is checked, and finally
237
If none is found, a reasonable default is (hopefully)
240
TODO: Check it's reasonably well-formed.
242
v = os.environ.get('BZR_EMAIL')
244
return v.decode(osutils.get_user_encoding())
246
v = self._get_user_id()
250
v = os.environ.get('EMAIL')
252
return v.decode(osutils.get_user_encoding())
254
name, email = _auto_user_id()
256
return '%s <%s>' % (name, email)
260
def signature_checking(self):
261
"""What is the current policy for signature checking?."""
262
policy = self._get_signature_checking()
263
if policy is not None:
265
return CHECK_IF_POSSIBLE
267
def signing_policy(self):
268
"""What is the current policy for signature checking?."""
269
policy = self._get_signing_policy()
270
if policy is not None:
272
return SIGN_WHEN_REQUIRED
274
def signature_needed(self):
275
"""Is a signature needed when committing ?."""
276
policy = self._get_signing_policy()
278
policy = self._get_signature_checking()
279
if policy is not None:
280
trace.warning("Please use create_signatures,"
281
" not check_signatures to set signing policy.")
282
if policy == CHECK_ALWAYS:
284
elif policy == SIGN_ALWAYS:
288
def get_alias(self, value):
289
return self._get_alias(value)
291
def _get_alias(self, value):
294
def get_nickname(self):
295
return self._get_nickname()
297
def _get_nickname(self):
300
def get_bzr_remote_path(self):
302
return os.environ['BZR_REMOTE_PATH']
304
path = self.get_user_option("bzr_remote_path")
310
class IniBasedConfig(Config):
311
"""A configuration policy that draws from ini files."""
313
def _get_parser(self, file=None):
314
if self._parser is not None:
317
input = self._get_filename()
321
self._parser = ConfigObj(input, encoding='utf-8')
322
except configobj.ConfigObjError, e:
323
raise errors.ParseConfigError(e.errors, e.config.filename)
326
def _get_matching_sections(self):
327
"""Return an ordered list of (section_name, extra_path) pairs.
329
If the section contains inherited configuration, extra_path is
330
a string containing the additional path components.
332
section = self._get_section()
333
if section is not None:
334
return [(section, '')]
338
def _get_section(self):
339
"""Override this to define the section used by the config."""
342
def _get_option_policy(self, section, option_name):
343
"""Return the policy for the given (section, option_name) pair."""
346
def _get_signature_checking(self):
347
"""See Config._get_signature_checking."""
348
policy = self._get_user_option('check_signatures')
350
return self._string_to_signature_policy(policy)
352
def _get_signing_policy(self):
353
"""See Config._get_signing_policy"""
354
policy = self._get_user_option('create_signatures')
356
return self._string_to_signing_policy(policy)
358
def _get_user_id(self):
359
"""Get the user id from the 'email' key in the current section."""
360
return self._get_user_option('email')
362
def _get_user_option(self, option_name):
363
"""See Config._get_user_option."""
364
for (section, extra_path) in self._get_matching_sections():
366
value = self._get_parser().get_value(section, option_name)
369
policy = self._get_option_policy(section, option_name)
370
if policy == POLICY_NONE:
372
elif policy == POLICY_NORECURSE:
373
# norecurse items only apply to the exact path
378
elif policy == POLICY_APPENDPATH:
380
value = urlutils.join(value, extra_path)
383
raise AssertionError('Unexpected config policy %r' % policy)
387
def _gpg_signing_command(self):
388
"""See Config.gpg_signing_command."""
389
return self._get_user_option('gpg_signing_command')
391
def _log_format(self):
392
"""See Config.log_format."""
393
return self._get_user_option('log_format')
395
def __init__(self, get_filename):
396
super(IniBasedConfig, self).__init__()
397
self._get_filename = get_filename
400
def _post_commit(self):
401
"""See Config.post_commit."""
402
return self._get_user_option('post_commit')
404
def _string_to_signature_policy(self, signature_string):
405
"""Convert a string to a signing policy."""
406
if signature_string.lower() == 'check-available':
407
return CHECK_IF_POSSIBLE
408
if signature_string.lower() == 'ignore':
410
if signature_string.lower() == 'require':
412
raise errors.BzrError("Invalid signatures policy '%s'"
415
def _string_to_signing_policy(self, signature_string):
416
"""Convert a string to a signing policy."""
417
if signature_string.lower() == 'when-required':
418
return SIGN_WHEN_REQUIRED
419
if signature_string.lower() == 'never':
421
if signature_string.lower() == 'always':
423
raise errors.BzrError("Invalid signing policy '%s'"
426
def _get_alias(self, value):
428
return self._get_parser().get_value("ALIASES",
433
def _get_nickname(self):
434
return self.get_user_option('nickname')
437
class GlobalConfig(IniBasedConfig):
438
"""The configuration that should be used for a specific location."""
440
def get_editor(self):
441
return self._get_user_option('editor')
444
super(GlobalConfig, self).__init__(config_filename)
446
def set_user_option(self, option, value):
447
"""Save option and its value in the configuration."""
448
# FIXME: RBC 20051029 This should refresh the parser and also take a
449
# file lock on bazaar.conf.
450
conf_dir = os.path.dirname(self._get_filename())
451
ensure_config_dir_exists(conf_dir)
452
if 'DEFAULT' not in self._get_parser():
453
self._get_parser()['DEFAULT'] = {}
454
self._get_parser()['DEFAULT'][option] = value
455
f = open(self._get_filename(), 'wb')
456
self._get_parser().write(f)
460
class LocationConfig(IniBasedConfig):
461
"""A configuration object that gives the policy for a location."""
463
def __init__(self, location):
464
name_generator = locations_config_filename
465
if (not os.path.exists(name_generator()) and
466
os.path.exists(branches_config_filename())):
467
if sys.platform == 'win32':
468
trace.warning('Please rename %s to %s'
469
% (branches_config_filename(),
470
locations_config_filename()))
472
trace.warning('Please rename ~/.bazaar/branches.conf'
473
' to ~/.bazaar/locations.conf')
474
name_generator = branches_config_filename
475
super(LocationConfig, self).__init__(name_generator)
476
# local file locations are looked up by local path, rather than
477
# by file url. This is because the config file is a user
478
# file, and we would rather not expose the user to file urls.
479
if location.startswith('file://'):
480
location = urlutils.local_path_from_url(location)
481
self.location = location
483
def _get_matching_sections(self):
484
"""Return an ordered list of section names matching this location."""
485
sections = self._get_parser()
486
location_names = self.location.split('/')
487
if self.location.endswith('/'):
488
del location_names[-1]
490
for section in sections:
491
# location is a local path if possible, so we need
492
# to convert 'file://' urls to local paths if necessary.
493
# This also avoids having file:///path be a more exact
494
# match than '/path'.
495
if section.startswith('file://'):
496
section_path = urlutils.local_path_from_url(section)
498
section_path = section
499
section_names = section_path.split('/')
500
if section.endswith('/'):
501
del section_names[-1]
502
names = zip(location_names, section_names)
505
if not fnmatch(name[0], name[1]):
510
# so, for the common prefix they matched.
511
# if section is longer, no match.
512
if len(section_names) > len(location_names):
514
matches.append((len(section_names), section,
515
'/'.join(location_names[len(section_names):])))
516
matches.sort(reverse=True)
518
for (length, section, extra_path) in matches:
519
sections.append((section, extra_path))
520
# should we stop looking for parent configs here?
522
if self._get_parser()[section].as_bool('ignore_parents'):
528
def _get_option_policy(self, section, option_name):
529
"""Return the policy for the given (section, option_name) pair."""
530
# check for the old 'recurse=False' flag
532
recurse = self._get_parser()[section].as_bool('recurse')
536
return POLICY_NORECURSE
538
policy_key = option_name + ':policy'
540
policy_name = self._get_parser()[section][policy_key]
544
return _policy_value[policy_name]
546
def _set_option_policy(self, section, option_name, option_policy):
547
"""Set the policy for the given option name in the given section."""
548
# The old recurse=False option affects all options in the
549
# section. To handle multiple policies in the section, we
550
# need to convert it to a policy_norecurse key.
552
recurse = self._get_parser()[section].as_bool('recurse')
556
symbol_versioning.warn(
557
'The recurse option is deprecated as of 0.14. '
558
'The section "%s" has been converted to use policies.'
561
del self._get_parser()[section]['recurse']
563
for key in self._get_parser()[section].keys():
564
if not key.endswith(':policy'):
565
self._get_parser()[section][key +
566
':policy'] = 'norecurse'
568
policy_key = option_name + ':policy'
569
policy_name = _policy_name[option_policy]
570
if policy_name is not None:
571
self._get_parser()[section][policy_key] = policy_name
573
if policy_key in self._get_parser()[section]:
574
del self._get_parser()[section][policy_key]
576
def set_user_option(self, option, value, store=STORE_LOCATION):
577
"""Save option and its value in the configuration."""
578
assert store in [STORE_LOCATION,
579
STORE_LOCATION_NORECURSE,
580
STORE_LOCATION_APPENDPATH], 'bad storage policy'
581
# FIXME: RBC 20051029 This should refresh the parser and also take a
582
# file lock on locations.conf.
583
conf_dir = os.path.dirname(self._get_filename())
584
ensure_config_dir_exists(conf_dir)
585
location = self.location
586
if location.endswith('/'):
587
location = location[:-1]
588
if (not location in self._get_parser() and
589
not location + '/' in self._get_parser()):
590
self._get_parser()[location]={}
591
elif location + '/' in self._get_parser():
592
location = location + '/'
593
self._get_parser()[location][option]=value
594
# the allowed values of store match the config policies
595
self._set_option_policy(location, option, store)
596
self._get_parser().write(file(self._get_filename(), 'wb'))
599
class BranchConfig(Config):
600
"""A configuration object giving the policy for a branch."""
602
def _get_branch_data_config(self):
603
if self._branch_data_config is None:
604
self._branch_data_config = TreeConfig(self.branch)
605
return self._branch_data_config
607
def _get_location_config(self):
608
if self._location_config is None:
609
self._location_config = LocationConfig(self.branch.base)
610
return self._location_config
612
def _get_global_config(self):
613
if self._global_config is None:
614
self._global_config = GlobalConfig()
615
return self._global_config
617
def _get_best_value(self, option_name):
618
"""This returns a user option from local, tree or global config.
620
They are tried in that order. Use get_safe_value if trusted values
623
for source in self.option_sources:
624
value = getattr(source(), option_name)()
625
if value is not None:
629
def _get_safe_value(self, option_name):
630
"""This variant of get_best_value never returns untrusted values.
632
It does not return values from the branch data, because the branch may
633
not be controlled by the user.
635
We may wish to allow locations.conf to control whether branches are
636
trusted in the future.
638
for source in (self._get_location_config, self._get_global_config):
639
value = getattr(source(), option_name)()
640
if value is not None:
644
def _get_user_id(self):
645
"""Return the full user id for the branch.
647
e.g. "John Hacker <jhacker@foo.org>"
648
This is looked up in the email controlfile for the branch.
651
return (self.branch.control_files.get_utf8("email")
653
.decode(osutils.get_user_encoding())
655
except errors.NoSuchFile, e:
658
return self._get_best_value('_get_user_id')
660
def _get_signature_checking(self):
661
"""See Config._get_signature_checking."""
662
return self._get_best_value('_get_signature_checking')
664
def _get_signing_policy(self):
665
"""See Config._get_signing_policy."""
666
return self._get_best_value('_get_signing_policy')
668
def _get_user_option(self, option_name):
669
"""See Config._get_user_option."""
670
for source in self.option_sources:
671
value = source()._get_user_option(option_name)
672
if value is not None:
676
def set_user_option(self, name, value, store=STORE_BRANCH,
678
if store == STORE_BRANCH:
679
self._get_branch_data_config().set_option(value, name)
680
elif store == STORE_GLOBAL:
681
self._get_global_config().set_user_option(name, value)
683
self._get_location_config().set_user_option(name, value, store)
686
if store in (STORE_GLOBAL, STORE_BRANCH):
687
mask_value = self._get_location_config().get_user_option(name)
688
if mask_value is not None:
689
trace.warning('Value "%s" is masked by "%s" from'
690
' locations.conf', value, mask_value)
692
if store == STORE_GLOBAL:
693
branch_config = self._get_branch_data_config()
694
mask_value = branch_config.get_user_option(name)
695
if mask_value is not None:
696
trace.warning('Value "%s" is masked by "%s" from'
697
' branch.conf', value, mask_value)
700
def _gpg_signing_command(self):
701
"""See Config.gpg_signing_command."""
702
return self._get_safe_value('_gpg_signing_command')
704
def __init__(self, branch):
705
super(BranchConfig, self).__init__()
706
self._location_config = None
707
self._branch_data_config = None
708
self._global_config = None
710
self.option_sources = (self._get_location_config,
711
self._get_branch_data_config,
712
self._get_global_config)
714
def _post_commit(self):
715
"""See Config.post_commit."""
716
return self._get_safe_value('_post_commit')
718
def _get_nickname(self):
719
value = self._get_explicit_nickname()
720
if value is not None:
722
return urlutils.unescape(self.branch.base.split('/')[-2])
724
def has_explicit_nickname(self):
725
"""Return true if a nickname has been explicitly assigned."""
726
return self._get_explicit_nickname() is not None
728
def _get_explicit_nickname(self):
729
return self._get_best_value('_get_nickname')
731
def _log_format(self):
732
"""See Config.log_format."""
733
return self._get_best_value('_log_format')
736
def ensure_config_dir_exists(path=None):
737
"""Make sure a configuration directory exists.
738
This makes sure that the directory exists.
739
On windows, since configuration directories are 2 levels deep,
740
it makes sure both the directory and the parent directory exists.
744
if not os.path.isdir(path):
745
if sys.platform == 'win32':
746
parent_dir = os.path.dirname(path)
747
if not os.path.isdir(parent_dir):
748
trace.mutter('creating config parent directory: %r', parent_dir)
750
trace.mutter('creating config directory: %r', path)
755
"""Return per-user configuration directory.
757
By default this is ~/.bazaar/
759
TODO: Global option --config-dir to override this.
761
base = os.environ.get('BZR_HOME', None)
762
if sys.platform == 'win32':
764
base = win32utils.get_appdata_location_unicode()
766
base = os.environ.get('HOME', None)
768
raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
770
return osutils.pathjoin(base, 'bazaar', '2.0')
772
# cygwin, linux, and darwin all have a $HOME directory
774
base = os.path.expanduser("~")
775
return osutils.pathjoin(base, ".bazaar")
778
def config_filename():
779
"""Return per-user configuration ini file filename."""
780
return osutils.pathjoin(config_dir(), 'bazaar.conf')
783
def branches_config_filename():
784
"""Return per-user configuration ini file filename."""
785
return osutils.pathjoin(config_dir(), 'branches.conf')
788
def locations_config_filename():
789
"""Return per-user configuration ini file filename."""
790
return osutils.pathjoin(config_dir(), 'locations.conf')
793
def authentication_config_filename():
794
"""Return per-user authentication ini file filename."""
795
return osutils.pathjoin(config_dir(), 'authentication.conf')
798
def user_ignore_config_filename():
799
"""Return the user default ignore filename"""
800
return osutils.pathjoin(config_dir(), 'ignore')
804
"""Calculate automatic user identification.
806
Returns (realname, email).
808
Only used when none is set in the environment or the id file.
810
This previously used the FQDN as the default domain, but that can
811
be very slow on machines where DNS is broken. So now we simply
816
if sys.platform == 'win32':
817
name = win32utils.get_user_name_unicode()
819
raise errors.BzrError("Cannot autodetect user name.\n"
820
"Please, set your name with command like:\n"
821
'bzr whoami "Your Name <name@domain.com>"')
822
host = win32utils.get_host_name_unicode()
824
host = socket.gethostname()
825
return name, (name + '@' + host)
830
w = pwd.getpwuid(uid)
832
# we try utf-8 first, because on many variants (like Linux),
833
# /etc/passwd "should" be in utf-8, and because it's unlikely to give
834
# false positives. (many users will have their user encoding set to
835
# latin-1, which cannot raise UnicodeError.)
837
gecos = w.pw_gecos.decode('utf-8')
841
encoding = osutils.get_user_encoding()
842
gecos = w.pw_gecos.decode(encoding)
844
raise errors.BzrCommandError('Unable to determine your name. '
845
'Use "bzr whoami" to set it.')
847
username = w.pw_name.decode(encoding)
849
raise errors.BzrCommandError('Unable to determine your name. '
850
'Use "bzr whoami" to set it.')
852
comma = gecos.find(',')
856
realname = gecos[:comma]
863
user_encoding = osutils.get_user_encoding()
864
realname = username = getpass.getuser().decode(user_encoding)
865
except UnicodeDecodeError:
866
raise errors.BzrError("Can't decode username as %s." % \
869
return realname, (username + '@' + socket.gethostname())
872
def parse_username(username):
873
"""Parse e-mail username and return a (name, address) tuple."""
874
match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
876
return (username, '')
878
return (match.group(1), match.group(2))
881
def extract_email_address(e):
882
"""Return just the address part of an email string.
884
That is just the user@domain part, nothing else.
885
This part is required to contain only ascii characters.
886
If it can't be extracted, raises an error.
888
>>> extract_email_address('Jane Tester <jane@test.com>')
891
name, email = parse_username(e)
893
raise errors.NoEmailInUsername(e)
897
class TreeConfig(IniBasedConfig):
898
"""Branch configuration data associated with its contents, not location"""
900
def __init__(self, branch):
903
def _get_parser(self, file=None):
905
return IniBasedConfig._get_parser(file)
906
return self._get_config()
908
def _get_config(self):
910
obj = ConfigObj(self.branch.control_files.get('branch.conf'),
912
except errors.NoSuchFile:
913
obj = ConfigObj(encoding='utf=8')
916
def get_option(self, name, section=None, default=None):
917
self.branch.lock_read()
919
obj = self._get_config()
921
if section is not None:
930
def set_option(self, value, name, section=None):
931
"""Set a per-branch configuration option"""
932
self.branch.lock_write()
934
cfg_obj = self._get_config()
939
obj = cfg_obj[section]
941
cfg_obj[section] = {}
942
obj = cfg_obj[section]
944
out_file = StringIO()
945
cfg_obj.write(out_file)
947
self.branch.control_files.put('branch.conf', out_file)
952
class AuthenticationConfig(object):
953
"""The authentication configuration file based on a ini file.
955
Implements the authentication.conf file described in
956
doc/developers/authentication-ring.txt.
959
def __init__(self, _file=None):
960
self._config = None # The ConfigObj
962
self._filename = authentication_config_filename()
963
self._input = self._filename = authentication_config_filename()
965
# Tests can provide a string as _file
966
self._filename = None
969
def _get_config(self):
970
if self._config is not None:
973
# FIXME: Should we validate something here ? Includes: empty
974
# sections are useless, at least one of
975
# user/password/password_encoding should be defined, etc.
977
# Note: the encoding below declares that the file itself is utf-8
978
# encoded, but the values in the ConfigObj are always Unicode.
979
self._config = ConfigObj(self._input, encoding='utf-8')
980
except configobj.ConfigObjError, e:
981
raise errors.ParseConfigError(e.errors, e.config.filename)
985
"""Save the config file, only tests should use it for now."""
986
conf_dir = os.path.dirname(self._filename)
987
ensure_config_dir_exists(conf_dir)
988
self._get_config().write(file(self._filename, 'wb'))
990
def _set_option(self, section_name, option_name, value):
991
"""Set an authentication configuration option"""
992
conf = self._get_config()
993
section = conf.get(section_name)
996
section = conf[section]
997
section[option_name] = value
1000
def get_credentials(self, scheme, host, port=None, user=None, path=None):
1001
"""Returns the matching credentials from authentication.conf file.
1003
:param scheme: protocol
1005
:param host: the server address
1007
:param port: the associated port (optional)
1009
:param user: login (optional)
1011
:param path: the absolute path on the server (optional)
1013
:return: A dict containing the matching credentials or None.
1015
- name: the section name of the credentials in the
1016
authentication.conf file,
1017
- user: can't de different from the provided user if any,
1018
- password: the decoded password, could be None if the credential
1019
defines only the user
1020
- verify_certificates: https specific, True if the server
1021
certificate should be verified, False otherwise.
1024
for auth_def_name, auth_def in self._get_config().items():
1025
a_scheme, a_host, a_user, a_path = map(
1026
auth_def.get, ['scheme', 'host', 'user', 'path'])
1029
a_port = auth_def.as_int('port')
1033
raise ValueError("'port' not numeric in %s" % auth_def_name)
1035
a_verify_certificates = auth_def.as_bool('verify_certificates')
1037
a_verify_certificates = True
1040
"'verify_certificates' not boolean in %s" % auth_def_name)
1043
if a_scheme is not None and scheme != a_scheme:
1045
if a_host is not None:
1046
if not (host == a_host
1047
or (a_host.startswith('.') and host.endswith(a_host))):
1049
if a_port is not None and port != a_port:
1051
if (a_path is not None and path is not None
1052
and not path.startswith(a_path)):
1054
if (a_user is not None and user is not None
1055
and a_user != user):
1056
# Never contradict the caller about the user to be used
1061
credentials = dict(name=auth_def_name,
1062
user=a_user, password=auth_def['password'],
1063
verify_certificates=a_verify_certificates)
1064
self.decode_password(credentials,
1065
auth_def.get('password_encoding', None))
1066
if 'auth' in debug.debug_flags:
1067
trace.mutter("Using authentication section: %r", auth_def_name)
1072
def get_user(self, scheme, host, port=None,
1073
realm=None, path=None, prompt=None):
1074
"""Get a user from authentication file.
1076
:param scheme: protocol
1078
:param host: the server address
1080
:param port: the associated port (optional)
1082
:param realm: the realm sent by the server (optional)
1084
:param path: the absolute path on the server (optional)
1086
:return: The found user.
1088
credentials = self.get_credentials(scheme, host, port, user=None,
1090
if credentials is not None:
1091
user = credentials['user']
1096
def get_password(self, scheme, host, user, port=None,
1097
realm=None, path=None, prompt=None):
1098
"""Get a password from authentication file or prompt the user for one.
1100
:param scheme: protocol
1102
:param host: the server address
1104
:param port: the associated port (optional)
1108
:param realm: the realm sent by the server (optional)
1110
:param path: the absolute path on the server (optional)
1112
:return: The found password or the one entered by the user.
1114
credentials = self.get_credentials(scheme, host, port, user, path)
1115
if credentials is not None:
1116
password = credentials['password']
1119
# Prompt user only if we could't find a password
1120
if password is None:
1122
# Create a default prompt suitable for most of the cases
1123
prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1124
# Special handling for optional fields in the prompt
1125
if port is not None:
1126
prompt_host = '%s:%d' % (host, port)
1129
password = ui.ui_factory.get_password(prompt,
1130
host=prompt_host, user=user)
1133
def decode_password(self, credentials, encoding):