~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Robert Collins
  • Date: 2010-04-08 04:34:03 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5139.
  • Revision ID: robertc@robertcollins.net-20100408043403-56z0d07vdqrx7f3t
Update bugfix for 528114 to trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
3
#            and others
4
4
#
14
14
#
15
15
# You should have received a copy of the GNU General Public License
16
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
 
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
"""Configuration that affects the behaviour of Bazaar.
20
20
 
37
37
[/home/robertc/source]
38
38
recurse=False|True(default)
39
39
email= as above
40
 
check_signatures= as above 
 
40
check_signatures= as above
41
41
create_signatures= as above.
42
42
 
43
43
explanation of options
45
45
editor - this option sets the pop up editor to use during commits.
46
46
email - this option sets the user id bzr will use when committing.
47
47
check_signatures - this option controls whether bzr will require good gpg
48
 
                   signatures, ignore them, or check them if they are 
 
48
                   signatures, ignore them, or check them if they are
49
49
                   present.
50
 
create_signatures - this option controls whether bzr will always create 
 
50
create_signatures - this option controls whether bzr will always create
51
51
                    gpg signatures, never create them, or create them if the
52
52
                    branch is configured to require them.
53
53
log_format - this option sets the default log format.  Possible values are
78
78
    errors,
79
79
    mail_client,
80
80
    osutils,
 
81
    registry,
81
82
    symbol_versioning,
82
83
    trace,
83
84
    ui,
121
122
STORE_BRANCH = 3
122
123
STORE_GLOBAL = 4
123
124
 
124
 
 
125
 
class ConfigObj(configobj.ConfigObj):
126
 
 
127
 
    def get_bool(self, section, key):
128
 
        return self[section].as_bool(key)
129
 
 
130
 
    def get_value(self, section, name):
131
 
        # Try [] for the old DEFAULT section.
132
 
        if section == "DEFAULT":
133
 
            try:
134
 
                return self[name]
135
 
            except KeyError:
136
 
                pass
137
 
        return self[section][name]
 
125
_ConfigObj = None
 
126
def ConfigObj(*args, **kwargs):
 
127
    global _ConfigObj
 
128
    if _ConfigObj is None:
 
129
        class ConfigObj(configobj.ConfigObj):
 
130
 
 
131
            def get_bool(self, section, key):
 
132
                return self[section].as_bool(key)
 
133
 
 
134
            def get_value(self, section, name):
 
135
                # Try [] for the old DEFAULT section.
 
136
                if section == "DEFAULT":
 
137
                    try:
 
138
                        return self[name]
 
139
                    except KeyError:
 
140
                        pass
 
141
                return self[section][name]
 
142
        _ConfigObj = ConfigObj
 
143
    return _ConfigObj(*args, **kwargs)
138
144
 
139
145
 
140
146
class Config(object):
141
147
    """A configuration policy - what username, editor, gpg needs etc."""
142
148
 
 
149
    def __init__(self):
 
150
        super(Config, self).__init__()
 
151
 
143
152
    def get_editor(self):
144
153
        """Get the users pop up editor."""
145
154
        raise NotImplementedError
146
155
 
 
156
    def get_change_editor(self, old_tree, new_tree):
 
157
        from bzrlib import diff
 
158
        cmd = self._get_change_editor()
 
159
        if cmd is None:
 
160
            return None
 
161
        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
 
162
                                             sys.stdout)
 
163
 
 
164
 
147
165
    def get_mail_client(self):
148
166
        """Get a mail client to use"""
149
167
        selected_client = self.get_user_option('mail_client')
 
168
        _registry = mail_client.mail_client_registry
150
169
        try:
151
 
            mail_client_class = {
152
 
                None: mail_client.DefaultMail,
153
 
                # Specific clients
154
 
                'evolution': mail_client.Evolution,
155
 
                'kmail': mail_client.KMail,
156
 
                'mutt': mail_client.Mutt,
157
 
                'thunderbird': mail_client.Thunderbird,
158
 
                # Generic options
159
 
                'default': mail_client.DefaultMail,
160
 
                'editor': mail_client.Editor,
161
 
                'mapi': mail_client.MAPIClient,
162
 
                'xdg-email': mail_client.XDGEmail,
163
 
            }[selected_client]
 
170
            mail_client_class = _registry.get(selected_client)
164
171
        except KeyError:
165
172
            raise errors.UnknownMailClient(selected_client)
166
173
        return mail_client_class(self)
179
186
        """Get a generic option - no special process, no default."""
180
187
        return self._get_user_option(option_name)
181
188
 
 
189
    def get_user_option_as_bool(self, option_name):
 
190
        """Get a generic option as a boolean - no special process, no default.
 
191
 
 
192
        :return None if the option doesn't exist or its value can't be
 
193
            interpreted as a boolean. Returns True or False otherwise.
 
194
        """
 
195
        s = self._get_user_option(option_name)
 
196
        return ui.bool_from_string(s)
 
197
 
 
198
    def get_user_option_as_list(self, option_name):
 
199
        """Get a generic option as a list - no special process, no default.
 
200
 
 
201
        :return None if the option doesn't exist. Returns the value as a list
 
202
            otherwise.
 
203
        """
 
204
        l = self._get_user_option(option_name)
 
205
        if isinstance(l, (str, unicode)):
 
206
            # A single value, most probably the user forgot the final ','
 
207
            l = [l]
 
208
        return l
 
209
 
182
210
    def gpg_signing_command(self):
183
211
        """What program should be used to sign signatures?"""
184
212
        result = self._gpg_signing_command()
201
229
        """See log_format()."""
202
230
        return None
203
231
 
204
 
    def __init__(self):
205
 
        super(Config, self).__init__()
206
 
 
207
232
    def post_commit(self):
208
233
        """An ordered list of python functions to call.
209
234
 
221
246
 
222
247
    def username(self):
223
248
        """Return email-style username.
224
 
    
 
249
 
225
250
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
226
 
        
 
251
 
227
252
        $BZR_EMAIL can be set to override this (as well as the
228
253
        deprecated $BZREMAIL), then
229
254
        the concrete policy type is checked, and finally
230
255
        $EMAIL is examined.
231
256
        If none is found, a reasonable default is (hopefully)
232
257
        created.
233
 
    
 
258
 
234
259
        TODO: Check it's reasonably well-formed.
235
260
        """
236
261
        v = os.environ.get('BZR_EMAIL')
237
262
        if v:
238
 
            return v.decode(bzrlib.user_encoding)
 
263
            return v.decode(osutils.get_user_encoding())
239
264
 
240
265
        v = self._get_user_id()
241
266
        if v:
243
268
 
244
269
        v = os.environ.get('EMAIL')
245
270
        if v:
246
 
            return v.decode(bzrlib.user_encoding)
 
271
            return v.decode(osutils.get_user_encoding())
247
272
 
248
273
        name, email = _auto_user_id()
249
274
        if name:
300
325
                path = 'bzr'
301
326
            return path
302
327
 
 
328
    def suppress_warning(self, warning):
 
329
        """Should the warning be suppressed or emitted.
 
330
 
 
331
        :param warning: The name of the warning being tested.
 
332
 
 
333
        :returns: True if the warning should be suppressed, False otherwise.
 
334
        """
 
335
        warnings = self.get_user_option_as_list('suppress_warnings')
 
336
        if warnings is None or warning not in warnings:
 
337
            return False
 
338
        else:
 
339
            return True
 
340
 
303
341
 
304
342
class IniBasedConfig(Config):
305
343
    """A configuration policy that draws from ini files."""
306
344
 
 
345
    def __init__(self, get_filename):
 
346
        super(IniBasedConfig, self).__init__()
 
347
        self._get_filename = get_filename
 
348
        self._parser = None
 
349
 
307
350
    def _get_parser(self, file=None):
308
351
        if self._parser is not None:
309
352
            return self._parser
337
380
        """Return the policy for the given (section, option_name) pair."""
338
381
        return POLICY_NONE
339
382
 
 
383
    def _get_change_editor(self):
 
384
        return self.get_user_option('change_editor')
 
385
 
340
386
    def _get_signature_checking(self):
341
387
        """See Config._get_signature_checking."""
342
388
        policy = self._get_user_option('check_signatures')
386
432
        """See Config.log_format."""
387
433
        return self._get_user_option('log_format')
388
434
 
389
 
    def __init__(self, get_filename):
390
 
        super(IniBasedConfig, self).__init__()
391
 
        self._get_filename = get_filename
392
 
        self._parser = None
393
 
        
394
435
    def _post_commit(self):
395
436
        """See Config.post_commit."""
396
437
        return self._get_user_option('post_commit')
419
460
 
420
461
    def _get_alias(self, value):
421
462
        try:
422
 
            return self._get_parser().get_value("ALIASES", 
 
463
            return self._get_parser().get_value("ALIASES",
423
464
                                                value)
424
465
        except KeyError:
425
466
            pass
439
480
 
440
481
    def set_user_option(self, option, value):
441
482
        """Save option and its value in the configuration."""
 
483
        self._set_option(option, value, 'DEFAULT')
 
484
 
 
485
    def get_aliases(self):
 
486
        """Return the aliases section."""
 
487
        if 'ALIASES' in self._get_parser():
 
488
            return self._get_parser()['ALIASES']
 
489
        else:
 
490
            return {}
 
491
 
 
492
    def set_alias(self, alias_name, alias_command):
 
493
        """Save the alias in the configuration."""
 
494
        self._set_option(alias_name, alias_command, 'ALIASES')
 
495
 
 
496
    def unset_alias(self, alias_name):
 
497
        """Unset an existing alias."""
 
498
        aliases = self._get_parser().get('ALIASES')
 
499
        if not aliases or alias_name not in aliases:
 
500
            raise errors.NoSuchAlias(alias_name)
 
501
        del aliases[alias_name]
 
502
        self._write_config_file()
 
503
 
 
504
    def _set_option(self, option, value, section):
442
505
        # FIXME: RBC 20051029 This should refresh the parser and also take a
443
506
        # file lock on bazaar.conf.
444
507
        conf_dir = os.path.dirname(self._get_filename())
445
508
        ensure_config_dir_exists(conf_dir)
446
 
        if 'DEFAULT' not in self._get_parser():
447
 
            self._get_parser()['DEFAULT'] = {}
448
 
        self._get_parser()['DEFAULT'][option] = value
449
 
        f = open(self._get_filename(), 'wb')
 
509
        self._get_parser().setdefault(section, {})[option] = value
 
510
        self._write_config_file()
 
511
 
 
512
    def _write_config_file(self):
 
513
        path = self._get_filename()
 
514
        f = osutils.open_with_ownership(path, 'wb')
450
515
        self._get_parser().write(f)
451
516
        f.close()
452
517
 
569
634
 
570
635
    def set_user_option(self, option, value, store=STORE_LOCATION):
571
636
        """Save option and its value in the configuration."""
572
 
        assert store in [STORE_LOCATION,
 
637
        if store not in [STORE_LOCATION,
573
638
                         STORE_LOCATION_NORECURSE,
574
 
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
 
639
                         STORE_LOCATION_APPENDPATH]:
 
640
            raise ValueError('bad storage policy %r for %r' %
 
641
                (store, option))
575
642
        # FIXME: RBC 20051029 This should refresh the parser and also take a
576
643
        # file lock on locations.conf.
577
644
        conf_dir = os.path.dirname(self._get_filename())
622
689
 
623
690
    def _get_safe_value(self, option_name):
624
691
        """This variant of get_best_value never returns untrusted values.
625
 
        
 
692
 
626
693
        It does not return values from the branch data, because the branch may
627
694
        not be controlled by the user.
628
695
 
637
704
 
638
705
    def _get_user_id(self):
639
706
        """Return the full user id for the branch.
640
 
    
641
 
        e.g. "John Hacker <jhacker@foo.org>"
 
707
 
 
708
        e.g. "John Hacker <jhacker@example.com>"
642
709
        This is looked up in the email controlfile for the branch.
643
710
        """
644
711
        try:
645
 
            return (self.branch.control_files.get_utf8("email") 
646
 
                    .read()
647
 
                    .decode(bzrlib.user_encoding)
 
712
            return (self.branch._transport.get_bytes("email")
 
713
                    .decode(osutils.get_user_encoding())
648
714
                    .rstrip("\r\n"))
649
715
        except errors.NoSuchFile, e:
650
716
            pass
651
 
        
 
717
 
652
718
        return self._get_best_value('_get_user_id')
653
719
 
 
720
    def _get_change_editor(self):
 
721
        return self._get_best_value('_get_change_editor')
 
722
 
654
723
    def _get_signature_checking(self):
655
724
        """See Config._get_signature_checking."""
656
725
        return self._get_best_value('_get_signature_checking')
690
759
                        trace.warning('Value "%s" is masked by "%s" from'
691
760
                                      ' branch.conf', value, mask_value)
692
761
 
693
 
 
694
762
    def _gpg_signing_command(self):
695
763
        """See Config.gpg_signing_command."""
696
764
        return self._get_safe_value('_gpg_signing_command')
697
 
        
 
765
 
698
766
    def __init__(self, branch):
699
767
        super(BranchConfig, self).__init__()
700
768
        self._location_config = None
701
769
        self._branch_data_config = None
702
770
        self._global_config = None
703
771
        self.branch = branch
704
 
        self.option_sources = (self._get_location_config, 
 
772
        self.option_sources = (self._get_location_config,
705
773
                               self._get_branch_data_config,
706
774
                               self._get_global_config)
707
775
 
742
810
                trace.mutter('creating config parent directory: %r', parent_dir)
743
811
            os.mkdir(parent_dir)
744
812
        trace.mutter('creating config directory: %r', path)
745
 
        os.mkdir(path)
 
813
        osutils.mkdir_with_ownership(path)
746
814
 
747
815
 
748
816
def config_dir():
749
817
    """Return per-user configuration directory.
750
818
 
751
819
    By default this is ~/.bazaar/
752
 
    
 
820
 
753
821
    TODO: Global option --config-dir to override this.
754
822
    """
755
823
    base = os.environ.get('BZR_HOME', None)
794
862
    return osutils.pathjoin(config_dir(), 'ignore')
795
863
 
796
864
 
 
865
def crash_dir():
 
866
    """Return the directory name to store crash files.
 
867
 
 
868
    This doesn't implicitly create it.
 
869
 
 
870
    On Windows it's in the config directory; elsewhere it's /var/crash
 
871
    which may be monitored by apport.  It can be overridden by
 
872
    $APPORT_CRASH_DIR.
 
873
    """
 
874
    if sys.platform == 'win32':
 
875
        return osutils.pathjoin(config_dir(), 'Crash')
 
876
    else:
 
877
        # XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp
 
878
        # 2010-01-31
 
879
        return os.environ.get('APPORT_CRASH_DIR', '/var/crash')
 
880
 
 
881
 
 
882
def xdg_cache_dir():
 
883
    # See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
 
884
    # Possibly this should be different on Windows?
 
885
    e = os.environ.get('XDG_CACHE_DIR', None)
 
886
    if e:
 
887
        return e
 
888
    else:
 
889
        return os.path.expanduser('~/.cache')
 
890
 
 
891
 
797
892
def _auto_user_id():
798
893
    """Calculate automatic user identification.
799
894
 
821
916
    try:
822
917
        import pwd
823
918
        uid = os.getuid()
824
 
        w = pwd.getpwuid(uid)
 
919
        try:
 
920
            w = pwd.getpwuid(uid)
 
921
        except KeyError:
 
922
            raise errors.BzrCommandError('Unable to determine your name.  '
 
923
                'Please use "bzr whoami" to set it.')
825
924
 
826
925
        # we try utf-8 first, because on many variants (like Linux),
827
926
        # /etc/passwd "should" be in utf-8, and because it's unlikely to give
832
931
            encoding = 'utf-8'
833
932
        except UnicodeError:
834
933
            try:
835
 
                gecos = w.pw_gecos.decode(bzrlib.user_encoding)
836
 
                encoding = bzrlib.user_encoding
 
934
                encoding = osutils.get_user_encoding()
 
935
                gecos = w.pw_gecos.decode(encoding)
837
936
            except UnicodeError:
838
937
                raise errors.BzrCommandError('Unable to determine your name.  '
839
938
                   'Use "bzr whoami" to set it.')
854
953
    except ImportError:
855
954
        import getpass
856
955
        try:
857
 
            realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
956
            user_encoding = osutils.get_user_encoding()
 
957
            realname = username = getpass.getuser().decode(user_encoding)
858
958
        except UnicodeDecodeError:
859
959
            raise errors.BzrError("Can't decode username as %s." % \
860
 
                    bzrlib.user_encoding)
 
960
                    user_encoding)
861
961
 
862
962
    return realname, (username + '@' + socket.gethostname())
863
963
 
874
974
def extract_email_address(e):
875
975
    """Return just the address part of an email string.
876
976
 
877
 
    That is just the user@domain part, nothing else. 
 
977
    That is just the user@domain part, nothing else.
878
978
    This part is required to contain only ascii characters.
879
979
    If it can't be extracted, raises an error.
880
980
 
890
990
class TreeConfig(IniBasedConfig):
891
991
    """Branch configuration data associated with its contents, not location"""
892
992
 
 
993
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
 
994
 
893
995
    def __init__(self, branch):
 
996
        self._config = branch._get_config()
894
997
        self.branch = branch
895
998
 
896
999
    def _get_parser(self, file=None):
897
1000
        if file is not None:
898
1001
            return IniBasedConfig._get_parser(file)
899
 
        return self._get_config()
900
 
 
901
 
    def _get_config(self):
902
 
        try:
903
 
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
904
 
                            encoding='utf-8')
905
 
        except errors.NoSuchFile:
906
 
            obj = ConfigObj(encoding='utf=8')
907
 
        return obj
 
1002
        return self._config._get_configobj()
908
1003
 
909
1004
    def get_option(self, name, section=None, default=None):
910
1005
        self.branch.lock_read()
911
1006
        try:
912
 
            obj = self._get_config()
913
 
            try:
914
 
                if section is not None:
915
 
                    obj = obj[section]
916
 
                result = obj[name]
917
 
            except KeyError:
918
 
                result = default
 
1007
            return self._config.get_option(name, section, default)
919
1008
        finally:
920
1009
            self.branch.unlock()
921
 
        return result
922
1010
 
923
1011
    def set_option(self, value, name, section=None):
924
1012
        """Set a per-branch configuration option"""
925
1013
        self.branch.lock_write()
926
1014
        try:
927
 
            cfg_obj = self._get_config()
928
 
            if section is None:
929
 
                obj = cfg_obj
930
 
            else:
931
 
                try:
932
 
                    obj = cfg_obj[section]
933
 
                except KeyError:
934
 
                    cfg_obj[section] = {}
935
 
                    obj = cfg_obj[section]
936
 
            obj[name] = value
937
 
            out_file = StringIO()
938
 
            cfg_obj.write(out_file)
939
 
            out_file.seek(0)
940
 
            self.branch.control_files.put('branch.conf', out_file)
 
1015
            self._config.set_option(value, name, section)
941
1016
        finally:
942
1017
            self.branch.unlock()
943
1018
 
990
1065
        section[option_name] = value
991
1066
        self._save()
992
1067
 
993
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None):
 
1068
    def get_credentials(self, scheme, host, port=None, user=None, path=None, 
 
1069
                        realm=None):
994
1070
        """Returns the matching credentials from authentication.conf file.
995
1071
 
996
1072
        :param scheme: protocol
1002
1078
        :param user: login (optional)
1003
1079
 
1004
1080
        :param path: the absolute path on the server (optional)
 
1081
        
 
1082
        :param realm: the http authentication realm (optional)
1005
1083
 
1006
1084
        :return: A dict containing the matching credentials or None.
1007
1085
           This includes:
1008
1086
           - name: the section name of the credentials in the
1009
1087
             authentication.conf file,
1010
 
           - user: can't de different from the provided user if any,
 
1088
           - user: can't be different from the provided user if any,
 
1089
           - scheme: the server protocol,
 
1090
           - host: the server address,
 
1091
           - port: the server port (can be None),
 
1092
           - path: the absolute server path (can be None),
 
1093
           - realm: the http specific authentication realm (can be None),
1011
1094
           - password: the decoded password, could be None if the credential
1012
1095
             defines only the user
1013
1096
           - verify_certificates: https specific, True if the server
1015
1098
        """
1016
1099
        credentials = None
1017
1100
        for auth_def_name, auth_def in self._get_config().items():
 
1101
            if type(auth_def) is not configobj.Section:
 
1102
                raise ValueError("%s defined outside a section" % auth_def_name)
 
1103
 
1018
1104
            a_scheme, a_host, a_user, a_path = map(
1019
1105
                auth_def.get, ['scheme', 'host', 'user', 'path'])
1020
1106
 
1051
1137
            if a_user is None:
1052
1138
                # Can't find a user
1053
1139
                continue
 
1140
            # Prepare a credentials dictionary with additional keys
 
1141
            # for the credential providers
1054
1142
            credentials = dict(name=auth_def_name,
1055
 
                               user=a_user, password=auth_def['password'],
 
1143
                               user=a_user,
 
1144
                               scheme=a_scheme,
 
1145
                               host=host,
 
1146
                               port=port,
 
1147
                               path=path,
 
1148
                               realm=realm,
 
1149
                               password=auth_def.get('password', None),
1056
1150
                               verify_certificates=a_verify_certificates)
 
1151
            # Decode the password in the credentials (or get one)
1057
1152
            self.decode_password(credentials,
1058
1153
                                 auth_def.get('password_encoding', None))
1059
1154
            if 'auth' in debug.debug_flags:
1060
1155
                trace.mutter("Using authentication section: %r", auth_def_name)
1061
1156
            break
1062
1157
 
 
1158
        if credentials is None:
 
1159
            # No credentials were found in authentication.conf, try the fallback
 
1160
            # credentials stores.
 
1161
            credentials = credential_store_registry.get_fallback_credentials(
 
1162
                scheme, host, port, user, path, realm)
 
1163
 
1063
1164
        return credentials
1064
1165
 
1065
 
    def get_user(self, scheme, host, port=None,
1066
 
                 realm=None, path=None, prompt=None):
 
1166
    def set_credentials(self, name, host, user, scheme=None, password=None,
 
1167
                        port=None, path=None, verify_certificates=None,
 
1168
                        realm=None):
 
1169
        """Set authentication credentials for a host.
 
1170
 
 
1171
        Any existing credentials with matching scheme, host, port and path
 
1172
        will be deleted, regardless of name.
 
1173
 
 
1174
        :param name: An arbitrary name to describe this set of credentials.
 
1175
        :param host: Name of the host that accepts these credentials.
 
1176
        :param user: The username portion of these credentials.
 
1177
        :param scheme: The URL scheme (e.g. ssh, http) the credentials apply
 
1178
            to.
 
1179
        :param password: Password portion of these credentials.
 
1180
        :param port: The IP port on the host that these credentials apply to.
 
1181
        :param path: A filesystem path on the host that these credentials
 
1182
            apply to.
 
1183
        :param verify_certificates: On https, verify server certificates if
 
1184
            True.
 
1185
        :param realm: The http authentication realm (optional).
 
1186
        """
 
1187
        values = {'host': host, 'user': user}
 
1188
        if password is not None:
 
1189
            values['password'] = password
 
1190
        if scheme is not None:
 
1191
            values['scheme'] = scheme
 
1192
        if port is not None:
 
1193
            values['port'] = '%d' % port
 
1194
        if path is not None:
 
1195
            values['path'] = path
 
1196
        if verify_certificates is not None:
 
1197
            values['verify_certificates'] = str(verify_certificates)
 
1198
        if realm is not None:
 
1199
            values['realm'] = realm
 
1200
        config = self._get_config()
 
1201
        for_deletion = []
 
1202
        for section, existing_values in config.items():
 
1203
            for key in ('scheme', 'host', 'port', 'path', 'realm'):
 
1204
                if existing_values.get(key) != values.get(key):
 
1205
                    break
 
1206
            else:
 
1207
                del config[section]
 
1208
        config.update({name: values})
 
1209
        self._save()
 
1210
 
 
1211
    def get_user(self, scheme, host, port=None, realm=None, path=None,
 
1212
                 prompt=None, ask=False, default=None):
1067
1213
        """Get a user from authentication file.
1068
1214
 
1069
1215
        :param scheme: protocol
1076
1222
 
1077
1223
        :param path: the absolute path on the server (optional)
1078
1224
 
 
1225
        :param ask: Ask the user if there is no explicitly configured username 
 
1226
                    (optional)
 
1227
 
 
1228
        :param default: The username returned if none is defined (optional).
 
1229
 
1079
1230
        :return: The found user.
1080
1231
        """
1081
1232
        credentials = self.get_credentials(scheme, host, port, user=None,
1082
 
                                           path=path)
 
1233
                                           path=path, realm=realm)
1083
1234
        if credentials is not None:
1084
1235
            user = credentials['user']
1085
1236
        else:
1086
1237
            user = None
 
1238
        if user is None:
 
1239
            if ask:
 
1240
                if prompt is None:
 
1241
                    # Create a default prompt suitable for most cases
 
1242
                    prompt = scheme.upper() + ' %(host)s username'
 
1243
                # Special handling for optional fields in the prompt
 
1244
                if port is not None:
 
1245
                    prompt_host = '%s:%d' % (host, port)
 
1246
                else:
 
1247
                    prompt_host = host
 
1248
                user = ui.ui_factory.get_username(prompt, host=prompt_host)
 
1249
            else:
 
1250
                user = default
1087
1251
        return user
1088
1252
 
1089
1253
    def get_password(self, scheme, host, user, port=None,
1104
1268
 
1105
1269
        :return: The found password or the one entered by the user.
1106
1270
        """
1107
 
        credentials = self.get_credentials(scheme, host, port, user, path)
 
1271
        credentials = self.get_credentials(scheme, host, port, user, path,
 
1272
                                           realm)
1108
1273
        if credentials is not None:
1109
1274
            password = credentials['password']
 
1275
            if password is not None and scheme is 'ssh':
 
1276
                trace.warning('password ignored in section [%s],'
 
1277
                              ' use an ssh agent instead'
 
1278
                              % credentials['name'])
 
1279
                password = None
1110
1280
        else:
1111
1281
            password = None
1112
1282
        # Prompt user only if we could't find a password
1113
1283
        if password is None:
1114
1284
            if prompt is None:
1115
 
                # Create a default prompt suitable for most of the cases
 
1285
                # Create a default prompt suitable for most cases
1116
1286
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1117
1287
            # Special handling for optional fields in the prompt
1118
1288
            if port is not None:
1124
1294
        return password
1125
1295
 
1126
1296
    def decode_password(self, credentials, encoding):
1127
 
        return credentials
 
1297
        try:
 
1298
            cs = credential_store_registry.get_credential_store(encoding)
 
1299
        except KeyError:
 
1300
            raise ValueError('%r is not a known password_encoding' % encoding)
 
1301
        credentials['password'] = cs.decode_password(credentials)
 
1302
        return credentials
 
1303
 
 
1304
 
 
1305
class CredentialStoreRegistry(registry.Registry):
 
1306
    """A class that registers credential stores.
 
1307
 
 
1308
    A credential store provides access to credentials via the password_encoding
 
1309
    field in authentication.conf sections.
 
1310
 
 
1311
    Except for stores provided by bzr itself, most stores are expected to be
 
1312
    provided by plugins that will therefore use
 
1313
    register_lazy(password_encoding, module_name, member_name, help=help,
 
1314
    fallback=fallback) to install themselves.
 
1315
 
 
1316
    A fallback credential store is one that is queried if no credentials can be
 
1317
    found via authentication.conf.
 
1318
    """
 
1319
 
 
1320
    def get_credential_store(self, encoding=None):
 
1321
        cs = self.get(encoding)
 
1322
        if callable(cs):
 
1323
            cs = cs()
 
1324
        return cs
 
1325
 
 
1326
    def is_fallback(self, name):
 
1327
        """Check if the named credentials store should be used as fallback."""
 
1328
        return self.get_info(name)
 
1329
 
 
1330
    def get_fallback_credentials(self, scheme, host, port=None, user=None,
 
1331
                                 path=None, realm=None):
 
1332
        """Request credentials from all fallback credentials stores.
 
1333
 
 
1334
        The first credentials store that can provide credentials wins.
 
1335
        """
 
1336
        credentials = None
 
1337
        for name in self.keys():
 
1338
            if not self.is_fallback(name):
 
1339
                continue
 
1340
            cs = self.get_credential_store(name)
 
1341
            credentials = cs.get_credentials(scheme, host, port, user,
 
1342
                                             path, realm)
 
1343
            if credentials is not None:
 
1344
                # We found some credentials
 
1345
                break
 
1346
        return credentials
 
1347
 
 
1348
    def register(self, key, obj, help=None, override_existing=False,
 
1349
                 fallback=False):
 
1350
        """Register a new object to a name.
 
1351
 
 
1352
        :param key: This is the key to use to request the object later.
 
1353
        :param obj: The object to register.
 
1354
        :param help: Help text for this entry. This may be a string or
 
1355
                a callable. If it is a callable, it should take two
 
1356
                parameters (registry, key): this registry and the key that
 
1357
                the help was registered under.
 
1358
        :param override_existing: Raise KeyErorr if False and something has
 
1359
                already been registered for that key. If True, ignore if there
 
1360
                is an existing key (always register the new value).
 
1361
        :param fallback: Whether this credential store should be 
 
1362
                used as fallback.
 
1363
        """
 
1364
        return super(CredentialStoreRegistry,
 
1365
                     self).register(key, obj, help, info=fallback,
 
1366
                                    override_existing=override_existing)
 
1367
 
 
1368
    def register_lazy(self, key, module_name, member_name,
 
1369
                      help=None, override_existing=False,
 
1370
                      fallback=False):
 
1371
        """Register a new credential store to be loaded on request.
 
1372
 
 
1373
        :param module_name: The python path to the module. Such as 'os.path'.
 
1374
        :param member_name: The member of the module to return.  If empty or
 
1375
                None, get() will return the module itself.
 
1376
        :param help: Help text for this entry. This may be a string or
 
1377
                a callable.
 
1378
        :param override_existing: If True, replace the existing object
 
1379
                with the new one. If False, if there is already something
 
1380
                registered with the same key, raise a KeyError
 
1381
        :param fallback: Whether this credential store should be 
 
1382
                used as fallback.
 
1383
        """
 
1384
        return super(CredentialStoreRegistry, self).register_lazy(
 
1385
            key, module_name, member_name, help,
 
1386
            info=fallback, override_existing=override_existing)
 
1387
 
 
1388
 
 
1389
credential_store_registry = CredentialStoreRegistry()
 
1390
 
 
1391
 
 
1392
class CredentialStore(object):
 
1393
    """An abstract class to implement storage for credentials"""
 
1394
 
 
1395
    def decode_password(self, credentials):
 
1396
        """Returns a clear text password for the provided credentials."""
 
1397
        raise NotImplementedError(self.decode_password)
 
1398
 
 
1399
    def get_credentials(self, scheme, host, port=None, user=None, path=None,
 
1400
                        realm=None):
 
1401
        """Return the matching credentials from this credential store.
 
1402
 
 
1403
        This method is only called on fallback credential stores.
 
1404
        """
 
1405
        raise NotImplementedError(self.get_credentials)
 
1406
 
 
1407
 
 
1408
 
 
1409
class PlainTextCredentialStore(CredentialStore):
 
1410
    """Plain text credential store for the authentication.conf file."""
 
1411
 
 
1412
    def decode_password(self, credentials):
 
1413
        """See CredentialStore.decode_password."""
 
1414
        return credentials['password']
 
1415
 
 
1416
 
 
1417
credential_store_registry.register('plain', PlainTextCredentialStore,
 
1418
                                   help=PlainTextCredentialStore.__doc__)
 
1419
credential_store_registry.default_key = 'plain'
 
1420
 
 
1421
 
 
1422
class BzrDirConfig(object):
 
1423
 
 
1424
    def __init__(self, bzrdir):
 
1425
        self._bzrdir = bzrdir
 
1426
        self._config = bzrdir._get_config()
 
1427
 
 
1428
    def set_default_stack_on(self, value):
 
1429
        """Set the default stacking location.
 
1430
 
 
1431
        It may be set to a location, or None.
 
1432
 
 
1433
        This policy affects all branches contained by this bzrdir, except for
 
1434
        those under repositories.
 
1435
        """
 
1436
        if self._config is None:
 
1437
            raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
 
1438
        if value is None:
 
1439
            self._config.set_option('', 'default_stack_on')
 
1440
        else:
 
1441
            self._config.set_option(value, 'default_stack_on')
 
1442
 
 
1443
    def get_default_stack_on(self):
 
1444
        """Return the default stacking location.
 
1445
 
 
1446
        This will either be a location, or None.
 
1447
 
 
1448
        This policy affects all branches contained by this bzrdir, except for
 
1449
        those under repositories.
 
1450
        """
 
1451
        if self._config is None:
 
1452
            return None
 
1453
        value = self._config.get_option('default_stack_on')
 
1454
        if value == '':
 
1455
            value = None
 
1456
        return value
 
1457
 
 
1458
 
 
1459
class TransportConfig(object):
 
1460
    """A Config that reads/writes a config file on a Transport.
 
1461
 
 
1462
    It is a low-level object that considers config data to be name/value pairs
 
1463
    that may be associated with a section.  Assigning meaning to the these
 
1464
    values is done at higher levels like TreeConfig.
 
1465
    """
 
1466
 
 
1467
    def __init__(self, transport, filename):
 
1468
        self._transport = transport
 
1469
        self._filename = filename
 
1470
 
 
1471
    def get_option(self, name, section=None, default=None):
 
1472
        """Return the value associated with a named option.
 
1473
 
 
1474
        :param name: The name of the value
 
1475
        :param section: The section the option is in (if any)
 
1476
        :param default: The value to return if the value is not set
 
1477
        :return: The value or default value
 
1478
        """
 
1479
        configobj = self._get_configobj()
 
1480
        if section is None:
 
1481
            section_obj = configobj
 
1482
        else:
 
1483
            try:
 
1484
                section_obj = configobj[section]
 
1485
            except KeyError:
 
1486
                return default
 
1487
        return section_obj.get(name, default)
 
1488
 
 
1489
    def set_option(self, value, name, section=None):
 
1490
        """Set the value associated with a named option.
 
1491
 
 
1492
        :param value: The value to set
 
1493
        :param name: The name of the value to set
 
1494
        :param section: The section the option is in (if any)
 
1495
        """
 
1496
        configobj = self._get_configobj()
 
1497
        if section is None:
 
1498
            configobj[name] = value
 
1499
        else:
 
1500
            configobj.setdefault(section, {})[name] = value
 
1501
        self._set_configobj(configobj)
 
1502
 
 
1503
    def _get_config_file(self):
 
1504
        try:
 
1505
            return StringIO(self._transport.get_bytes(self._filename))
 
1506
        except errors.NoSuchFile:
 
1507
            return StringIO()
 
1508
 
 
1509
    def _get_configobj(self):
 
1510
        return ConfigObj(self._get_config_file(), encoding='utf-8')
 
1511
 
 
1512
    def _set_configobj(self, configobj):
 
1513
        out_file = StringIO()
 
1514
        configobj.write(out_file)
 
1515
        out_file.seek(0)
 
1516
        self._transport.put_file(self._filename, out_file)