/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Robert Collins
  • Date: 2010-05-04 06:22:51 UTC
  • mto: This revision was merged to the branch mainline in revision 5206.
  • Revision ID: robertc@robertcollins.net-20100504062251-1ocjhrl53mum9ehw
Minor local_abspath docstring improvement.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
 
3
#            and others
3
4
#
4
5
# This program is free software; you can redistribute it and/or modify
5
6
# it under the terms of the GNU General Public License as published by
13
14
#
14
15
# You should have received a copy of the GNU General Public License
15
16
# along with this program; if not, write to the Free Software
16
 
# 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
17
18
 
18
19
"""Configuration that affects the behaviour of Bazaar.
19
20
 
20
21
Currently this configuration resides in ~/.bazaar/bazaar.conf
21
 
and ~/.bazaar/branches.conf, which is written to by bzr.
 
22
and ~/.bazaar/locations.conf, which is written to by bzr.
22
23
 
23
24
In bazaar.conf the following options may be set:
24
25
[DEFAULT]
27
28
check_signatures=require|ignore|check-available(default)
28
29
create_signatures=always|never|when-required(default)
29
30
gpg_signing_command=name-of-program
 
31
log_format=name-of-format
30
32
 
31
 
in branches.conf, you specify the url of a branch and options for it.
 
33
in locations.conf, you specify the url of a branch and options for it.
32
34
Wildcards may be used - * and ? as normal in shell completion. Options
33
 
set in both bazaar.conf and branches.conf are overriden by the branches.conf
 
35
set in both bazaar.conf and locations.conf are overridden by the locations.conf
34
36
setting.
35
37
[/home/robertc/source]
36
38
recurse=False|True(default)
37
39
email= as above
38
 
check_signatures= as abive 
 
40
check_signatures= as above
39
41
create_signatures= as above.
40
42
 
41
43
explanation of options
43
45
editor - this option sets the pop up editor to use during commits.
44
46
email - this option sets the user id bzr will use when committing.
45
47
check_signatures - this option controls whether bzr will require good gpg
46
 
                   signatures, ignore them, or check them if they are 
 
48
                   signatures, ignore them, or check them if they are
47
49
                   present.
48
 
create_signatures - this option controls whether bzr will always create 
 
50
create_signatures - this option controls whether bzr will always create
49
51
                    gpg signatures, never create them, or create them if the
50
52
                    branch is configured to require them.
51
 
                    NB: This option is planned, but not implemented yet.
 
53
log_format - this option sets the default log format.  Possible values are
 
54
             long, short, line, or a plugin can register new formats.
 
55
 
 
56
In bazaar.conf you can also define aliases in the ALIASES sections, example
 
57
 
 
58
[ALIASES]
 
59
lastlog=log --line -r-10..-1
 
60
ll=log --line -r-10..-1
 
61
h=help
 
62
up=pull
52
63
"""
53
64
 
54
 
 
55
 
import errno
56
65
import os
57
66
import sys
 
67
 
 
68
from bzrlib.lazy_import import lazy_import
 
69
lazy_import(globals(), """
 
70
import errno
58
71
from fnmatch import fnmatch
59
72
import re
 
73
from cStringIO import StringIO
60
74
 
61
75
import bzrlib
62
 
import bzrlib.errors as errors
63
 
import bzrlib.util.configobj.configobj as configobj
64
 
from StringIO import StringIO
 
76
from bzrlib import (
 
77
    debug,
 
78
    errors,
 
79
    mail_client,
 
80
    osutils,
 
81
    registry,
 
82
    symbol_versioning,
 
83
    trace,
 
84
    ui,
 
85
    urlutils,
 
86
    win32utils,
 
87
    )
 
88
from bzrlib.util.configobj import configobj
 
89
""")
 
90
 
65
91
 
66
92
CHECK_IF_POSSIBLE=0
67
93
CHECK_ALWAYS=1
68
94
CHECK_NEVER=2
69
95
 
70
96
 
71
 
class ConfigObj(configobj.ConfigObj):
72
 
 
73
 
    def get_bool(self, section, key):
74
 
        val = self[section][key].lower()
75
 
        if val in ('1', 'yes', 'true', 'on'):
76
 
            return True
77
 
        elif val in ('0', 'no', 'false', 'off'):
78
 
            return False
79
 
        else:
80
 
            raise ValueError("Value %r is not boolean" % val)
81
 
 
82
 
    def get_value(self, section, name):
83
 
        # Try [] for the old DEFAULT section.
84
 
        if section == "DEFAULT":
85
 
            try:
86
 
                return self[name]
87
 
            except KeyError:
88
 
                pass
89
 
        return self[section][name]
 
97
SIGN_WHEN_REQUIRED=0
 
98
SIGN_ALWAYS=1
 
99
SIGN_NEVER=2
 
100
 
 
101
 
 
102
POLICY_NONE = 0
 
103
POLICY_NORECURSE = 1
 
104
POLICY_APPENDPATH = 2
 
105
 
 
106
_policy_name = {
 
107
    POLICY_NONE: None,
 
108
    POLICY_NORECURSE: 'norecurse',
 
109
    POLICY_APPENDPATH: 'appendpath',
 
110
    }
 
111
_policy_value = {
 
112
    None: POLICY_NONE,
 
113
    'none': POLICY_NONE,
 
114
    'norecurse': POLICY_NORECURSE,
 
115
    'appendpath': POLICY_APPENDPATH,
 
116
    }
 
117
 
 
118
 
 
119
STORE_LOCATION = POLICY_NONE
 
120
STORE_LOCATION_NORECURSE = POLICY_NORECURSE
 
121
STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH
 
122
STORE_BRANCH = 3
 
123
STORE_GLOBAL = 4
 
124
 
 
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)
90
144
 
91
145
 
92
146
class Config(object):
93
147
    """A configuration policy - what username, editor, gpg needs etc."""
94
148
 
 
149
    def __init__(self):
 
150
        super(Config, self).__init__()
 
151
 
95
152
    def get_editor(self):
96
153
        """Get the users pop up editor."""
97
154
        raise NotImplementedError
98
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
 
 
165
    def get_mail_client(self):
 
166
        """Get a mail client to use"""
 
167
        selected_client = self.get_user_option('mail_client')
 
168
        _registry = mail_client.mail_client_registry
 
169
        try:
 
170
            mail_client_class = _registry.get(selected_client)
 
171
        except KeyError:
 
172
            raise errors.UnknownMailClient(selected_client)
 
173
        return mail_client_class(self)
 
174
 
99
175
    def _get_signature_checking(self):
100
176
        """Template method to override signature checking policy."""
101
177
 
 
178
    def _get_signing_policy(self):
 
179
        """Template method to override signature creation policy."""
 
180
 
102
181
    def _get_user_option(self, option_name):
103
182
        """Template method to provide a user option."""
104
183
        return None
107
186
        """Get a generic option - no special process, no default."""
108
187
        return self._get_user_option(option_name)
109
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
        if s is None:
 
197
            # The option doesn't exist
 
198
            return None
 
199
        val = ui.bool_from_string(s)
 
200
        if val is None:
 
201
            # The value can't be interpreted as a boolean
 
202
            trace.warning('Value "%s" is not a boolean for "%s"',
 
203
                          s, option_name)
 
204
        return val
 
205
 
 
206
    def get_user_option_as_list(self, option_name):
 
207
        """Get a generic option as a list - no special process, no default.
 
208
 
 
209
        :return None if the option doesn't exist. Returns the value as a list
 
210
            otherwise.
 
211
        """
 
212
        l = self._get_user_option(option_name)
 
213
        if isinstance(l, (str, unicode)):
 
214
            # A single value, most probably the user forgot the final ','
 
215
            l = [l]
 
216
        return l
 
217
 
110
218
    def gpg_signing_command(self):
111
219
        """What program should be used to sign signatures?"""
112
220
        result = self._gpg_signing_command()
118
226
        """See gpg_signing_command()."""
119
227
        return None
120
228
 
121
 
    def __init__(self):
122
 
        super(Config, self).__init__()
 
229
    def log_format(self):
 
230
        """What log format should be used"""
 
231
        result = self._log_format()
 
232
        if result is None:
 
233
            result = "long"
 
234
        return result
 
235
 
 
236
    def _log_format(self):
 
237
        """See log_format()."""
 
238
        return None
123
239
 
124
240
    def post_commit(self):
125
241
        """An ordered list of python functions to call.
138
254
 
139
255
    def username(self):
140
256
        """Return email-style username.
141
 
    
 
257
 
142
258
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
143
 
        
144
 
        $BZREMAIL can be set to override this, then
 
259
 
 
260
        $BZR_EMAIL can be set to override this (as well as the
 
261
        deprecated $BZREMAIL), then
145
262
        the concrete policy type is checked, and finally
146
263
        $EMAIL is examined.
147
264
        If none is found, a reasonable default is (hopefully)
148
265
        created.
149
 
    
 
266
 
150
267
        TODO: Check it's reasonably well-formed.
151
268
        """
152
 
        v = os.environ.get('BZREMAIL')
 
269
        v = os.environ.get('BZR_EMAIL')
153
270
        if v:
154
 
            return v.decode(bzrlib.user_encoding)
155
 
    
 
271
            return v.decode(osutils.get_user_encoding())
 
272
 
156
273
        v = self._get_user_id()
157
274
        if v:
158
275
            return v
159
 
        
 
276
 
160
277
        v = os.environ.get('EMAIL')
161
278
        if v:
162
 
            return v.decode(bzrlib.user_encoding)
 
279
            return v.decode(osutils.get_user_encoding())
163
280
 
164
281
        name, email = _auto_user_id()
165
282
        if name:
174
291
            return policy
175
292
        return CHECK_IF_POSSIBLE
176
293
 
 
294
    def signing_policy(self):
 
295
        """What is the current policy for signature checking?."""
 
296
        policy = self._get_signing_policy()
 
297
        if policy is not None:
 
298
            return policy
 
299
        return SIGN_WHEN_REQUIRED
 
300
 
177
301
    def signature_needed(self):
178
302
        """Is a signature needed when committing ?."""
179
 
        policy = self._get_signature_checking()
180
 
        if policy == CHECK_ALWAYS:
 
303
        policy = self._get_signing_policy()
 
304
        if policy is None:
 
305
            policy = self._get_signature_checking()
 
306
            if policy is not None:
 
307
                trace.warning("Please use create_signatures,"
 
308
                              " not check_signatures to set signing policy.")
 
309
            if policy == CHECK_ALWAYS:
 
310
                return True
 
311
        elif policy == SIGN_ALWAYS:
181
312
            return True
182
313
        return False
183
314
 
 
315
    def get_alias(self, value):
 
316
        return self._get_alias(value)
 
317
 
 
318
    def _get_alias(self, value):
 
319
        pass
 
320
 
 
321
    def get_nickname(self):
 
322
        return self._get_nickname()
 
323
 
 
324
    def _get_nickname(self):
 
325
        return None
 
326
 
 
327
    def get_bzr_remote_path(self):
 
328
        try:
 
329
            return os.environ['BZR_REMOTE_PATH']
 
330
        except KeyError:
 
331
            path = self.get_user_option("bzr_remote_path")
 
332
            if path is None:
 
333
                path = 'bzr'
 
334
            return path
 
335
 
 
336
    def suppress_warning(self, warning):
 
337
        """Should the warning be suppressed or emitted.
 
338
 
 
339
        :param warning: The name of the warning being tested.
 
340
 
 
341
        :returns: True if the warning should be suppressed, False otherwise.
 
342
        """
 
343
        warnings = self.get_user_option_as_list('suppress_warnings')
 
344
        if warnings is None or warning not in warnings:
 
345
            return False
 
346
        else:
 
347
            return True
 
348
 
184
349
 
185
350
class IniBasedConfig(Config):
186
351
    """A configuration policy that draws from ini files."""
187
352
 
 
353
    def __init__(self, get_filename):
 
354
        super(IniBasedConfig, self).__init__()
 
355
        self._get_filename = get_filename
 
356
        self._parser = None
 
357
 
188
358
    def _get_parser(self, file=None):
189
359
        if self._parser is not None:
190
360
            return self._parser
193
363
        else:
194
364
            input = file
195
365
        try:
196
 
            self._parser = ConfigObj(input)
 
366
            self._parser = ConfigObj(input, encoding='utf-8')
197
367
        except configobj.ConfigObjError, e:
198
368
            raise errors.ParseConfigError(e.errors, e.config.filename)
199
369
        return self._parser
200
370
 
 
371
    def _get_matching_sections(self):
 
372
        """Return an ordered list of (section_name, extra_path) pairs.
 
373
 
 
374
        If the section contains inherited configuration, extra_path is
 
375
        a string containing the additional path components.
 
376
        """
 
377
        section = self._get_section()
 
378
        if section is not None:
 
379
            return [(section, '')]
 
380
        else:
 
381
            return []
 
382
 
201
383
    def _get_section(self):
202
384
        """Override this to define the section used by the config."""
203
385
        return "DEFAULT"
204
386
 
 
387
    def _get_option_policy(self, section, option_name):
 
388
        """Return the policy for the given (section, option_name) pair."""
 
389
        return POLICY_NONE
 
390
 
 
391
    def _get_change_editor(self):
 
392
        return self.get_user_option('change_editor')
 
393
 
205
394
    def _get_signature_checking(self):
206
395
        """See Config._get_signature_checking."""
207
396
        policy = self._get_user_option('check_signatures')
208
397
        if policy:
209
398
            return self._string_to_signature_policy(policy)
210
399
 
 
400
    def _get_signing_policy(self):
 
401
        """See Config._get_signing_policy"""
 
402
        policy = self._get_user_option('create_signatures')
 
403
        if policy:
 
404
            return self._string_to_signing_policy(policy)
 
405
 
211
406
    def _get_user_id(self):
212
407
        """Get the user id from the 'email' key in the current section."""
213
408
        return self._get_user_option('email')
214
409
 
215
410
    def _get_user_option(self, option_name):
216
411
        """See Config._get_user_option."""
217
 
        try:
218
 
            return self._get_parser().get_value(self._get_section(),
219
 
                                                option_name)
220
 
        except KeyError:
221
 
            pass
 
412
        for (section, extra_path) in self._get_matching_sections():
 
413
            try:
 
414
                value = self._get_parser().get_value(section, option_name)
 
415
            except KeyError:
 
416
                continue
 
417
            policy = self._get_option_policy(section, option_name)
 
418
            if policy == POLICY_NONE:
 
419
                return value
 
420
            elif policy == POLICY_NORECURSE:
 
421
                # norecurse items only apply to the exact path
 
422
                if extra_path:
 
423
                    continue
 
424
                else:
 
425
                    return value
 
426
            elif policy == POLICY_APPENDPATH:
 
427
                if extra_path:
 
428
                    value = urlutils.join(value, extra_path)
 
429
                return value
 
430
            else:
 
431
                raise AssertionError('Unexpected config policy %r' % policy)
 
432
        else:
 
433
            return None
222
434
 
223
435
    def _gpg_signing_command(self):
224
436
        """See Config.gpg_signing_command."""
225
437
        return self._get_user_option('gpg_signing_command')
226
438
 
227
 
    def __init__(self, get_filename):
228
 
        super(IniBasedConfig, self).__init__()
229
 
        self._get_filename = get_filename
230
 
        self._parser = None
231
 
        
 
439
    def _log_format(self):
 
440
        """See Config.log_format."""
 
441
        return self._get_user_option('log_format')
 
442
 
232
443
    def _post_commit(self):
233
444
        """See Config.post_commit."""
234
445
        return self._get_user_option('post_commit')
244
455
        raise errors.BzrError("Invalid signatures policy '%s'"
245
456
                              % signature_string)
246
457
 
 
458
    def _string_to_signing_policy(self, signature_string):
 
459
        """Convert a string to a signing policy."""
 
460
        if signature_string.lower() == 'when-required':
 
461
            return SIGN_WHEN_REQUIRED
 
462
        if signature_string.lower() == 'never':
 
463
            return SIGN_NEVER
 
464
        if signature_string.lower() == 'always':
 
465
            return SIGN_ALWAYS
 
466
        raise errors.BzrError("Invalid signing policy '%s'"
 
467
                              % signature_string)
 
468
 
 
469
    def _get_alias(self, value):
 
470
        try:
 
471
            return self._get_parser().get_value("ALIASES",
 
472
                                                value)
 
473
        except KeyError:
 
474
            pass
 
475
 
 
476
    def _get_nickname(self):
 
477
        return self.get_user_option('nickname')
 
478
 
247
479
 
248
480
class GlobalConfig(IniBasedConfig):
249
481
    """The configuration that should be used for a specific location."""
254
486
    def __init__(self):
255
487
        super(GlobalConfig, self).__init__(config_filename)
256
488
 
 
489
    def set_user_option(self, option, value):
 
490
        """Save option and its value in the configuration."""
 
491
        self._set_option(option, value, 'DEFAULT')
 
492
 
 
493
    def get_aliases(self):
 
494
        """Return the aliases section."""
 
495
        if 'ALIASES' in self._get_parser():
 
496
            return self._get_parser()['ALIASES']
 
497
        else:
 
498
            return {}
 
499
 
 
500
    def set_alias(self, alias_name, alias_command):
 
501
        """Save the alias in the configuration."""
 
502
        self._set_option(alias_name, alias_command, 'ALIASES')
 
503
 
 
504
    def unset_alias(self, alias_name):
 
505
        """Unset an existing alias."""
 
506
        aliases = self._get_parser().get('ALIASES')
 
507
        if not aliases or alias_name not in aliases:
 
508
            raise errors.NoSuchAlias(alias_name)
 
509
        del aliases[alias_name]
 
510
        self._write_config_file()
 
511
 
 
512
    def _set_option(self, option, value, section):
 
513
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
514
        # file lock on bazaar.conf.
 
515
        conf_dir = os.path.dirname(self._get_filename())
 
516
        ensure_config_dir_exists(conf_dir)
 
517
        self._get_parser().setdefault(section, {})[option] = value
 
518
        self._write_config_file()
 
519
 
 
520
    def _write_config_file(self):
 
521
        path = self._get_filename()
 
522
        f = open(path, 'wb')
 
523
        osutils.copy_ownership_from_path(path)
 
524
        self._get_parser().write(f)
 
525
        f.close()
 
526
 
257
527
 
258
528
class LocationConfig(IniBasedConfig):
259
529
    """A configuration object that gives the policy for a location."""
260
530
 
261
531
    def __init__(self, location):
262
 
        super(LocationConfig, self).__init__(branches_config_filename)
263
 
        self._global_config = None
 
532
        name_generator = locations_config_filename
 
533
        if (not os.path.exists(name_generator()) and
 
534
                os.path.exists(branches_config_filename())):
 
535
            if sys.platform == 'win32':
 
536
                trace.warning('Please rename %s to %s'
 
537
                              % (branches_config_filename(),
 
538
                                 locations_config_filename()))
 
539
            else:
 
540
                trace.warning('Please rename ~/.bazaar/branches.conf'
 
541
                              ' to ~/.bazaar/locations.conf')
 
542
            name_generator = branches_config_filename
 
543
        super(LocationConfig, self).__init__(name_generator)
 
544
        # local file locations are looked up by local path, rather than
 
545
        # by file url. This is because the config file is a user
 
546
        # file, and we would rather not expose the user to file urls.
 
547
        if location.startswith('file://'):
 
548
            location = urlutils.local_path_from_url(location)
264
549
        self.location = location
265
550
 
266
 
    def _get_global_config(self):
267
 
        if self._global_config is None:
268
 
            self._global_config = GlobalConfig()
269
 
        return self._global_config
270
 
 
271
 
    def _get_section(self):
272
 
        """Get the section we should look in for config items.
273
 
 
274
 
        Returns None if none exists. 
275
 
        TODO: perhaps return a NullSection that thunks through to the 
276
 
              global config.
277
 
        """
 
551
    def _get_matching_sections(self):
 
552
        """Return an ordered list of section names matching this location."""
278
553
        sections = self._get_parser()
279
554
        location_names = self.location.split('/')
280
555
        if self.location.endswith('/'):
281
556
            del location_names[-1]
282
557
        matches=[]
283
558
        for section in sections:
284
 
            section_names = section.split('/')
 
559
            # location is a local path if possible, so we need
 
560
            # to convert 'file://' urls to local paths if necessary.
 
561
            # This also avoids having file:///path be a more exact
 
562
            # match than '/path'.
 
563
            if section.startswith('file://'):
 
564
                section_path = urlutils.local_path_from_url(section)
 
565
            else:
 
566
                section_path = section
 
567
            section_names = section_path.split('/')
285
568
            if section.endswith('/'):
286
569
                del section_names[-1]
287
570
            names = zip(location_names, section_names)
296
579
            # if section is longer, no match.
297
580
            if len(section_names) > len(location_names):
298
581
                continue
299
 
            # if path is longer, and recurse is not true, no match
300
 
            if len(section_names) < len(location_names):
301
 
                try:
302
 
                    if not self._get_parser().get_bool(section, 'recurse'):
303
 
                        continue
304
 
                except KeyError:
305
 
                    pass
306
 
            matches.append((len(section_names), section))
307
 
        if not len(matches):
308
 
            return None
 
582
            matches.append((len(section_names), section,
 
583
                            '/'.join(location_names[len(section_names):])))
309
584
        matches.sort(reverse=True)
310
 
        return matches[0][1]
311
 
 
312
 
    def _gpg_signing_command(self):
313
 
        """See Config.gpg_signing_command."""
314
 
        command = super(LocationConfig, self)._gpg_signing_command()
315
 
        if command is not None:
316
 
            return command
317
 
        return self._get_global_config()._gpg_signing_command()
318
 
 
319
 
    def _get_user_id(self):
320
 
        user_id = super(LocationConfig, self)._get_user_id()
321
 
        if user_id is not None:
322
 
            return user_id
323
 
        return self._get_global_config()._get_user_id()
324
 
 
325
 
    def _get_user_option(self, option_name):
326
 
        """See Config._get_user_option."""
327
 
        option_value = super(LocationConfig, 
328
 
                             self)._get_user_option(option_name)
329
 
        if option_value is not None:
330
 
            return option_value
331
 
        return self._get_global_config()._get_user_option(option_name)
332
 
 
333
 
    def _get_signature_checking(self):
334
 
        """See Config._get_signature_checking."""
335
 
        check = super(LocationConfig, self)._get_signature_checking()
336
 
        if check is not None:
337
 
            return check
338
 
        return self._get_global_config()._get_signature_checking()
339
 
 
340
 
    def _post_commit(self):
341
 
        """See Config.post_commit."""
342
 
        hook = self._get_user_option('post_commit')
343
 
        if hook is not None:
344
 
            return hook
345
 
        return self._get_global_config()._post_commit()
346
 
 
347
 
    def set_user_option(self, option, value):
 
585
        sections = []
 
586
        for (length, section, extra_path) in matches:
 
587
            sections.append((section, extra_path))
 
588
            # should we stop looking for parent configs here?
 
589
            try:
 
590
                if self._get_parser()[section].as_bool('ignore_parents'):
 
591
                    break
 
592
            except KeyError:
 
593
                pass
 
594
        return sections
 
595
 
 
596
    def _get_option_policy(self, section, option_name):
 
597
        """Return the policy for the given (section, option_name) pair."""
 
598
        # check for the old 'recurse=False' flag
 
599
        try:
 
600
            recurse = self._get_parser()[section].as_bool('recurse')
 
601
        except KeyError:
 
602
            recurse = True
 
603
        if not recurse:
 
604
            return POLICY_NORECURSE
 
605
 
 
606
        policy_key = option_name + ':policy'
 
607
        try:
 
608
            policy_name = self._get_parser()[section][policy_key]
 
609
        except KeyError:
 
610
            policy_name = None
 
611
 
 
612
        return _policy_value[policy_name]
 
613
 
 
614
    def _set_option_policy(self, section, option_name, option_policy):
 
615
        """Set the policy for the given option name in the given section."""
 
616
        # The old recurse=False option affects all options in the
 
617
        # section.  To handle multiple policies in the section, we
 
618
        # need to convert it to a policy_norecurse key.
 
619
        try:
 
620
            recurse = self._get_parser()[section].as_bool('recurse')
 
621
        except KeyError:
 
622
            pass
 
623
        else:
 
624
            symbol_versioning.warn(
 
625
                'The recurse option is deprecated as of 0.14.  '
 
626
                'The section "%s" has been converted to use policies.'
 
627
                % section,
 
628
                DeprecationWarning)
 
629
            del self._get_parser()[section]['recurse']
 
630
            if not recurse:
 
631
                for key in self._get_parser()[section].keys():
 
632
                    if not key.endswith(':policy'):
 
633
                        self._get_parser()[section][key +
 
634
                                                    ':policy'] = 'norecurse'
 
635
 
 
636
        policy_key = option_name + ':policy'
 
637
        policy_name = _policy_name[option_policy]
 
638
        if policy_name is not None:
 
639
            self._get_parser()[section][policy_key] = policy_name
 
640
        else:
 
641
            if policy_key in self._get_parser()[section]:
 
642
                del self._get_parser()[section][policy_key]
 
643
 
 
644
    def set_user_option(self, option, value, store=STORE_LOCATION):
348
645
        """Save option and its value in the configuration."""
 
646
        if store not in [STORE_LOCATION,
 
647
                         STORE_LOCATION_NORECURSE,
 
648
                         STORE_LOCATION_APPENDPATH]:
 
649
            raise ValueError('bad storage policy %r for %r' %
 
650
                (store, option))
349
651
        # FIXME: RBC 20051029 This should refresh the parser and also take a
350
 
        # file lock on branches.conf.
351
 
        if not os.path.isdir(os.path.dirname(self._get_filename())):
352
 
            os.mkdir(os.path.dirname(self._get_filename()))
 
652
        # file lock on locations.conf.
 
653
        conf_dir = os.path.dirname(self._get_filename())
 
654
        ensure_config_dir_exists(conf_dir)
353
655
        location = self.location
354
656
        if location.endswith('/'):
355
657
            location = location[:-1]
359
661
        elif location + '/' in self._get_parser():
360
662
            location = location + '/'
361
663
        self._get_parser()[location][option]=value
362
 
        self._get_parser().write()
 
664
        # the allowed values of store match the config policies
 
665
        self._set_option_policy(location, option, store)
 
666
        self._get_parser().write(file(self._get_filename(), 'wb'))
363
667
 
364
668
 
365
669
class BranchConfig(Config):
366
670
    """A configuration object giving the policy for a branch."""
367
671
 
 
672
    def _get_branch_data_config(self):
 
673
        if self._branch_data_config is None:
 
674
            self._branch_data_config = TreeConfig(self.branch)
 
675
        return self._branch_data_config
 
676
 
368
677
    def _get_location_config(self):
369
678
        if self._location_config is None:
370
679
            self._location_config = LocationConfig(self.branch.base)
371
680
        return self._location_config
372
681
 
 
682
    def _get_global_config(self):
 
683
        if self._global_config is None:
 
684
            self._global_config = GlobalConfig()
 
685
        return self._global_config
 
686
 
 
687
    def _get_best_value(self, option_name):
 
688
        """This returns a user option from local, tree or global config.
 
689
 
 
690
        They are tried in that order.  Use get_safe_value if trusted values
 
691
        are necessary.
 
692
        """
 
693
        for source in self.option_sources:
 
694
            value = getattr(source(), option_name)()
 
695
            if value is not None:
 
696
                return value
 
697
        return None
 
698
 
 
699
    def _get_safe_value(self, option_name):
 
700
        """This variant of get_best_value never returns untrusted values.
 
701
 
 
702
        It does not return values from the branch data, because the branch may
 
703
        not be controlled by the user.
 
704
 
 
705
        We may wish to allow locations.conf to control whether branches are
 
706
        trusted in the future.
 
707
        """
 
708
        for source in (self._get_location_config, self._get_global_config):
 
709
            value = getattr(source(), option_name)()
 
710
            if value is not None:
 
711
                return value
 
712
        return None
 
713
 
373
714
    def _get_user_id(self):
374
715
        """Return the full user id for the branch.
375
 
    
376
 
        e.g. "John Hacker <jhacker@foo.org>"
 
716
 
 
717
        e.g. "John Hacker <jhacker@example.com>"
377
718
        This is looked up in the email controlfile for the branch.
378
719
        """
379
720
        try:
380
 
            return (self.branch.controlfile("email", "r") 
381
 
                    .read()
382
 
                    .decode(bzrlib.user_encoding)
 
721
            return (self.branch._transport.get_bytes("email")
 
722
                    .decode(osutils.get_user_encoding())
383
723
                    .rstrip("\r\n"))
384
724
        except errors.NoSuchFile, e:
385
725
            pass
386
 
        
387
 
        return self._get_location_config()._get_user_id()
 
726
 
 
727
        return self._get_best_value('_get_user_id')
 
728
 
 
729
    def _get_change_editor(self):
 
730
        return self._get_best_value('_get_change_editor')
388
731
 
389
732
    def _get_signature_checking(self):
390
733
        """See Config._get_signature_checking."""
391
 
        return self._get_location_config()._get_signature_checking()
 
734
        return self._get_best_value('_get_signature_checking')
 
735
 
 
736
    def _get_signing_policy(self):
 
737
        """See Config._get_signing_policy."""
 
738
        return self._get_best_value('_get_signing_policy')
392
739
 
393
740
    def _get_user_option(self, option_name):
394
741
        """See Config._get_user_option."""
395
 
        return self._get_location_config()._get_user_option(option_name)
 
742
        for source in self.option_sources:
 
743
            value = source()._get_user_option(option_name)
 
744
            if value is not None:
 
745
                return value
 
746
        return None
 
747
 
 
748
    def set_user_option(self, name, value, store=STORE_BRANCH,
 
749
        warn_masked=False):
 
750
        if store == STORE_BRANCH:
 
751
            self._get_branch_data_config().set_option(value, name)
 
752
        elif store == STORE_GLOBAL:
 
753
            self._get_global_config().set_user_option(name, value)
 
754
        else:
 
755
            self._get_location_config().set_user_option(name, value, store)
 
756
        if not warn_masked:
 
757
            return
 
758
        if store in (STORE_GLOBAL, STORE_BRANCH):
 
759
            mask_value = self._get_location_config().get_user_option(name)
 
760
            if mask_value is not None:
 
761
                trace.warning('Value "%s" is masked by "%s" from'
 
762
                              ' locations.conf', value, mask_value)
 
763
            else:
 
764
                if store == STORE_GLOBAL:
 
765
                    branch_config = self._get_branch_data_config()
 
766
                    mask_value = branch_config.get_user_option(name)
 
767
                    if mask_value is not None:
 
768
                        trace.warning('Value "%s" is masked by "%s" from'
 
769
                                      ' branch.conf', value, mask_value)
396
770
 
397
771
    def _gpg_signing_command(self):
398
772
        """See Config.gpg_signing_command."""
399
 
        return self._get_location_config()._gpg_signing_command()
400
 
        
 
773
        return self._get_safe_value('_gpg_signing_command')
 
774
 
401
775
    def __init__(self, branch):
402
776
        super(BranchConfig, self).__init__()
403
777
        self._location_config = None
 
778
        self._branch_data_config = None
 
779
        self._global_config = None
404
780
        self.branch = branch
 
781
        self.option_sources = (self._get_location_config,
 
782
                               self._get_branch_data_config,
 
783
                               self._get_global_config)
405
784
 
406
785
    def _post_commit(self):
407
786
        """See Config.post_commit."""
408
 
        return self._get_location_config()._post_commit()
 
787
        return self._get_safe_value('_post_commit')
 
788
 
 
789
    def _get_nickname(self):
 
790
        value = self._get_explicit_nickname()
 
791
        if value is not None:
 
792
            return value
 
793
        return urlutils.unescape(self.branch.base.split('/')[-2])
 
794
 
 
795
    def has_explicit_nickname(self):
 
796
        """Return true if a nickname has been explicitly assigned."""
 
797
        return self._get_explicit_nickname() is not None
 
798
 
 
799
    def _get_explicit_nickname(self):
 
800
        return self._get_best_value('_get_nickname')
 
801
 
 
802
    def _log_format(self):
 
803
        """See Config.log_format."""
 
804
        return self._get_best_value('_log_format')
 
805
 
 
806
 
 
807
def ensure_config_dir_exists(path=None):
 
808
    """Make sure a configuration directory exists.
 
809
    This makes sure that the directory exists.
 
810
    On windows, since configuration directories are 2 levels deep,
 
811
    it makes sure both the directory and the parent directory exists.
 
812
    """
 
813
    if path is None:
 
814
        path = config_dir()
 
815
    if not os.path.isdir(path):
 
816
        if sys.platform == 'win32':
 
817
            parent_dir = os.path.dirname(path)
 
818
            if not os.path.isdir(parent_dir):
 
819
                trace.mutter('creating config parent directory: %r', parent_dir)
 
820
            os.mkdir(parent_dir)
 
821
        trace.mutter('creating config directory: %r', path)
 
822
        os.mkdir(path)
 
823
        osutils.copy_ownership_from_path(path)
409
824
 
410
825
 
411
826
def config_dir():
412
827
    """Return per-user configuration directory.
413
828
 
414
829
    By default this is ~/.bazaar/
415
 
    
 
830
 
416
831
    TODO: Global option --config-dir to override this.
417
832
    """
418
833
    base = os.environ.get('BZR_HOME', None)
419
834
    if sys.platform == 'win32':
420
835
        if base is None:
421
 
            base = os.environ.get('APPDATA', None)
 
836
            base = win32utils.get_appdata_location_unicode()
422
837
        if base is None:
423
838
            base = os.environ.get('HOME', None)
424
839
        if base is None:
425
 
            raise BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
426
 
        return os.path.join(base, 'bazaar', '2.0')
 
840
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
 
841
                                  ' or HOME set')
 
842
        return osutils.pathjoin(base, 'bazaar', '2.0')
427
843
    else:
428
844
        # cygwin, linux, and darwin all have a $HOME directory
429
845
        if base is None:
430
846
            base = os.path.expanduser("~")
431
 
        return os.path.join(base, ".bazaar")
 
847
        return osutils.pathjoin(base, ".bazaar")
432
848
 
433
849
 
434
850
def config_filename():
435
851
    """Return per-user configuration ini file filename."""
436
 
    return os.path.join(config_dir(), 'bazaar.conf')
 
852
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
437
853
 
438
854
 
439
855
def branches_config_filename():
440
856
    """Return per-user configuration ini file filename."""
441
 
    return os.path.join(config_dir(), 'branches.conf')
 
857
    return osutils.pathjoin(config_dir(), 'branches.conf')
 
858
 
 
859
 
 
860
def locations_config_filename():
 
861
    """Return per-user configuration ini file filename."""
 
862
    return osutils.pathjoin(config_dir(), 'locations.conf')
 
863
 
 
864
 
 
865
def authentication_config_filename():
 
866
    """Return per-user authentication ini file filename."""
 
867
    return osutils.pathjoin(config_dir(), 'authentication.conf')
 
868
 
 
869
 
 
870
def user_ignore_config_filename():
 
871
    """Return the user default ignore filename"""
 
872
    return osutils.pathjoin(config_dir(), 'ignore')
 
873
 
 
874
 
 
875
def crash_dir():
 
876
    """Return the directory name to store crash files.
 
877
 
 
878
    This doesn't implicitly create it.
 
879
 
 
880
    On Windows it's in the config directory; elsewhere it's /var/crash
 
881
    which may be monitored by apport.  It can be overridden by
 
882
    $APPORT_CRASH_DIR.
 
883
    """
 
884
    if sys.platform == 'win32':
 
885
        return osutils.pathjoin(config_dir(), 'Crash')
 
886
    else:
 
887
        # XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp
 
888
        # 2010-01-31
 
889
        return os.environ.get('APPORT_CRASH_DIR', '/var/crash')
 
890
 
 
891
 
 
892
def xdg_cache_dir():
 
893
    # See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
 
894
    # Possibly this should be different on Windows?
 
895
    e = os.environ.get('XDG_CACHE_DIR', None)
 
896
    if e:
 
897
        return e
 
898
    else:
 
899
        return os.path.expanduser('~/.cache')
442
900
 
443
901
 
444
902
def _auto_user_id():
454
912
    """
455
913
    import socket
456
914
 
457
 
    # XXX: Any good way to get real user name on win32?
 
915
    if sys.platform == 'win32':
 
916
        name = win32utils.get_user_name_unicode()
 
917
        if name is None:
 
918
            raise errors.BzrError("Cannot autodetect user name.\n"
 
919
                                  "Please, set your name with command like:\n"
 
920
                                  'bzr whoami "Your Name <name@domain.com>"')
 
921
        host = win32utils.get_host_name_unicode()
 
922
        if host is None:
 
923
            host = socket.gethostname()
 
924
        return name, (name + '@' + host)
458
925
 
459
926
    try:
460
927
        import pwd
461
928
        uid = os.getuid()
462
 
        w = pwd.getpwuid(uid)
463
 
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
464
 
        username = w.pw_name.decode(bzrlib.user_encoding)
 
929
        try:
 
930
            w = pwd.getpwuid(uid)
 
931
        except KeyError:
 
932
            raise errors.BzrCommandError('Unable to determine your name.  '
 
933
                'Please use "bzr whoami" to set it.')
 
934
 
 
935
        # we try utf-8 first, because on many variants (like Linux),
 
936
        # /etc/passwd "should" be in utf-8, and because it's unlikely to give
 
937
        # false positives.  (many users will have their user encoding set to
 
938
        # latin-1, which cannot raise UnicodeError.)
 
939
        try:
 
940
            gecos = w.pw_gecos.decode('utf-8')
 
941
            encoding = 'utf-8'
 
942
        except UnicodeError:
 
943
            try:
 
944
                encoding = osutils.get_user_encoding()
 
945
                gecos = w.pw_gecos.decode(encoding)
 
946
            except UnicodeError:
 
947
                raise errors.BzrCommandError('Unable to determine your name.  '
 
948
                   'Use "bzr whoami" to set it.')
 
949
        try:
 
950
            username = w.pw_name.decode(encoding)
 
951
        except UnicodeError:
 
952
            raise errors.BzrCommandError('Unable to determine your name.  '
 
953
                'Use "bzr whoami" to set it.')
 
954
 
465
955
        comma = gecos.find(',')
466
956
        if comma == -1:
467
957
            realname = gecos
472
962
 
473
963
    except ImportError:
474
964
        import getpass
475
 
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
965
        try:
 
966
            user_encoding = osutils.get_user_encoding()
 
967
            realname = username = getpass.getuser().decode(user_encoding)
 
968
        except UnicodeDecodeError:
 
969
            raise errors.BzrError("Can't decode username as %s." % \
 
970
                    user_encoding)
476
971
 
477
972
    return realname, (username + '@' + socket.gethostname())
478
973
 
479
974
 
 
975
def parse_username(username):
 
976
    """Parse e-mail username and return a (name, address) tuple."""
 
977
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
 
978
    if match is None:
 
979
        return (username, '')
 
980
    else:
 
981
        return (match.group(1), match.group(2))
 
982
 
 
983
 
480
984
def extract_email_address(e):
481
985
    """Return just the address part of an email string.
482
 
    
483
 
    That is just the user@domain part, nothing else. 
 
986
 
 
987
    That is just the user@domain part, nothing else.
484
988
    This part is required to contain only ascii characters.
485
989
    If it can't be extracted, raises an error.
486
 
    
 
990
 
487
991
    >>> extract_email_address('Jane Tester <jane@test.com>')
488
992
    "jane@test.com"
489
993
    """
490
 
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
491
 
    if not m:
492
 
        raise errors.BzrError("%r doesn't seem to contain "
493
 
                              "a reasonable email address" % e)
494
 
    return m.group(0)
495
 
 
496
 
class TreeConfig(object):
 
994
    name, email = parse_username(e)
 
995
    if not email:
 
996
        raise errors.NoEmailInUsername(e)
 
997
    return email
 
998
 
 
999
 
 
1000
class TreeConfig(IniBasedConfig):
497
1001
    """Branch configuration data associated with its contents, not location"""
 
1002
 
 
1003
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
 
1004
 
498
1005
    def __init__(self, branch):
 
1006
        self._config = branch._get_config()
499
1007
        self.branch = branch
500
1008
 
501
 
    def _get_config(self):
502
 
        try:
503
 
            obj = ConfigObj(self.branch.controlfile('branch.conf',
504
 
                                                    'rb').readlines())
505
 
            obj.decode('UTF-8')
506
 
        except errors.NoSuchFile:
507
 
            obj = ConfigObj()
508
 
        return obj
 
1009
    def _get_parser(self, file=None):
 
1010
        if file is not None:
 
1011
            return IniBasedConfig._get_parser(file)
 
1012
        return self._config._get_configobj()
509
1013
 
510
1014
    def get_option(self, name, section=None, default=None):
511
1015
        self.branch.lock_read()
512
1016
        try:
513
 
            obj = self._get_config()
514
 
            try:
515
 
                if section is not None:
516
 
                    obj[section]
517
 
                result = obj[name]
518
 
            except KeyError:
519
 
                result = default
 
1017
            return self._config.get_option(name, section, default)
520
1018
        finally:
521
1019
            self.branch.unlock()
522
 
        return result
523
1020
 
524
1021
    def set_option(self, value, name, section=None):
525
1022
        """Set a per-branch configuration option"""
526
1023
        self.branch.lock_write()
527
1024
        try:
528
 
            cfg_obj = self._get_config()
529
 
            if section is None:
530
 
                obj = cfg_obj
531
 
            else:
532
 
                try:
533
 
                    obj = cfg_obj[section]
534
 
                except KeyError:
535
 
                    cfg_obj[section] = {}
536
 
                    obj = cfg_obj[section]
537
 
            obj[name] = value
538
 
            cfg_obj.encode('UTF-8')
539
 
            out_file = StringIO(''.join([l+'\n' for l in cfg_obj.write()]))
540
 
            out_file.seek(0)
541
 
            self.branch.put_controlfile('branch.conf', out_file, encode=False)
 
1025
            self._config.set_option(value, name, section)
542
1026
        finally:
543
1027
            self.branch.unlock()
 
1028
 
 
1029
 
 
1030
class AuthenticationConfig(object):
 
1031
    """The authentication configuration file based on a ini file.
 
1032
 
 
1033
    Implements the authentication.conf file described in
 
1034
    doc/developers/authentication-ring.txt.
 
1035
    """
 
1036
 
 
1037
    def __init__(self, _file=None):
 
1038
        self._config = None # The ConfigObj
 
1039
        if _file is None:
 
1040
            self._filename = authentication_config_filename()
 
1041
            self._input = self._filename = authentication_config_filename()
 
1042
        else:
 
1043
            # Tests can provide a string as _file
 
1044
            self._filename = None
 
1045
            self._input = _file
 
1046
 
 
1047
    def _get_config(self):
 
1048
        if self._config is not None:
 
1049
            return self._config
 
1050
        try:
 
1051
            # FIXME: Should we validate something here ? Includes: empty
 
1052
            # sections are useless, at least one of
 
1053
            # user/password/password_encoding should be defined, etc.
 
1054
 
 
1055
            # Note: the encoding below declares that the file itself is utf-8
 
1056
            # encoded, but the values in the ConfigObj are always Unicode.
 
1057
            self._config = ConfigObj(self._input, encoding='utf-8')
 
1058
        except configobj.ConfigObjError, e:
 
1059
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
1060
        return self._config
 
1061
 
 
1062
    def _save(self):
 
1063
        """Save the config file, only tests should use it for now."""
 
1064
        conf_dir = os.path.dirname(self._filename)
 
1065
        ensure_config_dir_exists(conf_dir)
 
1066
        self._get_config().write(file(self._filename, 'wb'))
 
1067
 
 
1068
    def _set_option(self, section_name, option_name, value):
 
1069
        """Set an authentication configuration option"""
 
1070
        conf = self._get_config()
 
1071
        section = conf.get(section_name)
 
1072
        if section is None:
 
1073
            conf[section] = {}
 
1074
            section = conf[section]
 
1075
        section[option_name] = value
 
1076
        self._save()
 
1077
 
 
1078
    def get_credentials(self, scheme, host, port=None, user=None, path=None, 
 
1079
                        realm=None):
 
1080
        """Returns the matching credentials from authentication.conf file.
 
1081
 
 
1082
        :param scheme: protocol
 
1083
 
 
1084
        :param host: the server address
 
1085
 
 
1086
        :param port: the associated port (optional)
 
1087
 
 
1088
        :param user: login (optional)
 
1089
 
 
1090
        :param path: the absolute path on the server (optional)
 
1091
        
 
1092
        :param realm: the http authentication realm (optional)
 
1093
 
 
1094
        :return: A dict containing the matching credentials or None.
 
1095
           This includes:
 
1096
           - name: the section name of the credentials in the
 
1097
             authentication.conf file,
 
1098
           - user: can't be different from the provided user if any,
 
1099
           - scheme: the server protocol,
 
1100
           - host: the server address,
 
1101
           - port: the server port (can be None),
 
1102
           - path: the absolute server path (can be None),
 
1103
           - realm: the http specific authentication realm (can be None),
 
1104
           - password: the decoded password, could be None if the credential
 
1105
             defines only the user
 
1106
           - verify_certificates: https specific, True if the server
 
1107
             certificate should be verified, False otherwise.
 
1108
        """
 
1109
        credentials = None
 
1110
        for auth_def_name, auth_def in self._get_config().items():
 
1111
            if type(auth_def) is not configobj.Section:
 
1112
                raise ValueError("%s defined outside a section" % auth_def_name)
 
1113
 
 
1114
            a_scheme, a_host, a_user, a_path = map(
 
1115
                auth_def.get, ['scheme', 'host', 'user', 'path'])
 
1116
 
 
1117
            try:
 
1118
                a_port = auth_def.as_int('port')
 
1119
            except KeyError:
 
1120
                a_port = None
 
1121
            except ValueError:
 
1122
                raise ValueError("'port' not numeric in %s" % auth_def_name)
 
1123
            try:
 
1124
                a_verify_certificates = auth_def.as_bool('verify_certificates')
 
1125
            except KeyError:
 
1126
                a_verify_certificates = True
 
1127
            except ValueError:
 
1128
                raise ValueError(
 
1129
                    "'verify_certificates' not boolean in %s" % auth_def_name)
 
1130
 
 
1131
            # Attempt matching
 
1132
            if a_scheme is not None and scheme != a_scheme:
 
1133
                continue
 
1134
            if a_host is not None:
 
1135
                if not (host == a_host
 
1136
                        or (a_host.startswith('.') and host.endswith(a_host))):
 
1137
                    continue
 
1138
            if a_port is not None and port != a_port:
 
1139
                continue
 
1140
            if (a_path is not None and path is not None
 
1141
                and not path.startswith(a_path)):
 
1142
                continue
 
1143
            if (a_user is not None and user is not None
 
1144
                and a_user != user):
 
1145
                # Never contradict the caller about the user to be used
 
1146
                continue
 
1147
            if a_user is None:
 
1148
                # Can't find a user
 
1149
                continue
 
1150
            # Prepare a credentials dictionary with additional keys
 
1151
            # for the credential providers
 
1152
            credentials = dict(name=auth_def_name,
 
1153
                               user=a_user,
 
1154
                               scheme=a_scheme,
 
1155
                               host=host,
 
1156
                               port=port,
 
1157
                               path=path,
 
1158
                               realm=realm,
 
1159
                               password=auth_def.get('password', None),
 
1160
                               verify_certificates=a_verify_certificates)
 
1161
            # Decode the password in the credentials (or get one)
 
1162
            self.decode_password(credentials,
 
1163
                                 auth_def.get('password_encoding', None))
 
1164
            if 'auth' in debug.debug_flags:
 
1165
                trace.mutter("Using authentication section: %r", auth_def_name)
 
1166
            break
 
1167
 
 
1168
        if credentials is None:
 
1169
            # No credentials were found in authentication.conf, try the fallback
 
1170
            # credentials stores.
 
1171
            credentials = credential_store_registry.get_fallback_credentials(
 
1172
                scheme, host, port, user, path, realm)
 
1173
 
 
1174
        return credentials
 
1175
 
 
1176
    def set_credentials(self, name, host, user, scheme=None, password=None,
 
1177
                        port=None, path=None, verify_certificates=None,
 
1178
                        realm=None):
 
1179
        """Set authentication credentials for a host.
 
1180
 
 
1181
        Any existing credentials with matching scheme, host, port and path
 
1182
        will be deleted, regardless of name.
 
1183
 
 
1184
        :param name: An arbitrary name to describe this set of credentials.
 
1185
        :param host: Name of the host that accepts these credentials.
 
1186
        :param user: The username portion of these credentials.
 
1187
        :param scheme: The URL scheme (e.g. ssh, http) the credentials apply
 
1188
            to.
 
1189
        :param password: Password portion of these credentials.
 
1190
        :param port: The IP port on the host that these credentials apply to.
 
1191
        :param path: A filesystem path on the host that these credentials
 
1192
            apply to.
 
1193
        :param verify_certificates: On https, verify server certificates if
 
1194
            True.
 
1195
        :param realm: The http authentication realm (optional).
 
1196
        """
 
1197
        values = {'host': host, 'user': user}
 
1198
        if password is not None:
 
1199
            values['password'] = password
 
1200
        if scheme is not None:
 
1201
            values['scheme'] = scheme
 
1202
        if port is not None:
 
1203
            values['port'] = '%d' % port
 
1204
        if path is not None:
 
1205
            values['path'] = path
 
1206
        if verify_certificates is not None:
 
1207
            values['verify_certificates'] = str(verify_certificates)
 
1208
        if realm is not None:
 
1209
            values['realm'] = realm
 
1210
        config = self._get_config()
 
1211
        for_deletion = []
 
1212
        for section, existing_values in config.items():
 
1213
            for key in ('scheme', 'host', 'port', 'path', 'realm'):
 
1214
                if existing_values.get(key) != values.get(key):
 
1215
                    break
 
1216
            else:
 
1217
                del config[section]
 
1218
        config.update({name: values})
 
1219
        self._save()
 
1220
 
 
1221
    def get_user(self, scheme, host, port=None, realm=None, path=None,
 
1222
                 prompt=None, ask=False, default=None):
 
1223
        """Get a user from authentication file.
 
1224
 
 
1225
        :param scheme: protocol
 
1226
 
 
1227
        :param host: the server address
 
1228
 
 
1229
        :param port: the associated port (optional)
 
1230
 
 
1231
        :param realm: the realm sent by the server (optional)
 
1232
 
 
1233
        :param path: the absolute path on the server (optional)
 
1234
 
 
1235
        :param ask: Ask the user if there is no explicitly configured username 
 
1236
                    (optional)
 
1237
 
 
1238
        :param default: The username returned if none is defined (optional).
 
1239
 
 
1240
        :return: The found user.
 
1241
        """
 
1242
        credentials = self.get_credentials(scheme, host, port, user=None,
 
1243
                                           path=path, realm=realm)
 
1244
        if credentials is not None:
 
1245
            user = credentials['user']
 
1246
        else:
 
1247
            user = None
 
1248
        if user is None:
 
1249
            if ask:
 
1250
                if prompt is None:
 
1251
                    # Create a default prompt suitable for most cases
 
1252
                    prompt = scheme.upper() + ' %(host)s username'
 
1253
                # Special handling for optional fields in the prompt
 
1254
                if port is not None:
 
1255
                    prompt_host = '%s:%d' % (host, port)
 
1256
                else:
 
1257
                    prompt_host = host
 
1258
                user = ui.ui_factory.get_username(prompt, host=prompt_host)
 
1259
            else:
 
1260
                user = default
 
1261
        return user
 
1262
 
 
1263
    def get_password(self, scheme, host, user, port=None,
 
1264
                     realm=None, path=None, prompt=None):
 
1265
        """Get a password from authentication file or prompt the user for one.
 
1266
 
 
1267
        :param scheme: protocol
 
1268
 
 
1269
        :param host: the server address
 
1270
 
 
1271
        :param port: the associated port (optional)
 
1272
 
 
1273
        :param user: login
 
1274
 
 
1275
        :param realm: the realm sent by the server (optional)
 
1276
 
 
1277
        :param path: the absolute path on the server (optional)
 
1278
 
 
1279
        :return: The found password or the one entered by the user.
 
1280
        """
 
1281
        credentials = self.get_credentials(scheme, host, port, user, path,
 
1282
                                           realm)
 
1283
        if credentials is not None:
 
1284
            password = credentials['password']
 
1285
            if password is not None and scheme is 'ssh':
 
1286
                trace.warning('password ignored in section [%s],'
 
1287
                              ' use an ssh agent instead'
 
1288
                              % credentials['name'])
 
1289
                password = None
 
1290
        else:
 
1291
            password = None
 
1292
        # Prompt user only if we could't find a password
 
1293
        if password is None:
 
1294
            if prompt is None:
 
1295
                # Create a default prompt suitable for most cases
 
1296
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
 
1297
            # Special handling for optional fields in the prompt
 
1298
            if port is not None:
 
1299
                prompt_host = '%s:%d' % (host, port)
 
1300
            else:
 
1301
                prompt_host = host
 
1302
            password = ui.ui_factory.get_password(prompt,
 
1303
                                                  host=prompt_host, user=user)
 
1304
        return password
 
1305
 
 
1306
    def decode_password(self, credentials, encoding):
 
1307
        try:
 
1308
            cs = credential_store_registry.get_credential_store(encoding)
 
1309
        except KeyError:
 
1310
            raise ValueError('%r is not a known password_encoding' % encoding)
 
1311
        credentials['password'] = cs.decode_password(credentials)
 
1312
        return credentials
 
1313
 
 
1314
 
 
1315
class CredentialStoreRegistry(registry.Registry):
 
1316
    """A class that registers credential stores.
 
1317
 
 
1318
    A credential store provides access to credentials via the password_encoding
 
1319
    field in authentication.conf sections.
 
1320
 
 
1321
    Except for stores provided by bzr itself, most stores are expected to be
 
1322
    provided by plugins that will therefore use
 
1323
    register_lazy(password_encoding, module_name, member_name, help=help,
 
1324
    fallback=fallback) to install themselves.
 
1325
 
 
1326
    A fallback credential store is one that is queried if no credentials can be
 
1327
    found via authentication.conf.
 
1328
    """
 
1329
 
 
1330
    def get_credential_store(self, encoding=None):
 
1331
        cs = self.get(encoding)
 
1332
        if callable(cs):
 
1333
            cs = cs()
 
1334
        return cs
 
1335
 
 
1336
    def is_fallback(self, name):
 
1337
        """Check if the named credentials store should be used as fallback."""
 
1338
        return self.get_info(name)
 
1339
 
 
1340
    def get_fallback_credentials(self, scheme, host, port=None, user=None,
 
1341
                                 path=None, realm=None):
 
1342
        """Request credentials from all fallback credentials stores.
 
1343
 
 
1344
        The first credentials store that can provide credentials wins.
 
1345
        """
 
1346
        credentials = None
 
1347
        for name in self.keys():
 
1348
            if not self.is_fallback(name):
 
1349
                continue
 
1350
            cs = self.get_credential_store(name)
 
1351
            credentials = cs.get_credentials(scheme, host, port, user,
 
1352
                                             path, realm)
 
1353
            if credentials is not None:
 
1354
                # We found some credentials
 
1355
                break
 
1356
        return credentials
 
1357
 
 
1358
    def register(self, key, obj, help=None, override_existing=False,
 
1359
                 fallback=False):
 
1360
        """Register a new object to a name.
 
1361
 
 
1362
        :param key: This is the key to use to request the object later.
 
1363
        :param obj: The object to register.
 
1364
        :param help: Help text for this entry. This may be a string or
 
1365
                a callable. If it is a callable, it should take two
 
1366
                parameters (registry, key): this registry and the key that
 
1367
                the help was registered under.
 
1368
        :param override_existing: Raise KeyErorr if False and something has
 
1369
                already been registered for that key. If True, ignore if there
 
1370
                is an existing key (always register the new value).
 
1371
        :param fallback: Whether this credential store should be 
 
1372
                used as fallback.
 
1373
        """
 
1374
        return super(CredentialStoreRegistry,
 
1375
                     self).register(key, obj, help, info=fallback,
 
1376
                                    override_existing=override_existing)
 
1377
 
 
1378
    def register_lazy(self, key, module_name, member_name,
 
1379
                      help=None, override_existing=False,
 
1380
                      fallback=False):
 
1381
        """Register a new credential store to be loaded on request.
 
1382
 
 
1383
        :param module_name: The python path to the module. Such as 'os.path'.
 
1384
        :param member_name: The member of the module to return.  If empty or
 
1385
                None, get() will return the module itself.
 
1386
        :param help: Help text for this entry. This may be a string or
 
1387
                a callable.
 
1388
        :param override_existing: If True, replace the existing object
 
1389
                with the new one. If False, if there is already something
 
1390
                registered with the same key, raise a KeyError
 
1391
        :param fallback: Whether this credential store should be 
 
1392
                used as fallback.
 
1393
        """
 
1394
        return super(CredentialStoreRegistry, self).register_lazy(
 
1395
            key, module_name, member_name, help,
 
1396
            info=fallback, override_existing=override_existing)
 
1397
 
 
1398
 
 
1399
credential_store_registry = CredentialStoreRegistry()
 
1400
 
 
1401
 
 
1402
class CredentialStore(object):
 
1403
    """An abstract class to implement storage for credentials"""
 
1404
 
 
1405
    def decode_password(self, credentials):
 
1406
        """Returns a clear text password for the provided credentials."""
 
1407
        raise NotImplementedError(self.decode_password)
 
1408
 
 
1409
    def get_credentials(self, scheme, host, port=None, user=None, path=None,
 
1410
                        realm=None):
 
1411
        """Return the matching credentials from this credential store.
 
1412
 
 
1413
        This method is only called on fallback credential stores.
 
1414
        """
 
1415
        raise NotImplementedError(self.get_credentials)
 
1416
 
 
1417
 
 
1418
 
 
1419
class PlainTextCredentialStore(CredentialStore):
 
1420
    __doc__ = """Plain text credential store for the authentication.conf file"""
 
1421
 
 
1422
    def decode_password(self, credentials):
 
1423
        """See CredentialStore.decode_password."""
 
1424
        return credentials['password']
 
1425
 
 
1426
 
 
1427
credential_store_registry.register('plain', PlainTextCredentialStore,
 
1428
                                   help=PlainTextCredentialStore.__doc__)
 
1429
credential_store_registry.default_key = 'plain'
 
1430
 
 
1431
 
 
1432
class BzrDirConfig(object):
 
1433
 
 
1434
    def __init__(self, bzrdir):
 
1435
        self._bzrdir = bzrdir
 
1436
        self._config = bzrdir._get_config()
 
1437
 
 
1438
    def set_default_stack_on(self, value):
 
1439
        """Set the default stacking location.
 
1440
 
 
1441
        It may be set to a location, or None.
 
1442
 
 
1443
        This policy affects all branches contained by this bzrdir, except for
 
1444
        those under repositories.
 
1445
        """
 
1446
        if self._config is None:
 
1447
            raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
 
1448
        if value is None:
 
1449
            self._config.set_option('', 'default_stack_on')
 
1450
        else:
 
1451
            self._config.set_option(value, 'default_stack_on')
 
1452
 
 
1453
    def get_default_stack_on(self):
 
1454
        """Return the default stacking location.
 
1455
 
 
1456
        This will either be a location, or None.
 
1457
 
 
1458
        This policy affects all branches contained by this bzrdir, except for
 
1459
        those under repositories.
 
1460
        """
 
1461
        if self._config is None:
 
1462
            return None
 
1463
        value = self._config.get_option('default_stack_on')
 
1464
        if value == '':
 
1465
            value = None
 
1466
        return value
 
1467
 
 
1468
 
 
1469
class TransportConfig(object):
 
1470
    """A Config that reads/writes a config file on a Transport.
 
1471
 
 
1472
    It is a low-level object that considers config data to be name/value pairs
 
1473
    that may be associated with a section.  Assigning meaning to the these
 
1474
    values is done at higher levels like TreeConfig.
 
1475
    """
 
1476
 
 
1477
    def __init__(self, transport, filename):
 
1478
        self._transport = transport
 
1479
        self._filename = filename
 
1480
 
 
1481
    def get_option(self, name, section=None, default=None):
 
1482
        """Return the value associated with a named option.
 
1483
 
 
1484
        :param name: The name of the value
 
1485
        :param section: The section the option is in (if any)
 
1486
        :param default: The value to return if the value is not set
 
1487
        :return: The value or default value
 
1488
        """
 
1489
        configobj = self._get_configobj()
 
1490
        if section is None:
 
1491
            section_obj = configobj
 
1492
        else:
 
1493
            try:
 
1494
                section_obj = configobj[section]
 
1495
            except KeyError:
 
1496
                return default
 
1497
        return section_obj.get(name, default)
 
1498
 
 
1499
    def set_option(self, value, name, section=None):
 
1500
        """Set the value associated with a named option.
 
1501
 
 
1502
        :param value: The value to set
 
1503
        :param name: The name of the value to set
 
1504
        :param section: The section the option is in (if any)
 
1505
        """
 
1506
        configobj = self._get_configobj()
 
1507
        if section is None:
 
1508
            configobj[name] = value
 
1509
        else:
 
1510
            configobj.setdefault(section, {})[name] = value
 
1511
        self._set_configobj(configobj)
 
1512
 
 
1513
    def _get_config_file(self):
 
1514
        try:
 
1515
            return StringIO(self._transport.get_bytes(self._filename))
 
1516
        except errors.NoSuchFile:
 
1517
            return StringIO()
 
1518
 
 
1519
    def _get_configobj(self):
 
1520
        return ConfigObj(self._get_config_file(), encoding='utf-8')
 
1521
 
 
1522
    def _set_configobj(self, configobj):
 
1523
        out_file = StringIO()
 
1524
        configobj.write(out_file)
 
1525
        out_file.seek(0)
 
1526
        self._transport.put_file(self._filename, out_file)