875
860
return realname, (username + '@' + socket.gethostname())
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)
882
return (username, '')
884
return (match.group(1), match.group(2))
887
863
def extract_email_address(e):
888
864
"""Return just the address part of an email string.
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.
894
870
>>> extract_email_address('Jane Tester <jane@test.com>')
897
name, email = parse_username(e)
873
m = re.search(r'[\w+.-]+@[\w+.-]+', e)
899
875
raise errors.NoEmailInUsername(e)
903
879
class TreeConfig(IniBasedConfig):
904
880
"""Branch configuration data associated with its contents, not location"""
906
# XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
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
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()
889
def _get_config(self):
891
obj = ConfigObj(self.branch.control_files.get('branch.conf'),
893
except errors.NoSuchFile:
894
obj = ConfigObj(encoding='utf=8')
920
897
def get_option(self, name, section=None, default=None):
921
898
self.branch.lock_read()
923
return self._config.get_option(name, section, default)
900
obj = self._get_config()
902
if section is not None:
925
908
self.branch.unlock()
929
912
"""Set a per-branch configuration option"""
930
913
self.branch.lock_write()
932
self._config.set_option(value, name, section)
915
cfg_obj = self._get_config()
920
obj = cfg_obj[section]
922
cfg_obj[section] = {}
923
obj = cfg_obj[section]
925
out_file = StringIO()
926
cfg_obj.write(out_file)
928
self.branch.control_files.put('branch.conf', out_file)
934
930
self.branch.unlock()
937
class AuthenticationConfig(object):
938
"""The authentication configuration file based on a ini file.
940
Implements the authentication.conf file described in
941
doc/developers/authentication-ring.txt.
944
def __init__(self, _file=None):
945
self._config = None # The ConfigObj
947
self._filename = authentication_config_filename()
948
self._input = self._filename = authentication_config_filename()
950
# Tests can provide a string as _file
951
self._filename = None
954
def _get_config(self):
955
if self._config is not None:
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.
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)
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'))
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)
981
section = conf[section]
982
section[option_name] = value
985
def get_credentials(self, scheme, host, port=None, user=None, path=None):
986
"""Returns the matching credentials from authentication.conf file.
988
:param scheme: protocol
990
:param host: the server address
992
:param port: the associated port (optional)
994
:param user: login (optional)
996
:param path: the absolute path on the server (optional)
998
:return: A dict containing the matching credentials or None.
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.
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)
1013
a_scheme, a_host, a_user, a_path = map(
1014
auth_def.get, ['scheme', 'host', 'user', 'path'])
1017
a_port = auth_def.as_int('port')
1021
raise ValueError("'port' not numeric in %s" % auth_def_name)
1023
a_verify_certificates = auth_def.as_bool('verify_certificates')
1025
a_verify_certificates = True
1028
"'verify_certificates' not boolean in %s" % auth_def_name)
1031
if a_scheme is not None and scheme != a_scheme:
1033
if a_host is not None:
1034
if not (host == a_host
1035
or (a_host.startswith('.') and host.endswith(a_host))):
1037
if a_port is not None and port != a_port:
1039
if (a_path is not None and path is not None
1040
and not path.startswith(a_path)):
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
1049
credentials = dict(name=auth_def_name,
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)
1061
def get_user(self, scheme, host, port=None,
1062
realm=None, path=None, prompt=None):
1063
"""Get a user from authentication file.
1065
:param scheme: protocol
1067
:param host: the server address
1069
:param port: the associated port (optional)
1071
:param realm: the realm sent by the server (optional)
1073
:param path: the absolute path on the server (optional)
1075
:return: The found user.
1077
credentials = self.get_credentials(scheme, host, port, user=None,
1079
if credentials is not None:
1080
user = credentials['user']
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.
1089
:param scheme: protocol
1091
:param host: the server address
1093
:param port: the associated port (optional)
1097
:param realm: the realm sent by the server (optional)
1099
:param path: the absolute path on the server (optional)
1101
:return: The found password or the one entered by the user.
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'])
1113
# Prompt user only if we could't find a password
1114
if password 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)
1123
password = ui.ui_factory.get_password(prompt,
1124
host=prompt_host, user=user)
1127
def decode_password(self, credentials, encoding):
1131
class BzrDirConfig(object):
1133
def __init__(self, transport):
1134
self._config = TransportConfig(transport, 'control.conf')
1136
def set_default_stack_on(self, value):
1137
"""Set the default stacking location.
1139
It may be set to a location, or None.
1141
This policy affects all branches contained by this bzrdir, except for
1142
those under repositories.
1145
self._config.set_option('', 'default_stack_on')
1147
self._config.set_option(value, 'default_stack_on')
1149
def get_default_stack_on(self):
1150
"""Return the default stacking location.
1152
This will either be a location, or None.
1154
This policy affects all branches contained by this bzrdir, except for
1155
those under repositories.
1157
value = self._config.get_option('default_stack_on')
1163
class TransportConfig(object):
1164
"""A Config that reads/writes a config file on a Transport.
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.
1171
def __init__(self, transport, filename):
1172
self._transport = transport
1173
self._filename = filename
1175
def get_option(self, name, section=None, default=None):
1176
"""Return the value associated with a named option.
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
1183
configobj = self._get_configobj()
1185
section_obj = configobj
1188
section_obj = configobj[section]
1191
return section_obj.get(name, default)
1193
def set_option(self, value, name, section=None):
1194
"""Set the value associated with a named option.
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)
1200
configobj = self._get_configobj()
1202
configobj[name] = value
1204
configobj.setdefault(section, {})[name] = value
1205
self._set_configobj(configobj)
1207
def _get_configobj(self):
1209
return ConfigObj(self._transport.get(self._filename),
1211
except errors.NoSuchFile:
1212
return ConfigObj(encoding='utf-8')
1214
def _set_configobj(self, configobj):
1215
out_file = StringIO()
1216
configobj.write(out_file)
1218
self._transport.put_file(self._filename, out_file)