812
887
return realname, (username + '@' + socket.gethostname())
890
def parse_username(username):
891
"""Parse e-mail username and return a (name, address) tuple."""
892
match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
894
return (username, '')
896
return (match.group(1), match.group(2))
815
899
def extract_email_address(e):
816
900
"""Return just the address part of an email string.
818
902
That is just the user@domain part, nothing else.
819
903
This part is required to contain only ascii characters.
820
904
If it can't be extracted, raises an error.
822
906
>>> extract_email_address('Jane Tester <jane@test.com>')
825
m = re.search(r'[\w+.-]+@[\w+.-]+', e)
909
name, email = parse_username(e)
827
911
raise errors.NoEmailInUsername(e)
831
915
class TreeConfig(IniBasedConfig):
832
916
"""Branch configuration data associated with its contents, not location"""
918
# XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
833
920
def __init__(self, branch):
921
# XXX: Really this should be asking the branch for its configuration
922
# data, rather than relying on a Transport, so that it can work
923
# more cleanly with a RemoteBranch that has no transport.
924
self._config = TransportConfig(branch._transport, 'branch.conf')
834
925
self.branch = branch
836
927
def _get_parser(self, file=None):
837
928
if file is not None:
838
929
return IniBasedConfig._get_parser(file)
839
return self._get_config()
841
def _get_config(self):
843
obj = ConfigObj(self.branch.control_files.get('branch.conf'),
845
except errors.NoSuchFile:
846
obj = ConfigObj(encoding='utf=8')
930
return self._config._get_configobj()
849
932
def get_option(self, name, section=None, default=None):
850
933
self.branch.lock_read()
852
obj = self._get_config()
854
if section is not None:
935
return self._config.get_option(name, section, default)
860
937
self.branch.unlock()
864
941
"""Set a per-branch configuration option"""
865
942
self.branch.lock_write()
867
cfg_obj = self._get_config()
872
obj = cfg_obj[section]
874
cfg_obj[section] = {}
875
obj = cfg_obj[section]
877
out_file = StringIO()
878
cfg_obj.write(out_file)
880
self.branch.control_files.put('branch.conf', out_file)
944
self._config.set_option(value, name, section)
882
946
self.branch.unlock()
949
class AuthenticationConfig(object):
950
"""The authentication configuration file based on a ini file.
952
Implements the authentication.conf file described in
953
doc/developers/authentication-ring.txt.
956
def __init__(self, _file=None):
957
self._config = None # The ConfigObj
959
self._filename = authentication_config_filename()
960
self._input = self._filename = authentication_config_filename()
962
# Tests can provide a string as _file
963
self._filename = None
966
def _get_config(self):
967
if self._config is not None:
970
# FIXME: Should we validate something here ? Includes: empty
971
# sections are useless, at least one of
972
# user/password/password_encoding should be defined, etc.
974
# Note: the encoding below declares that the file itself is utf-8
975
# encoded, but the values in the ConfigObj are always Unicode.
976
self._config = ConfigObj(self._input, encoding='utf-8')
977
except configobj.ConfigObjError, e:
978
raise errors.ParseConfigError(e.errors, e.config.filename)
982
"""Save the config file, only tests should use it for now."""
983
conf_dir = os.path.dirname(self._filename)
984
ensure_config_dir_exists(conf_dir)
985
self._get_config().write(file(self._filename, 'wb'))
987
def _set_option(self, section_name, option_name, value):
988
"""Set an authentication configuration option"""
989
conf = self._get_config()
990
section = conf.get(section_name)
993
section = conf[section]
994
section[option_name] = value
997
def get_credentials(self, scheme, host, port=None, user=None, path=None):
998
"""Returns the matching credentials from authentication.conf file.
1000
:param scheme: protocol
1002
:param host: the server address
1004
:param port: the associated port (optional)
1006
:param user: login (optional)
1008
:param path: the absolute path on the server (optional)
1010
:return: A dict containing the matching credentials or None.
1012
- name: the section name of the credentials in the
1013
authentication.conf file,
1014
- user: can't de different from the provided user if any,
1015
- password: the decoded password, could be None if the credential
1016
defines only the user
1017
- verify_certificates: https specific, True if the server
1018
certificate should be verified, False otherwise.
1021
for auth_def_name, auth_def in self._get_config().items():
1022
if type(auth_def) is not configobj.Section:
1023
raise ValueError("%s defined outside a section" % auth_def_name)
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,
1063
password=auth_def.get('password', None),
1064
verify_certificates=a_verify_certificates)
1065
self.decode_password(credentials,
1066
auth_def.get('password_encoding', None))
1067
if 'auth' in debug.debug_flags:
1068
trace.mutter("Using authentication section: %r", auth_def_name)
1073
def get_user(self, scheme, host, port=None,
1074
realm=None, path=None, prompt=None):
1075
"""Get a user from authentication file.
1077
:param scheme: protocol
1079
:param host: the server address
1081
:param port: the associated port (optional)
1083
:param realm: the realm sent by the server (optional)
1085
:param path: the absolute path on the server (optional)
1087
:return: The found user.
1089
credentials = self.get_credentials(scheme, host, port, user=None,
1091
if credentials is not None:
1092
user = credentials['user']
1097
def get_password(self, scheme, host, user, port=None,
1098
realm=None, path=None, prompt=None):
1099
"""Get a password from authentication file or prompt the user for one.
1101
:param scheme: protocol
1103
:param host: the server address
1105
:param port: the associated port (optional)
1109
:param realm: the realm sent by the server (optional)
1111
:param path: the absolute path on the server (optional)
1113
:return: The found password or the one entered by the user.
1115
credentials = self.get_credentials(scheme, host, port, user, path)
1116
if credentials is not None:
1117
password = credentials['password']
1118
if password is not None and scheme is 'ssh':
1119
trace.warning('password ignored in section [%s],'
1120
' use an ssh agent instead'
1121
% credentials['name'])
1125
# Prompt user only if we could't find a password
1126
if password is None:
1128
# Create a default prompt suitable for most cases
1129
prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1130
# Special handling for optional fields in the prompt
1131
if port is not None:
1132
prompt_host = '%s:%d' % (host, port)
1135
password = ui.ui_factory.get_password(prompt,
1136
host=prompt_host, user=user)
1139
def decode_password(self, credentials, encoding):
1143
class BzrDirConfig(object):
1145
def __init__(self, transport):
1146
self._config = TransportConfig(transport, 'control.conf')
1148
def set_default_stack_on(self, value):
1149
"""Set the default stacking location.
1151
It may be set to a location, or None.
1153
This policy affects all branches contained by this bzrdir, except for
1154
those under repositories.
1157
self._config.set_option('', 'default_stack_on')
1159
self._config.set_option(value, 'default_stack_on')
1161
def get_default_stack_on(self):
1162
"""Return the default stacking location.
1164
This will either be a location, or None.
1166
This policy affects all branches contained by this bzrdir, except for
1167
those under repositories.
1169
value = self._config.get_option('default_stack_on')
1175
class TransportConfig(object):
1176
"""A Config that reads/writes a config file on a Transport.
1178
It is a low-level object that considers config data to be name/value pairs
1179
that may be associated with a section. Assigning meaning to the these
1180
values is done at higher levels like TreeConfig.
1183
def __init__(self, transport, filename):
1184
self._transport = transport
1185
self._filename = filename
1187
def get_option(self, name, section=None, default=None):
1188
"""Return the value associated with a named option.
1190
:param name: The name of the value
1191
:param section: The section the option is in (if any)
1192
:param default: The value to return if the value is not set
1193
:return: The value or default value
1195
configobj = self._get_configobj()
1197
section_obj = configobj
1200
section_obj = configobj[section]
1203
return section_obj.get(name, default)
1205
def set_option(self, value, name, section=None):
1206
"""Set the value associated with a named option.
1208
:param value: The value to set
1209
:param name: The name of the value to set
1210
:param section: The section the option is in (if any)
1212
configobj = self._get_configobj()
1214
configobj[name] = value
1216
configobj.setdefault(section, {})[name] = value
1217
self._set_configobj(configobj)
1219
def _get_configobj(self):
1221
return ConfigObj(self._transport.get(self._filename),
1223
except errors.NoSuchFile:
1224
return ConfigObj(encoding='utf-8')
1226
def _set_configobj(self, configobj):
1227
out_file = StringIO()
1228
configobj.write(out_file)
1230
self._transport.put_file(self._filename, out_file)