~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Michael Hudson
  • Date: 2007-11-26 13:45:49 UTC
  • mto: (3008.1.10 tree-less-merge-diffs)
  • mto: This revision was merged to the branch mainline in revision 3189.
  • Revision ID: michael.hudson@canonical.com-20071126134549-8l3hqyg2bie5yqdv
extract merger creation into a method

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
3
#            and others
4
4
#
78
78
    errors,
79
79
    mail_client,
80
80
    osutils,
81
 
    registry,
82
81
    symbol_versioning,
83
82
    trace,
84
83
    ui,
85
84
    urlutils,
86
85
    win32utils,
87
86
    )
88
 
from bzrlib.util.configobj import configobj
 
87
import bzrlib.util.configobj.configobj as configobj
89
88
""")
90
89
 
91
90
 
122
121
STORE_BRANCH = 3
123
122
STORE_GLOBAL = 4
124
123
 
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)
 
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]
144
138
 
145
139
 
146
140
class Config(object):
153
147
    def get_mail_client(self):
154
148
        """Get a mail client to use"""
155
149
        selected_client = self.get_user_option('mail_client')
156
 
        _registry = mail_client.mail_client_registry
157
150
        try:
158
 
            mail_client_class = _registry.get(selected_client)
 
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]
159
164
        except KeyError:
160
165
            raise errors.UnknownMailClient(selected_client)
161
166
        return mail_client_class(self)
230
235
        """
231
236
        v = os.environ.get('BZR_EMAIL')
232
237
        if v:
233
 
            return v.decode(osutils.get_user_encoding())
 
238
            return v.decode(bzrlib.user_encoding)
234
239
 
235
240
        v = self._get_user_id()
236
241
        if v:
238
243
 
239
244
        v = os.environ.get('EMAIL')
240
245
        if v:
241
 
            return v.decode(osutils.get_user_encoding())
 
246
            return v.decode(bzrlib.user_encoding)
242
247
 
243
248
        name, email = _auto_user_id()
244
249
        if name:
434
439
 
435
440
    def set_user_option(self, option, value):
436
441
        """Save option and its value in the configuration."""
437
 
        self._set_option(option, value, 'DEFAULT')
438
 
 
439
 
    def get_aliases(self):
440
 
        """Return the aliases section."""
441
 
        if 'ALIASES' in self._get_parser():
442
 
            return self._get_parser()['ALIASES']
443
 
        else:
444
 
            return {}
445
 
 
446
 
    def set_alias(self, alias_name, alias_command):
447
 
        """Save the alias in the configuration."""
448
 
        self._set_option(alias_name, alias_command, 'ALIASES')
449
 
 
450
 
    def unset_alias(self, alias_name):
451
 
        """Unset an existing alias."""
452
 
        aliases = self._get_parser().get('ALIASES')
453
 
        if not aliases or alias_name not in aliases:
454
 
            raise errors.NoSuchAlias(alias_name)
455
 
        del aliases[alias_name]
456
 
        self._write_config_file()
457
 
 
458
 
    def _set_option(self, option, value, section):
459
442
        # FIXME: RBC 20051029 This should refresh the parser and also take a
460
443
        # file lock on bazaar.conf.
461
444
        conf_dir = os.path.dirname(self._get_filename())
462
445
        ensure_config_dir_exists(conf_dir)
463
 
        self._get_parser().setdefault(section, {})[option] = value
464
 
        self._write_config_file()
465
 
 
466
 
    def _write_config_file(self):
 
446
        if 'DEFAULT' not in self._get_parser():
 
447
            self._get_parser()['DEFAULT'] = {}
 
448
        self._get_parser()['DEFAULT'][option] = value
467
449
        f = open(self._get_filename(), 'wb')
468
450
        self._get_parser().write(f)
469
451
        f.close()
587
569
 
588
570
    def set_user_option(self, option, value, store=STORE_LOCATION):
589
571
        """Save option and its value in the configuration."""
590
 
        if store not in [STORE_LOCATION,
 
572
        assert store in [STORE_LOCATION,
591
573
                         STORE_LOCATION_NORECURSE,
592
 
                         STORE_LOCATION_APPENDPATH]:
593
 
            raise ValueError('bad storage policy %r for %r' %
594
 
                (store, option))
 
574
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
595
575
        # FIXME: RBC 20051029 This should refresh the parser and also take a
596
576
        # file lock on locations.conf.
597
577
        conf_dir = os.path.dirname(self._get_filename())
658
638
    def _get_user_id(self):
659
639
        """Return the full user id for the branch.
660
640
    
661
 
        e.g. "John Hacker <jhacker@example.com>"
 
641
        e.g. "John Hacker <jhacker@foo.org>"
662
642
        This is looked up in the email controlfile for the branch.
663
643
        """
664
644
        try:
665
 
            return (self.branch._transport.get_bytes("email")
666
 
                    .decode(osutils.get_user_encoding())
 
645
            return (self.branch.control_files.get_utf8("email") 
 
646
                    .read()
 
647
                    .decode(bzrlib.user_encoding)
667
648
                    .rstrip("\r\n"))
668
649
        except errors.NoSuchFile, e:
669
650
            pass
778
759
        if base is None:
779
760
            base = os.environ.get('HOME', None)
780
761
        if base is None:
781
 
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
782
 
                                  ' or HOME set')
 
762
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
783
763
        return osutils.pathjoin(base, 'bazaar', '2.0')
784
764
    else:
785
765
        # cygwin, linux, and darwin all have a $HOME directory
840
820
    try:
841
821
        import pwd
842
822
        uid = os.getuid()
843
 
        try:
844
 
            w = pwd.getpwuid(uid)
845
 
        except KeyError:
846
 
            raise errors.BzrCommandError('Unable to determine your name.  '
847
 
                'Please use "bzr whoami" to set it.')
 
823
        w = pwd.getpwuid(uid)
848
824
 
849
825
        # we try utf-8 first, because on many variants (like Linux),
850
826
        # /etc/passwd "should" be in utf-8, and because it's unlikely to give
855
831
            encoding = 'utf-8'
856
832
        except UnicodeError:
857
833
            try:
858
 
                encoding = osutils.get_user_encoding()
859
 
                gecos = w.pw_gecos.decode(encoding)
 
834
                gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
835
                encoding = bzrlib.user_encoding
860
836
            except UnicodeError:
861
837
                raise errors.BzrCommandError('Unable to determine your name.  '
862
838
                   'Use "bzr whoami" to set it.')
877
853
    except ImportError:
878
854
        import getpass
879
855
        try:
880
 
            user_encoding = osutils.get_user_encoding()
881
 
            realname = username = getpass.getuser().decode(user_encoding)
 
856
            realname = username = getpass.getuser().decode(bzrlib.user_encoding)
882
857
        except UnicodeDecodeError:
883
858
            raise errors.BzrError("Can't decode username as %s." % \
884
 
                    user_encoding)
 
859
                    bzrlib.user_encoding)
885
860
 
886
861
    return realname, (username + '@' + socket.gethostname())
887
862
 
888
863
 
889
 
def parse_username(username):
890
 
    """Parse e-mail username and return a (name, address) tuple."""
891
 
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
892
 
    if match is None:
893
 
        return (username, '')
894
 
    else:
895
 
        return (match.group(1), match.group(2))
896
 
 
897
 
 
898
864
def extract_email_address(e):
899
865
    """Return just the address part of an email string.
900
 
 
 
866
    
901
867
    That is just the user@domain part, nothing else. 
902
868
    This part is required to contain only ascii characters.
903
869
    If it can't be extracted, raises an error.
904
 
 
 
870
    
905
871
    >>> extract_email_address('Jane Tester <jane@test.com>')
906
872
    "jane@test.com"
907
873
    """
908
 
    name, email = parse_username(e)
909
 
    if not email:
 
874
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
875
    if not m:
910
876
        raise errors.NoEmailInUsername(e)
911
 
    return email
 
877
    return m.group(0)
912
878
 
913
879
 
914
880
class TreeConfig(IniBasedConfig):
915
881
    """Branch configuration data associated with its contents, not location"""
916
882
 
917
 
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
918
 
 
919
883
    def __init__(self, branch):
920
 
        # XXX: Really this should be asking the branch for its configuration
921
 
        # data, rather than relying on a Transport, so that it can work 
922
 
        # more cleanly with a RemoteBranch that has no transport.
923
 
        self._config = TransportConfig(branch._transport, 'branch.conf')
924
884
        self.branch = branch
925
885
 
926
886
    def _get_parser(self, file=None):
927
887
        if file is not None:
928
888
            return IniBasedConfig._get_parser(file)
929
 
        return self._config._get_configobj()
 
889
        return self._get_config()
 
890
 
 
891
    def _get_config(self):
 
892
        try:
 
893
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
 
894
                            encoding='utf-8')
 
895
        except errors.NoSuchFile:
 
896
            obj = ConfigObj(encoding='utf=8')
 
897
        return obj
930
898
 
931
899
    def get_option(self, name, section=None, default=None):
932
900
        self.branch.lock_read()
933
901
        try:
934
 
            return self._config.get_option(name, section, default)
 
902
            obj = self._get_config()
 
903
            try:
 
904
                if section is not None:
 
905
                    obj = obj[section]
 
906
                result = obj[name]
 
907
            except KeyError:
 
908
                result = default
935
909
        finally:
936
910
            self.branch.unlock()
937
911
        return result
940
914
        """Set a per-branch configuration option"""
941
915
        self.branch.lock_write()
942
916
        try:
943
 
            self._config.set_option(value, name, section)
 
917
            cfg_obj = self._get_config()
 
918
            if section is None:
 
919
                obj = cfg_obj
 
920
            else:
 
921
                try:
 
922
                    obj = cfg_obj[section]
 
923
                except KeyError:
 
924
                    cfg_obj[section] = {}
 
925
                    obj = cfg_obj[section]
 
926
            obj[name] = value
 
927
            out_file = StringIO()
 
928
            cfg_obj.write(out_file)
 
929
            out_file.seek(0)
 
930
            self.branch.control_files.put('branch.conf', out_file)
944
931
        finally:
945
932
            self.branch.unlock()
946
933
 
1018
1005
        """
1019
1006
        credentials = None
1020
1007
        for auth_def_name, auth_def in self._get_config().items():
1021
 
            if type(auth_def) is not configobj.Section:
1022
 
                raise ValueError("%s defined outside a section" % auth_def_name)
1023
 
 
1024
1008
            a_scheme, a_host, a_user, a_path = map(
1025
1009
                auth_def.get, ['scheme', 'host', 'user', 'path'])
1026
1010
 
1058
1042
                # Can't find a user
1059
1043
                continue
1060
1044
            credentials = dict(name=auth_def_name,
1061
 
                               user=a_user,
1062
 
                               password=auth_def.get('password', None),
 
1045
                               user=a_user, password=auth_def['password'],
1063
1046
                               verify_certificates=a_verify_certificates)
1064
1047
            self.decode_password(credentials,
1065
1048
                                 auth_def.get('password_encoding', None))
1069
1052
 
1070
1053
        return credentials
1071
1054
 
1072
 
    def set_credentials(self, name, host, user, scheme=None, password=None,
1073
 
                        port=None, path=None, verify_certificates=None):
1074
 
        """Set authentication credentials for a host.
1075
 
 
1076
 
        Any existing credentials with matching scheme, host, port and path
1077
 
        will be deleted, regardless of name.
1078
 
 
1079
 
        :param name: An arbitrary name to describe this set of credentials.
1080
 
        :param host: Name of the host that accepts these credentials.
1081
 
        :param user: The username portion of these credentials.
1082
 
        :param scheme: The URL scheme (e.g. ssh, http) the credentials apply
1083
 
            to.
1084
 
        :param password: Password portion of these credentials.
1085
 
        :param port: The IP port on the host that these credentials apply to.
1086
 
        :param path: A filesystem path on the host that these credentials
1087
 
            apply to.
1088
 
        :param verify_certificates: On https, verify server certificates if
1089
 
            True.
1090
 
        """
1091
 
        values = {'host': host, 'user': user}
1092
 
        if password is not None:
1093
 
            values['password'] = password
1094
 
        if scheme is not None:
1095
 
            values['scheme'] = scheme
1096
 
        if port is not None:
1097
 
            values['port'] = '%d' % port
1098
 
        if path is not None:
1099
 
            values['path'] = path
1100
 
        if verify_certificates is not None:
1101
 
            values['verify_certificates'] = str(verify_certificates)
1102
 
        config = self._get_config()
1103
 
        for_deletion = []
1104
 
        for section, existing_values in config.items():
1105
 
            for key in ('scheme', 'host', 'port', 'path'):
1106
 
                if existing_values.get(key) != values.get(key):
1107
 
                    break
1108
 
            else:
1109
 
                del config[section]
1110
 
        config.update({name: values})
1111
 
        self._save()
1112
 
 
1113
1055
    def get_user(self, scheme, host, port=None,
1114
1056
                 realm=None, path=None, prompt=None):
1115
1057
        """Get a user from authentication file.
1155
1097
        credentials = self.get_credentials(scheme, host, port, user, path)
1156
1098
        if credentials is not None:
1157
1099
            password = credentials['password']
1158
 
            if password is not None and scheme is 'ssh':
1159
 
                trace.warning('password ignored in section [%s],'
1160
 
                              ' use an ssh agent instead'
1161
 
                              % credentials['name'])
1162
 
                password = None
1163
1100
        else:
1164
1101
            password = None
1165
1102
        # Prompt user only if we could't find a password
1166
1103
        if password is None:
1167
1104
            if prompt is None:
1168
 
                # Create a default prompt suitable for most cases
 
1105
                # Create a default prompt suitable for most of the cases
1169
1106
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1170
1107
            # Special handling for optional fields in the prompt
1171
1108
            if port is not None:
1177
1114
        return password
1178
1115
 
1179
1116
    def decode_password(self, credentials, encoding):
1180
 
        try:
1181
 
            cs = credential_store_registry.get_credential_store(encoding)
1182
 
        except KeyError:
1183
 
            raise ValueError('%r is not a known password_encoding' % encoding)
1184
 
        credentials['password'] = cs.decode_password(credentials)
1185
1117
        return credentials
1186
 
 
1187
 
 
1188
 
class CredentialStoreRegistry(registry.Registry):
1189
 
    """A class that registers credential stores.
1190
 
 
1191
 
    A credential store provides access to credentials via the password_encoding
1192
 
    field in authentication.conf sections.
1193
 
 
1194
 
    Except for stores provided by bzr itself,most stores are expected to be
1195
 
    provided by plugins that will therefore use
1196
 
    register_lazy(password_encoding, module_name, member_name, help=help,
1197
 
    info=info) to install themselves.
1198
 
    """
1199
 
 
1200
 
    def get_credential_store(self, encoding=None):
1201
 
        cs = self.get(encoding)
1202
 
        if callable(cs):
1203
 
            cs = cs()
1204
 
        return cs
1205
 
 
1206
 
 
1207
 
credential_store_registry = CredentialStoreRegistry()
1208
 
 
1209
 
 
1210
 
class CredentialStore(object):
1211
 
    """An abstract class to implement storage for credentials"""
1212
 
 
1213
 
    def decode_password(self, credentials):
1214
 
        """Returns a password for the provided credentials in clear text."""
1215
 
        raise NotImplementedError(self.decode_password)
1216
 
 
1217
 
 
1218
 
class PlainTextCredentialStore(CredentialStore):
1219
 
    """Plain text credential store for the authentication.conf file."""
1220
 
 
1221
 
    def decode_password(self, credentials):
1222
 
        """See CredentialStore.decode_password."""
1223
 
        return credentials['password']
1224
 
 
1225
 
 
1226
 
credential_store_registry.register('plain', PlainTextCredentialStore,
1227
 
                                   help=PlainTextCredentialStore.__doc__)
1228
 
credential_store_registry.default_key = 'plain'
1229
 
 
1230
 
 
1231
 
class BzrDirConfig(object):
1232
 
 
1233
 
    def __init__(self, transport):
1234
 
        self._config = TransportConfig(transport, 'control.conf')
1235
 
 
1236
 
    def set_default_stack_on(self, value):
1237
 
        """Set the default stacking location.
1238
 
 
1239
 
        It may be set to a location, or None.
1240
 
 
1241
 
        This policy affects all branches contained by this bzrdir, except for
1242
 
        those under repositories.
1243
 
        """
1244
 
        if value is None:
1245
 
            self._config.set_option('', 'default_stack_on')
1246
 
        else:
1247
 
            self._config.set_option(value, 'default_stack_on')
1248
 
 
1249
 
    def get_default_stack_on(self):
1250
 
        """Return the default stacking location.
1251
 
 
1252
 
        This will either be a location, or None.
1253
 
 
1254
 
        This policy affects all branches contained by this bzrdir, except for
1255
 
        those under repositories.
1256
 
        """
1257
 
        value = self._config.get_option('default_stack_on')
1258
 
        if value == '':
1259
 
            value = None
1260
 
        return value
1261
 
 
1262
 
 
1263
 
class TransportConfig(object):
1264
 
    """A Config that reads/writes a config file on a Transport.
1265
 
 
1266
 
    It is a low-level object that considers config data to be name/value pairs
1267
 
    that may be associated with a section.  Assigning meaning to the these
1268
 
    values is done at higher levels like TreeConfig.
1269
 
    """
1270
 
 
1271
 
    def __init__(self, transport, filename):
1272
 
        self._transport = transport
1273
 
        self._filename = filename
1274
 
 
1275
 
    def get_option(self, name, section=None, default=None):
1276
 
        """Return the value associated with a named option.
1277
 
 
1278
 
        :param name: The name of the value
1279
 
        :param section: The section the option is in (if any)
1280
 
        :param default: The value to return if the value is not set
1281
 
        :return: The value or default value
1282
 
        """
1283
 
        configobj = self._get_configobj()
1284
 
        if section is None:
1285
 
            section_obj = configobj
1286
 
        else:
1287
 
            try:
1288
 
                section_obj = configobj[section]
1289
 
            except KeyError:
1290
 
                return default
1291
 
        return section_obj.get(name, default)
1292
 
 
1293
 
    def set_option(self, value, name, section=None):
1294
 
        """Set the value associated with a named option.
1295
 
 
1296
 
        :param value: The value to set
1297
 
        :param name: The name of the value to set
1298
 
        :param section: The section the option is in (if any)
1299
 
        """
1300
 
        configobj = self._get_configobj()
1301
 
        if section is None:
1302
 
            configobj[name] = value
1303
 
        else:
1304
 
            configobj.setdefault(section, {})[name] = value
1305
 
        self._set_configobj(configobj)
1306
 
 
1307
 
    def _get_configobj(self):
1308
 
        try:
1309
 
            return ConfigObj(self._transport.get(self._filename),
1310
 
                             encoding='utf-8')
1311
 
        except errors.NoSuchFile:
1312
 
            return ConfigObj(encoding='utf-8')
1313
 
 
1314
 
    def _set_configobj(self, configobj):
1315
 
        out_file = StringIO()
1316
 
        configobj.write(out_file)
1317
 
        out_file.seek(0)
1318
 
        self._transport.put_file(self._filename, out_file)