~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Martin Pool
  • Date: 2007-10-12 08:00:07 UTC
  • mto: This revision was merged to the branch mainline in revision 2913.
  • Revision ID: mbp@sourcefrog.net-20071012080007-vf80woayyom8s8e1
Rename update_to_one_parent_via_delta to more wieldy update_basis_by_delta

Show diffs side-by-side

added added

removed removed

Lines of Context:
70
70
import errno
71
71
from fnmatch import fnmatch
72
72
import re
73
 
from cStringIO import StringIO
 
73
from StringIO import StringIO
74
74
 
75
75
import bzrlib
76
76
from bzrlib import (
77
 
    debug,
78
77
    errors,
79
78
    mail_client,
80
79
    osutils,
81
80
    symbol_versioning,
82
81
    trace,
83
 
    ui,
84
82
    urlutils,
85
83
    win32utils,
86
84
    )
87
 
from bzrlib.util.configobj import configobj
 
85
import bzrlib.util.configobj.configobj as configobj
88
86
""")
89
87
 
 
88
from bzrlib.trace import mutter, warning
 
89
 
90
90
 
91
91
CHECK_IF_POSSIBLE=0
92
92
CHECK_ALWAYS=1
147
147
    def get_mail_client(self):
148
148
        """Get a mail client to use"""
149
149
        selected_client = self.get_user_option('mail_client')
150
 
        _registry = mail_client.mail_client_registry
151
150
        try:
152
 
            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]
153
164
        except KeyError:
154
165
            raise errors.UnknownMailClient(selected_client)
155
166
        return mail_client_class(self)
225
236
        v = os.environ.get('BZR_EMAIL')
226
237
        if v:
227
238
            return v.decode(bzrlib.user_encoding)
228
 
 
 
239
        v = os.environ.get('BZREMAIL')
 
240
        if v:
 
241
            warning('BZREMAIL is deprecated in favor of BZR_EMAIL. Please update your configuration.')
 
242
            return v.decode(bzrlib.user_encoding)
 
243
    
229
244
        v = self._get_user_id()
230
245
        if v:
231
246
            return v
232
 
 
 
247
        
233
248
        v = os.environ.get('EMAIL')
234
249
        if v:
235
250
            return v.decode(bzrlib.user_encoding)
260
275
        if policy is None:
261
276
            policy = self._get_signature_checking()
262
277
            if policy is not None:
263
 
                trace.warning("Please use create_signatures,"
264
 
                              " not check_signatures to set signing policy.")
 
278
                warning("Please use create_signatures, not check_signatures "
 
279
                        "to set signing policy.")
265
280
            if policy == CHECK_ALWAYS:
266
281
                return True
267
282
        elif policy == SIGN_ALWAYS:
428
443
 
429
444
    def set_user_option(self, option, value):
430
445
        """Save option and its value in the configuration."""
431
 
        self._set_option(option, value, 'DEFAULT')
432
 
 
433
 
    def get_aliases(self):
434
 
        """Return the aliases section."""
435
 
        if 'ALIASES' in self._get_parser():
436
 
            return self._get_parser()['ALIASES']
437
 
        else:
438
 
            return {}
439
 
 
440
 
    def set_alias(self, alias_name, alias_command):
441
 
        """Save the alias in the configuration."""
442
 
        self._set_option(alias_name, alias_command, 'ALIASES')
443
 
 
444
 
    def unset_alias(self, alias_name):
445
 
        """Unset an existing alias."""
446
 
        aliases = self._get_parser().get('ALIASES')
447
 
        if not aliases or alias_name not in aliases:
448
 
            raise errors.NoSuchAlias(alias_name)
449
 
        del aliases[alias_name]
450
 
        self._write_config_file()
451
 
 
452
 
    def _set_option(self, option, value, section):
453
446
        # FIXME: RBC 20051029 This should refresh the parser and also take a
454
447
        # file lock on bazaar.conf.
455
448
        conf_dir = os.path.dirname(self._get_filename())
456
449
        ensure_config_dir_exists(conf_dir)
457
 
        self._get_parser().setdefault(section, {})[option] = value
458
 
        self._write_config_file()
459
 
 
460
 
    def _write_config_file(self):
 
450
        if 'DEFAULT' not in self._get_parser():
 
451
            self._get_parser()['DEFAULT'] = {}
 
452
        self._get_parser()['DEFAULT'][option] = value
461
453
        f = open(self._get_filename(), 'wb')
462
454
        self._get_parser().write(f)
463
455
        f.close()
468
460
 
469
461
    def __init__(self, location):
470
462
        name_generator = locations_config_filename
471
 
        if (not os.path.exists(name_generator()) and
 
463
        if (not os.path.exists(name_generator()) and 
472
464
                os.path.exists(branches_config_filename())):
473
465
            if sys.platform == 'win32':
474
 
                trace.warning('Please rename %s to %s'
475
 
                              % (branches_config_filename(),
476
 
                                 locations_config_filename()))
 
466
                warning('Please rename %s to %s' 
 
467
                         % (branches_config_filename(),
 
468
                            locations_config_filename()))
477
469
            else:
478
 
                trace.warning('Please rename ~/.bazaar/branches.conf'
479
 
                              ' to ~/.bazaar/locations.conf')
 
470
                warning('Please rename ~/.bazaar/branches.conf'
 
471
                        ' to ~/.bazaar/locations.conf')
480
472
            name_generator = branches_config_filename
481
473
        super(LocationConfig, self).__init__(name_generator)
482
474
        # local file locations are looked up by local path, rather than
581
573
 
582
574
    def set_user_option(self, option, value, store=STORE_LOCATION):
583
575
        """Save option and its value in the configuration."""
584
 
        if store not in [STORE_LOCATION,
 
576
        assert store in [STORE_LOCATION,
585
577
                         STORE_LOCATION_NORECURSE,
586
 
                         STORE_LOCATION_APPENDPATH]:
587
 
            raise ValueError('bad storage policy %r for %r' %
588
 
                (store, option))
 
578
                         STORE_LOCATION_APPENDPATH], 'bad storage policy'
589
579
        # FIXME: RBC 20051029 This should refresh the parser and also take a
590
580
        # file lock on locations.conf.
591
581
        conf_dir = os.path.dirname(self._get_filename())
652
642
    def _get_user_id(self):
653
643
        """Return the full user id for the branch.
654
644
    
655
 
        e.g. "John Hacker <jhacker@example.com>"
 
645
        e.g. "John Hacker <jhacker@foo.org>"
656
646
        This is looked up in the email controlfile for the branch.
657
647
        """
658
648
        try:
659
 
            return (self.branch._transport.get_bytes("email")
 
649
            return (self.branch.control_files.get_utf8("email") 
 
650
                    .read()
660
651
                    .decode(bzrlib.user_encoding)
661
652
                    .rstrip("\r\n"))
662
653
        except errors.NoSuchFile, e:
752
743
        if sys.platform == 'win32':
753
744
            parent_dir = os.path.dirname(path)
754
745
            if not os.path.isdir(parent_dir):
755
 
                trace.mutter('creating config parent directory: %r', parent_dir)
 
746
                mutter('creating config parent directory: %r', parent_dir)
756
747
            os.mkdir(parent_dir)
757
 
        trace.mutter('creating config directory: %r', path)
 
748
        mutter('creating config directory: %r', path)
758
749
        os.mkdir(path)
759
750
 
760
751
 
772
763
        if base is None:
773
764
            base = os.environ.get('HOME', None)
774
765
        if base is None:
775
 
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
776
 
                                  ' or HOME set')
 
766
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
777
767
        return osutils.pathjoin(base, 'bazaar', '2.0')
778
768
    else:
779
769
        # cygwin, linux, and darwin all have a $HOME directory
797
787
    return osutils.pathjoin(config_dir(), 'locations.conf')
798
788
 
799
789
 
800
 
def authentication_config_filename():
801
 
    """Return per-user authentication ini file filename."""
802
 
    return osutils.pathjoin(config_dir(), 'authentication.conf')
803
 
 
804
 
 
805
790
def user_ignore_config_filename():
806
791
    """Return the user default ignore filename"""
807
792
    return osutils.pathjoin(config_dir(), 'ignore')
875
860
    return realname, (username + '@' + socket.gethostname())
876
861
 
877
862
 
878
 
def parse_username(username):
879
 
    """Parse e-mail username and return a (name, address) tuple."""
880
 
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
881
 
    if match is None:
882
 
        return (username, '')
883
 
    else:
884
 
        return (match.group(1), match.group(2))
885
 
 
886
 
 
887
863
def extract_email_address(e):
888
864
    """Return just the address part of an email string.
889
 
 
 
865
    
890
866
    That is just the user@domain part, nothing else. 
891
867
    This part is required to contain only ascii characters.
892
868
    If it can't be extracted, raises an error.
893
 
 
 
869
    
894
870
    >>> extract_email_address('Jane Tester <jane@test.com>')
895
871
    "jane@test.com"
896
872
    """
897
 
    name, email = parse_username(e)
898
 
    if not email:
 
873
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
874
    if not m:
899
875
        raise errors.NoEmailInUsername(e)
900
 
    return email
 
876
    return m.group(0)
901
877
 
902
878
 
903
879
class TreeConfig(IniBasedConfig):
904
880
    """Branch configuration data associated with its contents, not location"""
905
 
 
906
 
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
907
 
 
908
881
    def __init__(self, branch):
909
 
        # XXX: Really this should be asking the branch for its configuration
910
 
        # data, rather than relying on a Transport, so that it can work 
911
 
        # more cleanly with a RemoteBranch that has no transport.
912
 
        self._config = TransportConfig(branch._transport, 'branch.conf')
913
882
        self.branch = branch
914
883
 
915
884
    def _get_parser(self, file=None):
916
885
        if file is not None:
917
886
            return IniBasedConfig._get_parser(file)
918
 
        return self._config._get_configobj()
 
887
        return self._get_config()
 
888
 
 
889
    def _get_config(self):
 
890
        try:
 
891
            obj = ConfigObj(self.branch.control_files.get('branch.conf'),
 
892
                            encoding='utf-8')
 
893
        except errors.NoSuchFile:
 
894
            obj = ConfigObj(encoding='utf=8')
 
895
        return obj
919
896
 
920
897
    def get_option(self, name, section=None, default=None):
921
898
        self.branch.lock_read()
922
899
        try:
923
 
            return self._config.get_option(name, section, default)
 
900
            obj = self._get_config()
 
901
            try:
 
902
                if section is not None:
 
903
                    obj = obj[section]
 
904
                result = obj[name]
 
905
            except KeyError:
 
906
                result = default
924
907
        finally:
925
908
            self.branch.unlock()
926
909
        return result
929
912
        """Set a per-branch configuration option"""
930
913
        self.branch.lock_write()
931
914
        try:
932
 
            self._config.set_option(value, name, section)
 
915
            cfg_obj = self._get_config()
 
916
            if section is None:
 
917
                obj = cfg_obj
 
918
            else:
 
919
                try:
 
920
                    obj = cfg_obj[section]
 
921
                except KeyError:
 
922
                    cfg_obj[section] = {}
 
923
                    obj = cfg_obj[section]
 
924
            obj[name] = value
 
925
            out_file = StringIO()
 
926
            cfg_obj.write(out_file)
 
927
            out_file.seek(0)
 
928
            self.branch.control_files.put('branch.conf', out_file)
933
929
        finally:
934
930
            self.branch.unlock()
935
 
 
936
 
 
937
 
class AuthenticationConfig(object):
938
 
    """The authentication configuration file based on a ini file.
939
 
 
940
 
    Implements the authentication.conf file described in
941
 
    doc/developers/authentication-ring.txt.
942
 
    """
943
 
 
944
 
    def __init__(self, _file=None):
945
 
        self._config = None # The ConfigObj
946
 
        if _file is None:
947
 
            self._filename = authentication_config_filename()
948
 
            self._input = self._filename = authentication_config_filename()
949
 
        else:
950
 
            # Tests can provide a string as _file
951
 
            self._filename = None
952
 
            self._input = _file
953
 
 
954
 
    def _get_config(self):
955
 
        if self._config is not None:
956
 
            return self._config
957
 
        try:
958
 
            # FIXME: Should we validate something here ? Includes: empty
959
 
            # sections are useless, at least one of
960
 
            # user/password/password_encoding should be defined, etc.
961
 
 
962
 
            # Note: the encoding below declares that the file itself is utf-8
963
 
            # encoded, but the values in the ConfigObj are always Unicode.
964
 
            self._config = ConfigObj(self._input, encoding='utf-8')
965
 
        except configobj.ConfigObjError, e:
966
 
            raise errors.ParseConfigError(e.errors, e.config.filename)
967
 
        return self._config
968
 
 
969
 
    def _save(self):
970
 
        """Save the config file, only tests should use it for now."""
971
 
        conf_dir = os.path.dirname(self._filename)
972
 
        ensure_config_dir_exists(conf_dir)
973
 
        self._get_config().write(file(self._filename, 'wb'))
974
 
 
975
 
    def _set_option(self, section_name, option_name, value):
976
 
        """Set an authentication configuration option"""
977
 
        conf = self._get_config()
978
 
        section = conf.get(section_name)
979
 
        if section is None:
980
 
            conf[section] = {}
981
 
            section = conf[section]
982
 
        section[option_name] = value
983
 
        self._save()
984
 
 
985
 
    def get_credentials(self, scheme, host, port=None, user=None, path=None):
986
 
        """Returns the matching credentials from authentication.conf file.
987
 
 
988
 
        :param scheme: protocol
989
 
 
990
 
        :param host: the server address
991
 
 
992
 
        :param port: the associated port (optional)
993
 
 
994
 
        :param user: login (optional)
995
 
 
996
 
        :param path: the absolute path on the server (optional)
997
 
 
998
 
        :return: A dict containing the matching credentials or None.
999
 
           This includes:
1000
 
           - name: the section name of the credentials in the
1001
 
             authentication.conf file,
1002
 
           - user: can't de different from the provided user if any,
1003
 
           - password: the decoded password, could be None if the credential
1004
 
             defines only the user
1005
 
           - verify_certificates: https specific, True if the server
1006
 
             certificate should be verified, False otherwise.
1007
 
        """
1008
 
        credentials = None
1009
 
        for auth_def_name, auth_def in self._get_config().items():
1010
 
            if type(auth_def) is not configobj.Section:
1011
 
                raise ValueError("%s defined outside a section" % auth_def_name)
1012
 
 
1013
 
            a_scheme, a_host, a_user, a_path = map(
1014
 
                auth_def.get, ['scheme', 'host', 'user', 'path'])
1015
 
 
1016
 
            try:
1017
 
                a_port = auth_def.as_int('port')
1018
 
            except KeyError:
1019
 
                a_port = None
1020
 
            except ValueError:
1021
 
                raise ValueError("'port' not numeric in %s" % auth_def_name)
1022
 
            try:
1023
 
                a_verify_certificates = auth_def.as_bool('verify_certificates')
1024
 
            except KeyError:
1025
 
                a_verify_certificates = True
1026
 
            except ValueError:
1027
 
                raise ValueError(
1028
 
                    "'verify_certificates' not boolean in %s" % auth_def_name)
1029
 
 
1030
 
            # Attempt matching
1031
 
            if a_scheme is not None and scheme != a_scheme:
1032
 
                continue
1033
 
            if a_host is not None:
1034
 
                if not (host == a_host
1035
 
                        or (a_host.startswith('.') and host.endswith(a_host))):
1036
 
                    continue
1037
 
            if a_port is not None and port != a_port:
1038
 
                continue
1039
 
            if (a_path is not None and path is not None
1040
 
                and not path.startswith(a_path)):
1041
 
                continue
1042
 
            if (a_user is not None and user is not None
1043
 
                and a_user != user):
1044
 
                # Never contradict the caller about the user to be used
1045
 
                continue
1046
 
            if a_user is None:
1047
 
                # Can't find a user
1048
 
                continue
1049
 
            credentials = dict(name=auth_def_name,
1050
 
                               user=a_user,
1051
 
                               password=auth_def.get('password', None),
1052
 
                               verify_certificates=a_verify_certificates)
1053
 
            self.decode_password(credentials,
1054
 
                                 auth_def.get('password_encoding', None))
1055
 
            if 'auth' in debug.debug_flags:
1056
 
                trace.mutter("Using authentication section: %r", auth_def_name)
1057
 
            break
1058
 
 
1059
 
        return credentials
1060
 
 
1061
 
    def get_user(self, scheme, host, port=None,
1062
 
                 realm=None, path=None, prompt=None):
1063
 
        """Get a user from authentication file.
1064
 
 
1065
 
        :param scheme: protocol
1066
 
 
1067
 
        :param host: the server address
1068
 
 
1069
 
        :param port: the associated port (optional)
1070
 
 
1071
 
        :param realm: the realm sent by the server (optional)
1072
 
 
1073
 
        :param path: the absolute path on the server (optional)
1074
 
 
1075
 
        :return: The found user.
1076
 
        """
1077
 
        credentials = self.get_credentials(scheme, host, port, user=None,
1078
 
                                           path=path)
1079
 
        if credentials is not None:
1080
 
            user = credentials['user']
1081
 
        else:
1082
 
            user = None
1083
 
        return user
1084
 
 
1085
 
    def get_password(self, scheme, host, user, port=None,
1086
 
                     realm=None, path=None, prompt=None):
1087
 
        """Get a password from authentication file or prompt the user for one.
1088
 
 
1089
 
        :param scheme: protocol
1090
 
 
1091
 
        :param host: the server address
1092
 
 
1093
 
        :param port: the associated port (optional)
1094
 
 
1095
 
        :param user: login
1096
 
 
1097
 
        :param realm: the realm sent by the server (optional)
1098
 
 
1099
 
        :param path: the absolute path on the server (optional)
1100
 
 
1101
 
        :return: The found password or the one entered by the user.
1102
 
        """
1103
 
        credentials = self.get_credentials(scheme, host, port, user, path)
1104
 
        if credentials is not None:
1105
 
            password = credentials['password']
1106
 
            if password is not None and scheme is 'ssh':
1107
 
                trace.warning('password ignored in section [%s],'
1108
 
                              ' use an ssh agent instead'
1109
 
                              % credentials['name'])
1110
 
                password = None
1111
 
        else:
1112
 
            password = None
1113
 
        # Prompt user only if we could't find a password
1114
 
        if password is None:
1115
 
            if prompt is None:
1116
 
                # Create a default prompt suitable for most cases
1117
 
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1118
 
            # Special handling for optional fields in the prompt
1119
 
            if port is not None:
1120
 
                prompt_host = '%s:%d' % (host, port)
1121
 
            else:
1122
 
                prompt_host = host
1123
 
            password = ui.ui_factory.get_password(prompt,
1124
 
                                                  host=prompt_host, user=user)
1125
 
        return password
1126
 
 
1127
 
    def decode_password(self, credentials, encoding):
1128
 
        return credentials
1129
 
 
1130
 
 
1131
 
class BzrDirConfig(object):
1132
 
 
1133
 
    def __init__(self, transport):
1134
 
        self._config = TransportConfig(transport, 'control.conf')
1135
 
 
1136
 
    def set_default_stack_on(self, value):
1137
 
        """Set the default stacking location.
1138
 
 
1139
 
        It may be set to a location, or None.
1140
 
 
1141
 
        This policy affects all branches contained by this bzrdir, except for
1142
 
        those under repositories.
1143
 
        """
1144
 
        if value is None:
1145
 
            self._config.set_option('', 'default_stack_on')
1146
 
        else:
1147
 
            self._config.set_option(value, 'default_stack_on')
1148
 
 
1149
 
    def get_default_stack_on(self):
1150
 
        """Return the default stacking location.
1151
 
 
1152
 
        This will either be a location, or None.
1153
 
 
1154
 
        This policy affects all branches contained by this bzrdir, except for
1155
 
        those under repositories.
1156
 
        """
1157
 
        value = self._config.get_option('default_stack_on')
1158
 
        if value == '':
1159
 
            value = None
1160
 
        return value
1161
 
 
1162
 
 
1163
 
class TransportConfig(object):
1164
 
    """A Config that reads/writes a config file on a Transport.
1165
 
 
1166
 
    It is a low-level object that considers config data to be name/value pairs
1167
 
    that may be associated with a section.  Assigning meaning to the these
1168
 
    values is done at higher levels like TreeConfig.
1169
 
    """
1170
 
 
1171
 
    def __init__(self, transport, filename):
1172
 
        self._transport = transport
1173
 
        self._filename = filename
1174
 
 
1175
 
    def get_option(self, name, section=None, default=None):
1176
 
        """Return the value associated with a named option.
1177
 
 
1178
 
        :param name: The name of the value
1179
 
        :param section: The section the option is in (if any)
1180
 
        :param default: The value to return if the value is not set
1181
 
        :return: The value or default value
1182
 
        """
1183
 
        configobj = self._get_configobj()
1184
 
        if section is None:
1185
 
            section_obj = configobj
1186
 
        else:
1187
 
            try:
1188
 
                section_obj = configobj[section]
1189
 
            except KeyError:
1190
 
                return default
1191
 
        return section_obj.get(name, default)
1192
 
 
1193
 
    def set_option(self, value, name, section=None):
1194
 
        """Set the value associated with a named option.
1195
 
 
1196
 
        :param value: The value to set
1197
 
        :param name: The name of the value to set
1198
 
        :param section: The section the option is in (if any)
1199
 
        """
1200
 
        configobj = self._get_configobj()
1201
 
        if section is None:
1202
 
            configobj[name] = value
1203
 
        else:
1204
 
            configobj.setdefault(section, {})[name] = value
1205
 
        self._set_configobj(configobj)
1206
 
 
1207
 
    def _get_configobj(self):
1208
 
        try:
1209
 
            return ConfigObj(self._transport.get(self._filename),
1210
 
                             encoding='utf-8')
1211
 
        except errors.NoSuchFile:
1212
 
            return ConfigObj(encoding='utf-8')
1213
 
 
1214
 
    def _set_configobj(self, configobj):
1215
 
        out_file = StringIO()
1216
 
        configobj.write(out_file)
1217
 
        out_file.seek(0)
1218
 
        self._transport.put_file(self._filename, out_file)