1# Copyright (C) 2005-2014, 2016 Canonical Ltd
2#   Authors: Robert Collins <robert.collins@canonical.com>
3#            and others
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19"""Configuration that affects the behaviour of Breezy.
20
21Currently this configuration resides in ~/.config/breezy/breezy.conf
22and ~/.config/breezy/locations.conf, which is written to by brz.
23
24If the first location doesn't exist, then brz falls back to reading
25Bazaar configuration files in ~/.bazaar or ~/.config/bazaar.
26
27In breezy.conf the following options may be set:
28[DEFAULT]
29editor=name-of-program
30email=Your Name <your@email.address>
31check_signatures=require|ignore|check-available(default)
32create_signatures=always|never|when-required(default)
33log_format=name-of-format
34validate_signatures_in_log=true|false(default)
35acceptable_keys=pattern1,pattern2
36gpg_signing_key=amy@example.com
37
38in locations.conf, you specify the url of a branch and options for it.
39Wildcards may be used - * and ? as normal in shell completion. Options
40set in both breezy.conf and locations.conf are overridden by the locations.conf
41setting.
42[/home/robertc/source]
43recurse=False|True(default)
44email= as above
45check_signatures= as above
46create_signatures= as above.
47validate_signatures_in_log=as above
48acceptable_keys=as above
49
50explanation of options
51----------------------
52editor - this option sets the pop up editor to use during commits.
53email - this option sets the user id brz will use when committing.
54check_signatures - this option will control whether brz will require good gpg
55                   signatures, ignore them, or check them if they are
56                   present.  Currently it is unused except that
57                   check_signatures turns on create_signatures.
58create_signatures - this option controls whether brz will always create
59                    gpg signatures or not on commits.  There is an unused
60                    option which in future is expected to work if
61                    branch settings require signatures.
62log_format - this option sets the default log format.  Possible values are
63             long, short, line, or a plugin can register new formats.
64validate_signatures_in_log - show GPG signature validity in log output
65acceptable_keys - comma separated list of key patterns acceptable for
66                  verify-signatures command
67
68In breezy.conf you can also define aliases in the ALIASES sections, example
69
70[ALIASES]
71lastlog=log --line -r-10..-1
72ll=log --line -r-10..-1
73h=help
74up=pull
75"""
76
77import os
78import sys
79
80import configobj
81from io import BytesIO
82
83import breezy
84from .lazy_import import lazy_import
85lazy_import(globals(), """
86import base64
87import errno
88import fnmatch
89import re
90import stat
91
92from breezy import (
93    cmdline,
94    controldir,
95    debug,
96    directory_service,
97    lock,
98    lockdir,
99    mergetools,
100    osutils,
101    trace,
102    transport,
103    ui,
104    urlutils,
105    win32utils,
106    )
107from breezy.i18n import gettext
108""")
109from . import (
110    commands,
111    bedding,
112    errors,
113    hooks,
114    lazy_regex,
115    registry,
116    )
117
118
119CHECK_IF_POSSIBLE = 0
120CHECK_ALWAYS = 1
121CHECK_NEVER = 2
122
123
124SIGN_WHEN_REQUIRED = 0
125SIGN_ALWAYS = 1
126SIGN_NEVER = 2
127
128
129POLICY_NONE = 0
130POLICY_NORECURSE = 1
131POLICY_APPENDPATH = 2
132
133_policy_name = {
134    POLICY_NONE: None,
135    POLICY_NORECURSE: 'norecurse',
136    POLICY_APPENDPATH: 'appendpath',
137    }
138_policy_value = {
139    None: POLICY_NONE,
140    'none': POLICY_NONE,
141    'norecurse': POLICY_NORECURSE,
142    'appendpath': POLICY_APPENDPATH,
143    }
144
145
146STORE_LOCATION = POLICY_NONE
147STORE_LOCATION_NORECURSE = POLICY_NORECURSE
148STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH
149STORE_BRANCH = 3
150STORE_GLOBAL = 4
151
152
153class OptionExpansionLoop(errors.BzrError):
154
155    _fmt = 'Loop involving %(refs)r while expanding "%(string)s".'
156
157    def __init__(self, string, refs):
158        self.string = string
159        self.refs = '->'.join(refs)
160
161
162class ExpandingUnknownOption(errors.BzrError):
163
164    _fmt = 'Option "%(name)s" is not defined while expanding "%(string)s".'
165
166    def __init__(self, name, string):
167        self.name = name
168        self.string = string
169
170
171class IllegalOptionName(errors.BzrError):
172
173    _fmt = 'Option "%(name)s" is not allowed.'
174
175    def __init__(self, name):
176        self.name = name
177
178
179class ConfigContentError(errors.BzrError):
180
181    _fmt = "Config file %(filename)s is not UTF-8 encoded\n"
182
183    def __init__(self, filename):
184        self.filename = filename
185
186
187class ParseConfigError(errors.BzrError):
188
189    _fmt = "Error(s) parsing config file %(filename)s:\n%(errors)s"
190
191    def __init__(self, errors, filename):
192        self.filename = filename
193        self.errors = '\n'.join(e.msg for e in errors)
194
195
196class ConfigOptionValueError(errors.BzrError):
197
198    _fmt = ('Bad value "%(value)s" for option "%(name)s".\n'
199            'See ``brz help %(name)s``')
200
201    def __init__(self, name, value):
202        errors.BzrError.__init__(self, name=name, value=value)
203
204
205class NoEmailInUsername(errors.BzrError):
206
207    _fmt = "%(username)r does not seem to contain a reasonable email address"
208
209    def __init__(self, username):
210        self.username = username
211
212
213class NoSuchConfig(errors.BzrError):
214
215    _fmt = ('The "%(config_id)s" configuration does not exist.')
216
217    def __init__(self, config_id):
218        errors.BzrError.__init__(self, config_id=config_id)
219
220
221class NoSuchConfigOption(errors.BzrError):
222
223    _fmt = ('The "%(option_name)s" configuration option does not exist.')
224
225    def __init__(self, option_name):
226        errors.BzrError.__init__(self, option_name=option_name)
227
228
229def signature_policy_from_unicode(signature_string):
230    """Convert a string to a signing policy."""
231    if signature_string.lower() == 'check-available':
232        return CHECK_IF_POSSIBLE
233    if signature_string.lower() == 'ignore':
234        return CHECK_NEVER
235    if signature_string.lower() == 'require':
236        return CHECK_ALWAYS
237    raise ValueError("Invalid signatures policy '%s'"
238                     % signature_string)
239
240
241def signing_policy_from_unicode(signature_string):
242    """Convert a string to a signing policy."""
243    if signature_string.lower() == 'when-required':
244        return SIGN_WHEN_REQUIRED
245    if signature_string.lower() == 'never':
246        return SIGN_NEVER
247    if signature_string.lower() == 'always':
248        return SIGN_ALWAYS
249    raise ValueError("Invalid signing policy '%s'"
250                     % signature_string)
251
252
253def _has_triplequote_bug():
254    """True if triple quote logic is reversed, see lp:710410."""
255    conf = configobj.ConfigObj()
256    quote = getattr(conf, "_get_triple_quote", None)
257    if quote and quote('"""') != "'''":
258        return True
259    return False
260
261
262class ConfigObj(configobj.ConfigObj):
263
264    def __init__(self, infile=None, **kwargs):
265        # We define our own interpolation mechanism calling it option expansion
266        super(ConfigObj, self).__init__(infile=infile,
267                                        interpolation=False,
268                                        **kwargs)
269
270    if _has_triplequote_bug():
271        def _get_triple_quote(self, value):
272            quot = super(ConfigObj, self)._get_triple_quote(value)
273            if quot == configobj.tdquot:
274                return configobj.tsquot
275            return configobj.tdquot
276
277    def get_bool(self, section, key):
278        return self[section].as_bool(key)
279
280    def get_value(self, section, name):
281        # Try [] for the old DEFAULT section.
282        if section == "DEFAULT":
283            try:
284                return self[name]
285            except KeyError:
286                pass
287        return self[section][name]
288
289
290class Config(object):
291    """A configuration policy - what username, editor, gpg needs etc."""
292
293    def __init__(self):
294        super(Config, self).__init__()
295
296    def config_id(self):
297        """Returns a unique ID for the config."""
298        raise NotImplementedError(self.config_id)
299
300    def get_change_editor(self, old_tree, new_tree):
301        from breezy import diff
302        cmd = self._get_change_editor()
303        if cmd is None:
304            return None
305        cmd = cmd.replace('@old_path', '{old_path}')
306        cmd = cmd.replace('@new_path', '{new_path}')
307        cmd = cmdline.split(cmd)
308        if '{old_path}' not in cmd:
309            cmd.extend(['{old_path}', '{new_path}'])
310        return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
311                                             sys.stdout)
312
313    def _get_signature_checking(self):
314        """Template method to override signature checking policy."""
315
316    def _get_signing_policy(self):
317        """Template method to override signature creation policy."""
318
319    option_ref_re = None
320
321    def expand_options(self, string, env=None):
322        """Expand option references in the string in the configuration context.
323
324        :param string: The string containing option to expand.
325
326        :param env: An option dict defining additional configuration options or
327            overriding existing ones.
328
329        :returns: The expanded string.
330        """
331        return self._expand_options_in_string(string, env)
332
333    def _expand_options_in_list(self, slist, env=None, _ref_stack=None):
334        """Expand options in  a list of strings in the configuration context.
335
336        :param slist: A list of strings.
337
338        :param env: An option dict defining additional configuration options or
339            overriding existing ones.
340
341        :param _ref_stack: Private list containing the options being
342            expanded to detect loops.
343
344        :returns: The flatten list of expanded strings.
345        """
346        # expand options in each value separately flattening lists
347        result = []
348        for s in slist:
349            value = self._expand_options_in_string(s, env, _ref_stack)
350            if isinstance(value, list):
351                result.extend(value)
352            else:
353                result.append(value)
354        return result
355
356    def _expand_options_in_string(self, string, env=None, _ref_stack=None):
357        """Expand options in the string in the configuration context.
358
359        :param string: The string to be expanded.
360
361        :param env: An option dict defining additional configuration options or
362            overriding existing ones.
363
364        :param _ref_stack: Private list containing the options being
365            expanded to detect loops.
366
367        :returns: The expanded string.
368        """
369        if string is None:
370            # Not much to expand there
371            return None
372        if _ref_stack is None:
373            # What references are currently resolved (to detect loops)
374            _ref_stack = []
375        if self.option_ref_re is None:
376            # We want to match the most embedded reference first (i.e. for
377            # '{{foo}}' we will get '{foo}',
378            # for '{bar{baz}}' we will get '{baz}'
379            self.option_ref_re = re.compile('({[^{}]+})')
380        result = string
381        # We need to iterate until no more refs appear ({{foo}} will need two
382        # iterations for example).
383        while True:
384            raw_chunks = self.option_ref_re.split(result)
385            if len(raw_chunks) == 1:
386                # Shorcut the trivial case: no refs
387                return result
388            chunks = []
389            list_value = False
390            # Split will isolate refs so that every other chunk is a ref
391            chunk_is_ref = False
392            for chunk in raw_chunks:
393                if not chunk_is_ref:
394                    if chunk:
395                        # Keep only non-empty strings (or we get bogus empty
396                        # slots when a list value is involved).
397                        chunks.append(chunk)
398                    chunk_is_ref = True
399                else:
400                    name = chunk[1:-1]
401                    if name in _ref_stack:
402                        raise OptionExpansionLoop(string, _ref_stack)
403                    _ref_stack.append(name)
404                    value = self._expand_option(name, env, _ref_stack)
405                    if value is None:
406                        raise ExpandingUnknownOption(name, string)
407                    if isinstance(value, list):
408                        list_value = True
409                        chunks.extend(value)
410                    else:
411                        chunks.append(value)
412                    _ref_stack.pop()
413                    chunk_is_ref = False
414            if list_value:
415                # Once a list appears as the result of an expansion, all
416                # callers will get a list result. This allows a consistent
417                # behavior even when some options in the expansion chain
418                # defined as strings (no comma in their value) but their
419                # expanded value is a list.
420                return self._expand_options_in_list(chunks, env, _ref_stack)
421            else:
422                result = ''.join(chunks)
423        return result
424
425    def _expand_option(self, name, env, _ref_stack):
426        if env is not None and name in env:
427            # Special case, values provided in env takes precedence over
428            # anything else
429            value = env[name]
430        else:
431            # FIXME: This is a limited implementation, what we really need is a
432            # way to query the brz config for the value of an option,
433            # respecting the scope rules (That is, once we implement fallback
434            # configs, getting the option value should restart from the top
435            # config, not the current one) -- vila 20101222
436            value = self.get_user_option(name, expand=False)
437            if isinstance(value, list):
438                value = self._expand_options_in_list(value, env, _ref_stack)
439            else:
440                value = self._expand_options_in_string(value, env, _ref_stack)
441        return value
442
443    def _get_user_option(self, option_name):
444        """Template method to provide a user option."""
445        return None
446
447    def get_user_option(self, option_name, expand=True):
448        """Get a generic option - no special process, no default.
449
450        :param option_name: The queried option.
451
452        :param expand: Whether options references should be expanded.
453
454        :returns: The value of the option.
455        """
456        value = self._get_user_option(option_name)
457        if expand:
458            if isinstance(value, list):
459                value = self._expand_options_in_list(value)
460            elif isinstance(value, dict):
461                trace.warning('Cannot expand "%s":'
462                              ' Dicts do not support option expansion'
463                              % (option_name,))
464            else:
465                value = self._expand_options_in_string(value)
466        for hook in OldConfigHooks['get']:
467            hook(self, option_name, value)
468        return value
469
470    def get_user_option_as_bool(self, option_name, expand=None, default=None):
471        """Get a generic option as a boolean.
472
473        :param expand: Allow expanding references to other config values.
474        :param default: Default value if nothing is configured
475        :return None if the option doesn't exist or its value can't be
476            interpreted as a boolean. Returns True or False otherwise.
477        """
478        s = self.get_user_option(option_name, expand=expand)
479        if s is None:
480            # The option doesn't exist
481            return default
482        val = ui.bool_from_string(s)
483        if val is None:
484            # The value can't be interpreted as a boolean
485            trace.warning('Value "%s" is not a boolean for "%s"',
486                          s, option_name)
487        return val
488
489    def get_user_option_as_list(self, option_name, expand=None):
490        """Get a generic option as a list - no special process, no default.
491
492        :return None if the option doesn't exist. Returns the value as a list
493            otherwise.
494        """
495        l = self.get_user_option(option_name, expand=expand)
496        if isinstance(l, str):
497            # A single value, most probably the user forgot (or didn't care to
498            # add) the final ','
499            l = [l]
500        return l
501
502    def _log_format(self):
503        """See log_format()."""
504        return None
505
506    def validate_signatures_in_log(self):
507        """Show GPG signature validity in log"""
508        result = self._validate_signatures_in_log()
509        if result == "true":
510            result = True
511        else:
512            result = False
513        return result
514
515    def _validate_signatures_in_log(self):
516        """See validate_signatures_in_log()."""
517        return None
518
519    def _post_commit(self):
520        """See Config.post_commit."""
521        return None
522
523    def user_email(self):
524        """Return just the email component of a username."""
525        return extract_email_address(self.username())
526
527    def username(self):
528        """Return email-style username.
529
530        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
531
532        $BRZ_EMAIL or $BZR_EMAIL can be set to override this, then
533        the concrete policy type is checked, and finally
534        $EMAIL is examined.
535        If no username can be found, NoWhoami exception is raised.
536        """
537        v = os.environ.get('BRZ_EMAIL') or os.environ.get('BZR_EMAIL')
538        if v:
539            return v
540        v = self._get_user_id()
541        if v:
542            return v
543        return bedding.default_email()
544
545    def get_alias(self, value):
546        return self._get_alias(value)
547
548    def _get_alias(self, value):
549        pass
550
551    def get_nickname(self):
552        return self._get_nickname()
553
554    def _get_nickname(self):
555        return None
556
557    def get_bzr_remote_path(self):
558        try:
559            return os.environ['BZR_REMOTE_PATH']
560        except KeyError:
561            path = self.get_user_option("bzr_remote_path")
562            if path is None:
563                path = 'bzr'
564            return path
565
566    def suppress_warning(self, warning):
567        """Should the warning be suppressed or emitted.
568
569        :param warning: The name of the warning being tested.
570
571        :returns: True if the warning should be suppressed, False otherwise.
572        """
573        warnings = self.get_user_option_as_list('suppress_warnings')
574        if warnings is None or warning not in warnings:
575            return False
576        else:
577            return True
578
579    def get_merge_tools(self):
580        tools = {}
581        for (oname, value, section, conf_id, parser) in self._get_options():
582            if oname.startswith('bzr.mergetool.'):
583                tool_name = oname[len('bzr.mergetool.'):]
584                tools[tool_name] = self.get_user_option(oname, False)
585        trace.mutter('loaded merge tools: %r' % tools)
586        return tools
587
588    def find_merge_tool(self, name):
589        # We fake a defaults mechanism here by checking if the given name can
590        # be found in the known_merge_tools if it's not found in the config.
591        # This should be done through the proposed config defaults mechanism
592        # when it becomes available in the future.
593        command_line = (self.get_user_option('bzr.mergetool.%s' % name,
594                                             expand=False) or
595                        mergetools.known_merge_tools.get(name, None))
596        return command_line
597
598
599class _ConfigHooks(hooks.Hooks):
600    """A dict mapping hook names and a list of callables for configs.
601    """
602
603    def __init__(self):
604        """Create the default hooks.
605
606        These are all empty initially, because by default nothing should get
607        notified.
608        """
609        super(_ConfigHooks, self).__init__('breezy.config', 'ConfigHooks')
610        self.add_hook('load',
611                      'Invoked when a config store is loaded.'
612                      ' The signature is (store).',
613                      (2, 4))
614        self.add_hook('save',
615                      'Invoked when a config store is saved.'
616                      ' The signature is (store).',
617                      (2, 4))
618        # The hooks for config options
619        self.add_hook('get',
620                      'Invoked when a config option is read.'
621                      ' The signature is (stack, name, value).',
622                      (2, 4))
623        self.add_hook('set',
624                      'Invoked when a config option is set.'
625                      ' The signature is (stack, name, value).',
626                      (2, 4))
627        self.add_hook('remove',
628                      'Invoked when a config option is removed.'
629                      ' The signature is (stack, name).',
630                      (2, 4))
631
632
633ConfigHooks = _ConfigHooks()
634
635
636class _OldConfigHooks(hooks.Hooks):
637    """A dict mapping hook names and a list of callables for configs.
638    """
639
640    def __init__(self):
641        """Create the default hooks.
642
643        These are all empty initially, because by default nothing should get
644        notified.
645        """
646        super(_OldConfigHooks, self).__init__(
647            'breezy.config', 'OldConfigHooks')
648        self.add_hook('load',
649                      'Invoked when a config store is loaded.'
650                      ' The signature is (config).',
651                      (2, 4))
652        self.add_hook('save',
653                      'Invoked when a config store is saved.'
654                      ' The signature is (config).',
655                      (2, 4))
656        # The hooks for config options
657        self.add_hook('get',
658                      'Invoked when a config option is read.'
659                      ' The signature is (config, name, value).',
660                      (2, 4))
661        self.add_hook('set',
662                      'Invoked when a config option is set.'
663                      ' The signature is (config, name, value).',
664                      (2, 4))
665        self.add_hook('remove',
666                      'Invoked when a config option is removed.'
667                      ' The signature is (config, name).',
668                      (2, 4))
669
670
671OldConfigHooks = _OldConfigHooks()
672
673
674class IniBasedConfig(Config):
675    """A configuration policy that draws from ini files."""
676
677    def __init__(self, file_name=None):
678        """Base class for configuration files using an ini-like syntax.
679
680        :param file_name: The configuration file path.
681        """
682        super(IniBasedConfig, self).__init__()
683        self.file_name = file_name
684        self.file_name = file_name
685        self._content = None
686        self._parser = None
687
688    @classmethod
689    def from_string(cls, str_or_unicode, file_name=None, save=False):
690        """Create a config object from a string.
691
692        :param str_or_unicode: A string representing the file content. This
693            will be utf-8 encoded.
694
695        :param file_name: The configuration file path.
696
697        :param _save: Whether the file should be saved upon creation.
698        """
699        conf = cls(file_name=file_name)
700        conf._create_from_string(str_or_unicode, save)
701        return conf
702
703    def _create_from_string(self, str_or_unicode, save):
704        if isinstance(str_or_unicode, str):
705            str_or_unicode = str_or_unicode.encode('utf-8')
706        self._content = BytesIO(str_or_unicode)
707        # Some tests use in-memory configs, some other always need the config
708        # file to exist on disk.
709        if save:
710            self._write_config_file()
711
712    def _get_parser(self):
713        if self._parser is not None:
714            return self._parser
715        if self._content is not None:
716            co_input = self._content
717        elif self.file_name is None:
718            raise AssertionError('We have no content to create the config')
719        else:
720            co_input = self.file_name
721        try:
722            self._parser = ConfigObj(co_input, encoding='utf-8')
723        except configobj.ConfigObjError as e:
724            raise ParseConfigError(e.errors, e.config.filename)
725        except UnicodeDecodeError:
726            raise ConfigContentError(self.file_name)
727        # Make sure self.reload() will use the right file name
728        self._parser.filename = self.file_name
729        for hook in OldConfigHooks['load']:
730            hook(self)
731        return self._parser
732
733    def reload(self):
734        """Reload the config file from disk."""
735        if self.file_name is None:
736            raise AssertionError('We need a file name to reload the config')
737        if self._parser is not None:
738            self._parser.reload()
739        for hook in ConfigHooks['load']:
740            hook(self)
741
742    def _get_matching_sections(self):
743        """Return an ordered list of (section_name, extra_path) pairs.
744
745        If the section contains inherited configuration, extra_path is
746        a string containing the additional path components.
747        """
748        section = self._get_section()
749        if section is not None:
750            return [(section, '')]
751        else:
752            return []
753
754    def _get_section(self):
755        """Override this to define the section used by the config."""
756        return "DEFAULT"
757
758    def _get_sections(self, name=None):
759        """Returns an iterator of the sections specified by ``name``.
760
761        :param name: The section name. If None is supplied, the default
762            configurations are yielded.
763
764        :return: A tuple (name, section, config_id) for all sections that will
765            be walked by user_get_option() in the 'right' order. The first one
766            is where set_user_option() will update the value.
767        """
768        parser = self._get_parser()
769        if name is not None:
770            yield (name, parser[name], self.config_id())
771        else:
772            # No section name has been given so we fallback to the configobj
773            # itself which holds the variables defined outside of any section.
774            yield (None, parser, self.config_id())
775
776    def _get_options(self, sections=None):
777        """Return an ordered list of (name, value, section, config_id) tuples.
778
779        All options are returned with their associated value and the section
780        they appeared in. ``config_id`` is a unique identifier for the
781        configuration file the option is defined in.
782
783        :param sections: Default to ``_get_matching_sections`` if not
784            specified. This gives a better control to daughter classes about
785            which sections should be searched. This is a list of (name,
786            configobj) tuples.
787        """
788        if sections is None:
789            parser = self._get_parser()
790            sections = []
791            for (section_name, _) in self._get_matching_sections():
792                try:
793                    section = parser[section_name]
794                except KeyError:
795                    # This could happen for an empty file for which we define a
796                    # DEFAULT section. FIXME: Force callers to provide sections
797                    # instead ? -- vila 20100930
798                    continue
799                sections.append((section_name, section))
800        config_id = self.config_id()
801        for (section_name, section) in sections:
802            for (name, value) in section.iteritems():
803                yield (name, parser._quote(value), section_name,
804                       config_id, parser)
805
806    def _get_option_policy(self, section, option_name):
807        """Return the policy for the given (section, option_name) pair."""
808        return POLICY_NONE
809
810    def _get_change_editor(self):
811        return self.get_user_option('change_editor', expand=False)
812
813    def _get_signature_checking(self):
814        """See Config._get_signature_checking."""
815        policy = self._get_user_option('check_signatures')
816        if policy:
817            return signature_policy_from_unicode(policy)
818
819    def _get_signing_policy(self):
820        """See Config._get_signing_policy"""
821        policy = self._get_user_option('create_signatures')
822        if policy:
823            return signing_policy_from_unicode(policy)
824
825    def _get_user_id(self):
826        """Get the user id from the 'email' key in the current section."""
827        return self._get_user_option('email')
828
829    def _get_user_option(self, option_name):
830        """See Config._get_user_option."""
831        for (section, extra_path) in self._get_matching_sections():
832            try:
833                value = self._get_parser().get_value(section, option_name)
834            except KeyError:
835                continue
836            policy = self._get_option_policy(section, option_name)
837            if policy == POLICY_NONE:
838                return value
839            elif policy == POLICY_NORECURSE:
840                # norecurse items only apply to the exact path
841                if extra_path:
842                    continue
843                else:
844                    return value
845            elif policy == POLICY_APPENDPATH:
846                if extra_path:
847                    value = urlutils.join(value, extra_path)
848                return value
849            else:
850                raise AssertionError('Unexpected config policy %r' % policy)
851        else:
852            return None
853
854    def _log_format(self):
855        """See Config.log_format."""
856        return self._get_user_option('log_format')
857
858    def _validate_signatures_in_log(self):
859        """See Config.validate_signatures_in_log."""
860        return self._get_user_option('validate_signatures_in_log')
861
862    def _acceptable_keys(self):
863        """See Config.acceptable_keys."""
864        return self._get_user_option('acceptable_keys')
865
866    def _post_commit(self):
867        """See Config.post_commit."""
868        return self._get_user_option('post_commit')
869
870    def _get_alias(self, value):
871        try:
872            return self._get_parser().get_value("ALIASES",
873                                                value)
874        except KeyError:
875            pass
876
877    def _get_nickname(self):
878        return self.get_user_option('nickname')
879
880    def remove_user_option(self, option_name, section_name=None):
881        """Remove a user option and save the configuration file.
882
883        :param option_name: The option to be removed.
884
885        :param section_name: The section the option is defined in, default to
886            the default section.
887        """
888        self.reload()
889        parser = self._get_parser()
890        if section_name is None:
891            section = parser
892        else:
893            section = parser[section_name]
894        try:
895            del section[option_name]
896        except KeyError:
897            raise NoSuchConfigOption(option_name)
898        self._write_config_file()
899        for hook in OldConfigHooks['remove']:
900            hook(self, option_name)
901
902    def _write_config_file(self):
903        if self.file_name is None:
904            raise AssertionError('We cannot save, self.file_name is None')
905        from . import atomicfile
906        conf_dir = os.path.dirname(self.file_name)
907        bedding.ensure_config_dir_exists(conf_dir)
908        with atomicfile.AtomicFile(self.file_name) as atomic_file:
909            self._get_parser().write(atomic_file)
910        osutils.copy_ownership_from_path(self.file_name)
911        for hook in OldConfigHooks['save']:
912            hook(self)
913
914
915class LockableConfig(IniBasedConfig):
916    """A configuration needing explicit locking for access.
917
918    If several processes try to write the config file, the accesses need to be
919    serialized.
920
921    Daughter classes should use the self.lock_write() decorator method when
922    they upate a config (they call, directly or indirectly, the
923    ``_write_config_file()`` method. These methods (typically ``set_option()``
924    and variants must reload the config file from disk before calling
925    ``_write_config_file()``), this can be achieved by calling the
926    ``self.reload()`` method. Note that the lock scope should cover both the
927    reading and the writing of the config file which is why the decorator can't
928    be applied to ``_write_config_file()`` only.
929
930    This should be enough to implement the following logic:
931    - lock for exclusive write access,
932    - reload the config file from disk,
933    - set the new value
934    - unlock
935
936    This logic guarantees that a writer can update a value without erasing an
937    update made by another writer.
938    """
939
940    lock_name = 'lock'
941
942    def __init__(self, file_name):
943        super(LockableConfig, self).__init__(file_name=file_name)
944        self.dir = osutils.dirname(osutils.safe_unicode(self.file_name))
945        # FIXME: It doesn't matter that we don't provide possible_transports
946        # below since this is currently used only for local config files ;
947        # local transports are not shared. But if/when we start using
948        # LockableConfig for other kind of transports, we will need to reuse
949        # whatever connection is already established -- vila 20100929
950        self.transport = transport.get_transport_from_path(self.dir)
951        self._lock = lockdir.LockDir(self.transport, self.lock_name)
952
953    def _create_from_string(self, unicode_bytes, save):
954        super(LockableConfig, self)._create_from_string(unicode_bytes, False)
955        if save:
956            # We need to handle the saving here (as opposed to IniBasedConfig)
957            # to be able to lock
958            self.lock_write()
959            self._write_config_file()
960            self.unlock()
961
962    def lock_write(self, token=None):
963        """Takes a write lock in the directory containing the config file.
964
965        If the directory doesn't exist it is created.
966        """
967        bedding.ensure_config_dir_exists(self.dir)
968        token = self._lock.lock_write(token)
969        return lock.LogicalLockResult(self.unlock, token)
970
971    def unlock(self):
972        self._lock.unlock()
973
974    def break_lock(self):
975        self._lock.break_lock()
976
977    def remove_user_option(self, option_name, section_name=None):
978        with self.lock_write():
979            super(LockableConfig, self).remove_user_option(
980                option_name, section_name)
981
982    def _write_config_file(self):
983        if self._lock is None or not self._lock.is_held:
984            # NB: if the following exception is raised it probably means a
985            # missing call to lock_write() by one of the callers.
986            raise errors.ObjectNotLocked(self)
987        super(LockableConfig, self)._write_config_file()
988
989
990class GlobalConfig(LockableConfig):
991    """The configuration that should be used for a specific location."""
992
993    def __init__(self):
994        super(GlobalConfig, self).__init__(file_name=bedding.config_path())
995
996    def config_id(self):
997        return 'breezy'
998
999    @classmethod
1000    def from_string(cls, str_or_unicode, save=False):
1001        """Create a config object from a string.
1002
1003        :param str_or_unicode: A string representing the file content. This
1004            will be utf-8 encoded.
1005
1006        :param save: Whether the file should be saved upon creation.
1007        """
1008        conf = cls()
1009        conf._create_from_string(str_or_unicode, save)
1010        return conf
1011
1012    def set_user_option(self, option, value):
1013        """Save option and its value in the configuration."""
1014        with self.lock_write():
1015            self._set_option(option, value, 'DEFAULT')
1016
1017    def get_aliases(self):
1018        """Return the aliases section."""
1019        if 'ALIASES' in self._get_parser():
1020            return self._get_parser()['ALIASES']
1021        else:
1022            return {}
1023
1024    def set_alias(self, alias_name, alias_command):
1025        """Save the alias in the configuration."""
1026        with self.lock_write():
1027            self._set_option(alias_name, alias_command, 'ALIASES')
1028
1029    def unset_alias(self, alias_name):
1030        """Unset an existing alias."""
1031        with self.lock_write():
1032            self.reload()
1033            aliases = self._get_parser().get('ALIASES')
1034            if not aliases or alias_name not in aliases:
1035                raise errors.NoSuchAlias(alias_name)
1036            del aliases[alias_name]
1037            self._write_config_file()
1038
1039    def _set_option(self, option, value, section):
1040        self.reload()
1041        self._get_parser().setdefault(section, {})[option] = value
1042        self._write_config_file()
1043        for hook in OldConfigHooks['set']:
1044            hook(self, option, value)
1045
1046    def _get_sections(self, name=None):
1047        """See IniBasedConfig._get_sections()."""
1048        parser = self._get_parser()
1049        # We don't give access to options defined outside of any section, we
1050        # used the DEFAULT section by... default.
1051        if name in (None, 'DEFAULT'):
1052            # This could happen for an empty file where the DEFAULT section
1053            # doesn't exist yet. So we force DEFAULT when yielding
1054            name = 'DEFAULT'
1055            if 'DEFAULT' not in parser:
1056                parser['DEFAULT'] = {}
1057        yield (name, parser[name], self.config_id())
1058
1059    def remove_user_option(self, option_name, section_name=None):
1060        if section_name is None:
1061            # We need to force the default section.
1062            section_name = 'DEFAULT'
1063        with self.lock_write():
1064            # We need to avoid the LockableConfig implementation or we'll lock
1065            # twice
1066            super(LockableConfig, self).remove_user_option(
1067                option_name, section_name)
1068
1069
1070def _iter_for_location_by_parts(sections, location):
1071    """Keep only the sessions matching the specified location.
1072
1073    :param sections: An iterable of section names.
1074
1075    :param location: An url or a local path to match against.
1076
1077    :returns: An iterator of (section, extra_path, nb_parts) where nb is the
1078        number of path components in the section name, section is the section
1079        name and extra_path is the difference between location and the section
1080        name.
1081
1082    ``location`` will always be a local path and never a 'file://' url but the
1083    section names themselves can be in either form.
1084    """
1085    location_parts = location.rstrip('/').split('/')
1086
1087    for section in sections:
1088        # location is a local path if possible, so we need to convert 'file://'
1089        # urls in section names to local paths if necessary.
1090
1091        # This also avoids having file:///path be a more exact
1092        # match than '/path'.
1093
1094        # FIXME: This still raises an issue if a user defines both file:///path
1095        # *and* /path. Should we raise an error in this case -- vila 20110505
1096
1097        if section.startswith('file://'):
1098            section_path = urlutils.local_path_from_url(section)
1099        else:
1100            section_path = section
1101        section_parts = section_path.rstrip('/').split('/')
1102
1103        matched = True
1104        if len(section_parts) > len(location_parts):
1105            # More path components in the section, they can't match
1106            matched = False
1107        else:
1108            # Rely on zip truncating in length to the length of the shortest
1109            # argument sequence.
1110            for name in zip(location_parts, section_parts):
1111                if not fnmatch.fnmatch(name[0], name[1]):
1112                    matched = False
1113                    break
1114        if not matched:
1115            continue
1116        # build the path difference between the section and the location
1117        extra_path = '/'.join(location_parts[len(section_parts):])
1118        yield section, extra_path, len(section_parts)
1119
1120
1121class LocationConfig(LockableConfig):
1122    """A configuration object that gives the policy for a location."""
1123
1124    def __init__(self, location):
1125        super(LocationConfig, self).__init__(
1126            file_name=bedding.locations_config_path())
1127        # local file locations are looked up by local path, rather than
1128        # by file url. This is because the config file is a user
1129        # file, and we would rather not expose the user to file urls.
1130        if location.startswith('file://'):
1131            location = urlutils.local_path_from_url(location)
1132        self.location = location
1133
1134    def config_id(self):
1135        return 'locations'
1136
1137    @classmethod
1138    def from_string(cls, str_or_unicode, location, save=False):
1139        """Create a config object from a string.
1140
1141        :param str_or_unicode: A string representing the file content. This will
1142            be utf-8 encoded.
1143
1144        :param location: The location url to filter the configuration.
1145
1146        :param save: Whether the file should be saved upon creation.
1147        """
1148        conf = cls(location)
1149        conf._create_from_string(str_or_unicode, save)
1150        return conf
1151
1152    def _get_matching_sections(self):
1153        """Return an ordered list of section names matching this location."""
1154        # put the longest (aka more specific) locations first
1155        matches = sorted(
1156            _iter_for_location_by_parts(self._get_parser(), self.location),
1157            key=lambda match: (match[2], match[0]),
1158            reverse=True)
1159        for (section, extra_path, length) in matches:
1160            yield section, extra_path
1161            # should we stop looking for parent configs here?
1162            try:
1163                if self._get_parser()[section].as_bool('ignore_parents'):
1164                    break
1165            except KeyError:
1166                pass
1167
1168    def _get_sections(self, name=None):
1169        """See IniBasedConfig._get_sections()."""
1170        # We ignore the name here as the only sections handled are named with
1171        # the location path and we don't expose embedded sections either.
1172        parser = self._get_parser()
1173        for name, extra_path in self._get_matching_sections():
1174            yield (name, parser[name], self.config_id())
1175
1176    def _get_option_policy(self, section, option_name):
1177        """Return the policy for the given (section, option_name) pair."""
1178        # check for the old 'recurse=False' flag
1179        try:
1180            recurse = self._get_parser()[section].as_bool('recurse')
1181        except KeyError:
1182            recurse = True
1183        if not recurse:
1184            return POLICY_NORECURSE
1185
1186        policy_key = option_name + ':policy'
1187        try:
1188            policy_name = self._get_parser()[section][policy_key]
1189        except KeyError:
1190            policy_name = None
1191
1192        return _policy_value[policy_name]
1193
1194    def _set_option_policy(self, section, option_name, option_policy):
1195        """Set the policy for the given option name in the given section."""
1196        policy_key = option_name + ':policy'
1197        policy_name = _policy_name[option_policy]
1198        if policy_name is not None:
1199            self._get_parser()[section][policy_key] = policy_name
1200        else:
1201            if policy_key in self._get_parser()[section]:
1202                del self._get_parser()[section][policy_key]
1203
1204    def set_user_option(self, option, value, store=STORE_LOCATION):
1205        """Save option and its value in the configuration."""
1206        if store not in [STORE_LOCATION,
1207                         STORE_LOCATION_NORECURSE,
1208                         STORE_LOCATION_APPENDPATH]:
1209            raise ValueError('bad storage policy %r for %r' %
1210                             (store, option))
1211        with self.lock_write():
1212            self.reload()
1213            location = self.location
1214            if location.endswith('/'):
1215                location = location[:-1]
1216            parser = self._get_parser()
1217            if location not in parser and not location + '/' in parser:
1218                parser[location] = {}
1219            elif location + '/' in parser:
1220                location = location + '/'
1221            parser[location][option] = value
1222            # the allowed values of store match the config policies
1223            self._set_option_policy(location, option, store)
1224            self._write_config_file()
1225            for hook in OldConfigHooks['set']:
1226                hook(self, option, value)
1227
1228
1229class BranchConfig(Config):
1230    """A configuration object giving the policy for a branch."""
1231
1232    def __init__(self, branch):
1233        super(BranchConfig, self).__init__()
1234        self._location_config = None
1235        self._branch_data_config = None
1236        self._global_config = None
1237        self.branch = branch
1238        self.option_sources = (self._get_location_config,
1239                               self._get_branch_data_config,
1240                               self._get_global_config)
1241
1242    def config_id(self):
1243        return 'branch'
1244
1245    def _get_branch_data_config(self):
1246        if self._branch_data_config is None:
1247            self._branch_data_config = TreeConfig(self.branch)
1248            self._branch_data_config.config_id = self.config_id
1249        return self._branch_data_config
1250
1251    def _get_location_config(self):
1252        if self._location_config is None:
1253            if self.branch.base is None:
1254                self.branch.base = 'memory://'
1255            self._location_config = LocationConfig(self.branch.base)
1256        return self._location_config
1257
1258    def _get_global_config(self):
1259        if self._global_config is None:
1260            self._global_config = GlobalConfig()
1261        return self._global_config
1262
1263    def _get_best_value(self, option_name):
1264        """This returns a user option from local, tree or global config.
1265
1266        They are tried in that order.  Use get_safe_value if trusted values
1267        are necessary.
1268        """
1269        for source in self.option_sources:
1270            value = getattr(source(), option_name)()
1271            if value is not None:
1272                return value
1273        return None
1274
1275    def _get_safe_value(self, option_name):
1276        """This variant of get_best_value never returns untrusted values.
1277
1278        It does not return values from the branch data, because the branch may
1279        not be controlled by the user.
1280
1281        We may wish to allow locations.conf to control whether branches are
1282        trusted in the future.
1283        """
1284        for source in (self._get_location_config, self._get_global_config):
1285            value = getattr(source(), option_name)()
1286            if value is not None:
1287                return value
1288        return None
1289
1290    def _get_user_id(self):
1291        """Return the full user id for the branch.
1292
1293        e.g. "John Hacker <jhacker@example.com>"
1294        This is looked up in the email controlfile for the branch.
1295        """
1296        return self._get_best_value('_get_user_id')
1297
1298    def _get_change_editor(self):
1299        return self._get_best_value('_get_change_editor')
1300
1301    def _get_signature_checking(self):
1302        """See Config._get_signature_checking."""
1303        return self._get_best_value('_get_signature_checking')
1304
1305    def _get_signing_policy(self):
1306        """See Config._get_signing_policy."""
1307        return self._get_best_value('_get_signing_policy')
1308
1309    def _get_user_option(self, option_name):
1310        """See Config._get_user_option."""
1311        for source in self.option_sources:
1312            value = source()._get_user_option(option_name)
1313            if value is not None:
1314                return value
1315        return None
1316
1317    def _get_sections(self, name=None):
1318        """See IniBasedConfig.get_sections()."""
1319        for source in self.option_sources:
1320            for section in source()._get_sections(name):
1321                yield section
1322
1323    def _get_options(self, sections=None):
1324        # First the locations options
1325        for option in self._get_location_config()._get_options():
1326            yield option
1327        # Then the branch options
1328        branch_config = self._get_branch_data_config()
1329        if sections is None:
1330            sections = [('DEFAULT', branch_config._get_parser())]
1331        # FIXME: We shouldn't have to duplicate the code in IniBasedConfig but
1332        # Config itself has no notion of sections :( -- vila 20101001
1333        config_id = self.config_id()
1334        for (section_name, section) in sections:
1335            for (name, value) in section.iteritems():
1336                yield (name, value, section_name,
1337                       config_id, branch_config._get_parser())
1338        # Then the global options
1339        for option in self._get_global_config()._get_options():
1340            yield option
1341
1342    def set_user_option(self, name, value, store=STORE_BRANCH,
1343                        warn_masked=False):
1344        if store == STORE_BRANCH:
1345            self._get_branch_data_config().set_option(value, name)
1346        elif store == STORE_GLOBAL:
1347            self._get_global_config().set_user_option(name, value)
1348        else:
1349            self._get_location_config().set_user_option(name, value, store)
1350        if not warn_masked:
1351            return
1352        if store in (STORE_GLOBAL, STORE_BRANCH):
1353            mask_value = self._get_location_config().get_user_option(name)
1354            if mask_value is not None:
1355                trace.warning('Value "%s" is masked by "%s" from'
1356                              ' locations.conf', value, mask_value)
1357            else:
1358                if store == STORE_GLOBAL:
1359                    branch_config = self._get_branch_data_config()
1360                    mask_value = branch_config.get_user_option(name)
1361                    if mask_value is not None:
1362                        trace.warning('Value "%s" is masked by "%s" from'
1363                                      ' branch.conf', value, mask_value)
1364
1365    def remove_user_option(self, option_name, section_name=None):
1366        self._get_branch_data_config().remove_option(option_name, section_name)
1367
1368    def _post_commit(self):
1369        """See Config.post_commit."""
1370        return self._get_safe_value('_post_commit')
1371
1372    def _get_nickname(self):
1373        value = self._get_explicit_nickname()
1374        if value is not None:
1375            return value
1376        if self.branch.name:
1377            return self.branch.name
1378        return urlutils.unescape(self.branch.base.split('/')[-2])
1379
1380    def has_explicit_nickname(self):
1381        """Return true if a nickname has been explicitly assigned."""
1382        return self._get_explicit_nickname() is not None
1383
1384    def _get_explicit_nickname(self):
1385        return self._get_best_value('_get_nickname')
1386
1387    def _log_format(self):
1388        """See Config.log_format."""
1389        return self._get_best_value('_log_format')
1390
1391    def _validate_signatures_in_log(self):
1392        """See Config.validate_signatures_in_log."""
1393        return self._get_best_value('_validate_signatures_in_log')
1394
1395    def _acceptable_keys(self):
1396        """See Config.acceptable_keys."""
1397        return self._get_best_value('_acceptable_keys')
1398
1399
1400def parse_username(username):
1401    """Parse e-mail username and return a (name, address) tuple."""
1402    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
1403    if match is None:
1404        return (username, '')
1405    return (match.group(1), match.group(2))
1406
1407
1408def extract_email_address(e):
1409    """Return just the address part of an email string.
1410
1411    That is just the user@domain part, nothing else.
1412    This part is required to contain only ascii characters.
1413    If it can't be extracted, raises an error.
1414
1415    >>> extract_email_address('Jane Tester <jane@test.com>')
1416    "jane@test.com"
1417    """
1418    name, email = parse_username(e)
1419    if not email:
1420        raise NoEmailInUsername(e)
1421    return email
1422
1423
1424class TreeConfig(IniBasedConfig):
1425    """Branch configuration data associated with its contents, not location"""
1426
1427    # XXX: Really needs a better name, as this is not part of the tree!
1428    # -- mbp 20080507
1429
1430    def __init__(self, branch):
1431        self._config = branch._get_config()
1432        self.branch = branch
1433
1434    def _get_parser(self, file=None):
1435        if file is not None:
1436            return IniBasedConfig._get_parser(file)
1437        return self._config._get_configobj()
1438
1439    def get_option(self, name, section=None, default=None):
1440        with self.branch.lock_read():
1441            return self._config.get_option(name, section, default)
1442
1443    def set_option(self, value, name, section=None):
1444        """Set a per-branch configuration option"""
1445        # FIXME: We shouldn't need to lock explicitly here but rather rely on
1446        # higher levels providing the right lock -- vila 20101004
1447        with self.branch.lock_write():
1448            self._config.set_option(value, name, section)
1449
1450    def remove_option(self, option_name, section_name=None):
1451        # FIXME: We shouldn't need to lock explicitly here but rather rely on
1452        # higher levels providing the right lock -- vila 20101004
1453        with self.branch.lock_write():
1454            self._config.remove_option(option_name, section_name)
1455
1456
1457_authentication_config_permission_errors = set()
1458
1459
1460class AuthenticationConfig(object):
1461    """The authentication configuration file based on a ini file.
1462
1463    Implements the authentication.conf file described in
1464    doc/developers/authentication-ring.txt.
1465    """
1466
1467    def __init__(self, _file=None):
1468        self._config = None  # The ConfigObj
1469        if _file is None:
1470            self._input = self._filename = bedding.authentication_config_path()
1471            self._check_permissions()
1472        else:
1473            # Tests can provide a string as _file
1474            self._filename = None
1475            self._input = _file
1476
1477    def _get_config(self):
1478        if self._config is not None:
1479            return self._config
1480        try:
1481            # FIXME: Should we validate something here ? Includes: empty
1482            # sections are useless, at least one of
1483            # user/password/password_encoding should be defined, etc.
1484
1485            # Note: the encoding below declares that the file itself is utf-8
1486            # encoded, but the values in the ConfigObj are always Unicode.
1487            self._config = ConfigObj(self._input, encoding='utf-8')
1488        except configobj.ConfigObjError as e:
1489            raise ParseConfigError(e.errors, e.config.filename)
1490        except UnicodeError:
1491            raise ConfigContentError(self._filename)
1492        return self._config
1493
1494    def _check_permissions(self):
1495        """Check permission of auth file are user read/write able only."""
1496        try:
1497            st = os.stat(self._filename)
1498        except OSError as e:
1499            if e.errno != errno.ENOENT:
1500                trace.mutter('Unable to stat %r: %r', self._filename, e)
1501            return
1502        mode = stat.S_IMODE(st.st_mode)
1503        if ((stat.S_IXOTH | stat.S_IWOTH | stat.S_IROTH | stat.S_IXGRP
1504             | stat.S_IWGRP | stat.S_IRGRP) & mode):
1505            # Only warn once
1506            if (self._filename not in _authentication_config_permission_errors and
1507                not GlobalConfig().suppress_warning(
1508                    'insecure_permissions')):
1509                trace.warning("The file '%s' has insecure "
1510                              "file permissions. Saved passwords may be accessible "
1511                              "by other users.", self._filename)
1512                _authentication_config_permission_errors.add(self._filename)
1513
1514    def _save(self):
1515        """Save the config file, only tests should use it for now."""
1516        conf_dir = os.path.dirname(self._filename)
1517        bedding.ensure_config_dir_exists(conf_dir)
1518        fd = os.open(self._filename, os.O_RDWR | os.O_CREAT, 0o600)
1519        try:
1520            f = os.fdopen(fd, 'wb')
1521            self._get_config().write(f)
1522        finally:
1523            f.close()
1524
1525    def _set_option(self, section_name, option_name, value):
1526        """Set an authentication configuration option"""
1527        conf = self._get_config()
1528        section = conf.get(section_name)
1529        if section is None:
1530            conf[section] = {}
1531            section = conf[section]
1532        section[option_name] = value
1533        self._save()
1534
1535    def get_credentials(self, scheme, host, port=None, user=None, path=None,
1536                        realm=None):
1537        """Returns the matching credentials from authentication.conf file.
1538
1539        :param scheme: protocol
1540
1541        :param host: the server address
1542
1543        :param port: the associated port (optional)
1544
1545        :param user: login (optional)
1546
1547        :param path: the absolute path on the server (optional)
1548
1549        :param realm: the http authentication realm (optional)
1550
1551        :return: A dict containing the matching credentials or None.
1552           This includes:
1553           - name: the section name of the credentials in the
1554             authentication.conf file,
1555           - user: can't be different from the provided user if any,
1556           - scheme: the server protocol,
1557           - host: the server address,
1558           - port: the server port (can be None),
1559           - path: the absolute server path (can be None),
1560           - realm: the http specific authentication realm (can be None),
1561           - password: the decoded password, could be None if the credential
1562             defines only the user
1563           - verify_certificates: https specific, True if the server
1564             certificate should be verified, False otherwise.
1565        """
1566        credentials = None
1567        for auth_def_name, auth_def in self._get_config().iteritems():
1568            if not isinstance(auth_def, configobj.Section):
1569                raise ValueError("%s defined outside a section" %
1570                                 auth_def_name)
1571
1572            a_scheme, a_host, a_user, a_path = map(
1573                auth_def.get, ['scheme', 'host', 'user', 'path'])
1574
1575            try:
1576                a_port = auth_def.as_int('port')
1577            except KeyError:
1578                a_port = None
1579            except ValueError:
1580                raise ValueError("'port' not numeric in %s" % auth_def_name)
1581            try:
1582                a_verify_certificates = auth_def.as_bool('verify_certificates')
1583            except KeyError:
1584                a_verify_certificates = True
1585            except ValueError:
1586                raise ValueError(
1587                    "'verify_certificates' not boolean in %s" % auth_def_name)
1588
1589            # Attempt matching
1590            if a_scheme is not None and scheme != a_scheme:
1591                continue
1592            if a_host is not None:
1593                if not (host == a_host or
1594                        (a_host.startswith('.') and host.endswith(a_host))):
1595                    continue
1596            if a_port is not None and port != a_port:
1597                continue
1598            if (a_path is not None and path is not None and
1599                    not path.startswith(a_path)):
1600                continue
1601            if (a_user is not None and user is not None and
1602                    a_user != user):
1603                # Never contradict the caller about the user to be used
1604                continue
1605            if a_user is None:
1606                # Can't find a user
1607                continue
1608            # Prepare a credentials dictionary with additional keys
1609            # for the credential providers
1610            credentials = dict(name=auth_def_name,
1611                               user=a_user,
1612                               scheme=a_scheme,
1613                               host=host,
1614                               port=port,
1615                               path=path,
1616                               realm=realm,
1617                               password=auth_def.get('password', None),
1618                               verify_certificates=a_verify_certificates)
1619            # Decode the password in the credentials (or get one)
1620            self.decode_password(credentials,
1621                                 auth_def.get('password_encoding', None))
1622            if 'auth' in debug.debug_flags:
1623                trace.mutter("Using authentication section: %r", auth_def_name)
1624            break
1625
1626        if credentials is None:
1627            # No credentials were found in authentication.conf, try the fallback
1628            # credentials stores.
1629            credentials = credential_store_registry.get_fallback_credentials(
1630                scheme, host, port, user, path, realm)
1631
1632        return credentials
1633
1634    def set_credentials(self, name, host, user, scheme=None, password=None,
1635                        port=None, path=None, verify_certificates=None,
1636                        realm=None):
1637        """Set authentication credentials for a host.
1638
1639        Any existing credentials with matching scheme, host, port and path
1640        will be deleted, regardless of name.
1641
1642        :param name: An arbitrary name to describe this set of credentials.
1643        :param host: Name of the host that accepts these credentials.
1644        :param user: The username portion of these credentials.
1645        :param scheme: The URL scheme (e.g. ssh, http) the credentials apply
1646            to.
1647        :param password: Password portion of these credentials.
1648        :param port: The IP port on the host that these credentials apply to.
1649        :param path: A filesystem path on the host that these credentials
1650            apply to.
1651        :param verify_certificates: On https, verify server certificates if
1652            True.
1653        :param realm: The http authentication realm (optional).
1654        """
1655        values = {'host': host, 'user': user}
1656        if password is not None:
1657            values['password'] = password
1658        if scheme is not None:
1659            values['scheme'] = scheme
1660        if port is not None:
1661            values['port'] = '%d' % port
1662        if path is not None:
1663            values['path'] = path
1664        if verify_certificates is not None:
1665            values['verify_certificates'] = str(verify_certificates)
1666        if realm is not None:
1667            values['realm'] = realm
1668        config = self._get_config()
1669        for section, existing_values in config.iteritems():
1670            for key in ('scheme', 'host', 'port', 'path', 'realm'):
1671                if existing_values.get(key) != values.get(key):
1672                    break
1673            else:
1674                del config[section]
1675        config.update({name: values})
1676        self._save()
1677
1678    def get_user(self, scheme, host, port=None, realm=None, path=None,
1679                 prompt=None, ask=False, default=None):
1680        """Get a user from authentication file.
1681
1682        :param scheme: protocol
1683
1684        :param host: the server address
1685
1686        :param port: the associated port (optional)
1687
1688        :param realm: the realm sent by the server (optional)
1689
1690        :param path: the absolute path on the server (optional)
1691
1692        :param ask: Ask the user if there is no explicitly configured username
1693                    (optional)
1694
1695        :param default: The username returned if none is defined (optional).
1696
1697        :return: The found user.
1698        """
1699        credentials = self.get_credentials(scheme, host, port, user=None,
1700                                           path=path, realm=realm)
1701        if credentials is not None:
1702            user = credentials['user']
1703        else:
1704            user = None
1705        if user is None:
1706            if ask:
1707                if prompt is None:
1708                    # Create a default prompt suitable for most cases
1709                    prompt = u'%s' % (scheme.upper(),) + u' %(host)s username'
1710                # Special handling for optional fields in the prompt
1711                if port is not None:
1712                    prompt_host = '%s:%d' % (host, port)
1713                else:
1714                    prompt_host = host
1715                user = ui.ui_factory.get_username(prompt, host=prompt_host)
1716            else:
1717                user = default
1718        return user
1719
1720    def get_password(self, scheme, host, user, port=None,
1721                     realm=None, path=None, prompt=None):
1722        """Get a password from authentication file or prompt the user for one.
1723
1724        :param scheme: protocol
1725
1726        :param host: the server address
1727
1728        :param port: the associated port (optional)
1729
1730        :param user: login
1731
1732        :param realm: the realm sent by the server (optional)
1733
1734        :param path: the absolute path on the server (optional)
1735
1736        :return: The found password or the one entered by the user.
1737        """
1738        credentials = self.get_credentials(scheme, host, port, user, path,
1739                                           realm)
1740        if credentials is not None:
1741            password = credentials['password']
1742            if password is not None and scheme == 'ssh':
1743                trace.warning('password ignored in section [%s],'
1744                              ' use an ssh agent instead'
1745                              % credentials['name'])
1746                password = None
1747        else:
1748            password = None
1749        # Prompt user only if we could't find a password
1750        if password is None:
1751            if prompt is None:
1752                # Create a default prompt suitable for most cases
1753                prompt = (u'%s' %
1754                          scheme.upper() + u' %(user)s@%(host)s password')
1755            # Special handling for optional fields in the prompt
1756            if port is not None:
1757                prompt_host = '%s:%d' % (host, port)
1758            else:
1759                prompt_host = host
1760            password = ui.ui_factory.get_password(prompt,
1761                                                  host=prompt_host, user=user)
1762        return password
1763
1764    def decode_password(self, credentials, encoding):
1765        try:
1766            cs = credential_store_registry.get_credential_store(encoding)
1767        except KeyError:
1768            raise ValueError('%r is not a known password_encoding' % encoding)
1769        credentials['password'] = cs.decode_password(credentials)
1770        return credentials
1771
1772
1773class CredentialStoreRegistry(registry.Registry):
1774    """A class that registers credential stores.
1775
1776    A credential store provides access to credentials via the password_encoding
1777    field in authentication.conf sections.
1778
1779    Except for stores provided by brz itself, most stores are expected to be
1780    provided by plugins that will therefore use
1781    register_lazy(password_encoding, module_name, member_name, help=help,
1782    fallback=fallback) to install themselves.
1783
1784    A fallback credential store is one that is queried if no credentials can be
1785    found via authentication.conf.
1786    """
1787
1788    def get_credential_store(self, encoding=None):
1789        cs = self.get(encoding)
1790        if callable(cs):
1791            cs = cs()
1792        return cs
1793
1794    def is_fallback(self, name):
1795        """Check if the named credentials store should be used as fallback."""
1796        return self.get_info(name)
1797
1798    def get_fallback_credentials(self, scheme, host, port=None, user=None,
1799                                 path=None, realm=None):
1800        """Request credentials from all fallback credentials stores.
1801
1802        The first credentials store that can provide credentials wins.
1803        """
1804        credentials = None
1805        for name in self.keys():
1806            if not self.is_fallback(name):
1807                continue
1808            cs = self.get_credential_store(name)
1809            credentials = cs.get_credentials(scheme, host, port, user,
1810                                             path, realm)
1811            if credentials is not None:
1812                # We found some credentials
1813                break
1814        return credentials
1815
1816    def register(self, key, obj, help=None, override_existing=False,
1817                 fallback=False):
1818        """Register a new object to a name.
1819
1820        :param key: This is the key to use to request the object later.
1821        :param obj: The object to register.
1822        :param help: Help text for this entry. This may be a string or
1823                a callable. If it is a callable, it should take two
1824                parameters (registry, key): this registry and the key that
1825                the help was registered under.
1826        :param override_existing: Raise KeyErorr if False and something has
1827                already been registered for that key. If True, ignore if there
1828                is an existing key (always register the new value).
1829        :param fallback: Whether this credential store should be
1830                used as fallback.
1831        """
1832        return super(CredentialStoreRegistry,
1833                     self).register(key, obj, help, info=fallback,
1834                                    override_existing=override_existing)
1835
1836    def register_lazy(self, key, module_name, member_name,
1837                      help=None, override_existing=False,
1838                      fallback=False):
1839        """Register a new credential store to be loaded on request.
1840
1841        :param module_name: The python path to the module. Such as 'os.path'.
1842        :param member_name: The member of the module to return.  If empty or
1843                None, get() will return the module itself.
1844        :param help: Help text for this entry. This may be a string or
1845                a callable.
1846        :param override_existing: If True, replace the existing object
1847                with the new one. If False, if there is already something
1848                registered with the same key, raise a KeyError
1849        :param fallback: Whether this credential store should be
1850                used as fallback.
1851        """
1852        return super(CredentialStoreRegistry, self).register_lazy(
1853            key, module_name, member_name, help,
1854            info=fallback, override_existing=override_existing)
1855
1856
1857credential_store_registry = CredentialStoreRegistry()
1858
1859
1860class CredentialStore(object):
1861    """An abstract class to implement storage for credentials"""
1862
1863    def decode_password(self, credentials):
1864        """Returns a clear text password for the provided credentials."""
1865        raise NotImplementedError(self.decode_password)
1866
1867    def get_credentials(self, scheme, host, port=None, user=None, path=None,
1868                        realm=None):
1869        """Return the matching credentials from this credential store.
1870
1871        This method is only called on fallback credential stores.
1872        """
1873        raise NotImplementedError(self.get_credentials)
1874
1875
1876class PlainTextCredentialStore(CredentialStore):
1877    __doc__ = """Plain text credential store for the authentication.conf file"""
1878
1879    def decode_password(self, credentials):
1880        """See CredentialStore.decode_password."""
1881        return credentials['password']
1882
1883
1884credential_store_registry.register('plain', PlainTextCredentialStore,
1885                                   help=PlainTextCredentialStore.__doc__)
1886credential_store_registry.default_key = 'plain'
1887
1888
1889class Base64CredentialStore(CredentialStore):
1890    __doc__ = """Base64 credential store for the authentication.conf file"""
1891
1892    def decode_password(self, credentials):
1893        """See CredentialStore.decode_password."""
1894        # GZ 2012-07-28: Will raise binascii.Error if password is not base64,
1895        #                should probably propogate as something more useful.
1896        return base64.standard_b64decode(credentials['password'])
1897
1898
1899credential_store_registry.register('base64', Base64CredentialStore,
1900                                   help=Base64CredentialStore.__doc__)
1901
1902
1903class BzrDirConfig(object):
1904
1905    def __init__(self, bzrdir):
1906        self._bzrdir = bzrdir
1907        self._config = bzrdir._get_config()
1908
1909    def set_default_stack_on(self, value):
1910        """Set the default stacking location.
1911
1912        It may be set to a location, or None.
1913
1914        This policy affects all branches contained by this control dir, except
1915        for those under repositories.
1916        """
1917        if self._config is None:
1918            raise errors.BzrError("Cannot set configuration in %s"
1919                                  % self._bzrdir)
1920        if value is None:
1921            self._config.set_option('', 'default_stack_on')
1922        else:
1923            self._config.set_option(value, 'default_stack_on')
1924
1925    def get_default_stack_on(self):
1926        """Return the default stacking location.
1927
1928        This will either be a location, or None.
1929
1930        This policy affects all branches contained by this control dir, except
1931        for those under repositories.
1932        """
1933        if self._config is None:
1934            return None
1935        value = self._config.get_option('default_stack_on')
1936        if value == '':
1937            value = None
1938        return value
1939
1940
1941class TransportConfig(object):
1942    """A Config that reads/writes a config file on a Transport.
1943
1944    It is a low-level object that considers config data to be name/value pairs
1945    that may be associated with a section.  Assigning meaning to these values
1946    is done at higher levels like TreeConfig.
1947    """
1948
1949    def __init__(self, transport, filename):
1950        self._transport = transport
1951        self._filename = filename
1952
1953    def get_option(self, name, section=None, default=None):
1954        """Return the value associated with a named option.
1955
1956        :param name: The name of the value
1957        :param section: The section the option is in (if any)
1958        :param default: The value to return if the value is not set
1959        :return: The value or default value
1960        """
1961        configobj = self._get_configobj()
1962        if section is None:
1963            section_obj = configobj
1964        else:
1965            try:
1966                section_obj = configobj[section]
1967            except KeyError:
1968                return default
1969        value = section_obj.get(name, default)
1970        for hook in OldConfigHooks['get']:
1971            hook(self, name, value)
1972        return value
1973
1974    def set_option(self, value, name, section=None):
1975        """Set the value associated with a named option.
1976
1977        :param value: The value to set
1978        :param name: The name of the value to set
1979        :param section: The section the option is in (if any)
1980        """
1981        configobj = self._get_configobj()
1982        if section is None:
1983            configobj[name] = value
1984        else:
1985            configobj.setdefault(section, {})[name] = value
1986        for hook in OldConfigHooks['set']:
1987            hook(self, name, value)
1988        self._set_configobj(configobj)
1989
1990    def remove_option(self, option_name, section_name=None):
1991        configobj = self._get_configobj()
1992        if section_name is None:
1993            del configobj[option_name]
1994        else:
1995            del configobj[section_name][option_name]
1996        for hook in OldConfigHooks['remove']:
1997            hook(self, option_name)
1998        self._set_configobj(configobj)
1999
2000    def _get_config_file(self):
2001        try:
2002            f = BytesIO(self._transport.get_bytes(self._filename))
2003            for hook in OldConfigHooks['load']:
2004                hook(self)
2005            return f
2006        except errors.NoSuchFile:
2007            return BytesIO()
2008        except errors.PermissionDenied:
2009            trace.warning(
2010                "Permission denied while trying to open "
2011                "configuration file %s.",
2012                urlutils.unescape_for_display(
2013                    urlutils.join(self._transport.base, self._filename),
2014                    "utf-8"))
2015            return BytesIO()
2016
2017    def _external_url(self):
2018        return urlutils.join(self._transport.external_url(), self._filename)
2019
2020    def _get_configobj(self):
2021        f = self._get_config_file()
2022        try:
2023            try:
2024                conf = ConfigObj(f, encoding='utf-8')
2025            except configobj.ConfigObjError as e:
2026                raise ParseConfigError(e.errors, self._external_url())
2027            except UnicodeDecodeError:
2028                raise ConfigContentError(self._external_url())
2029        finally:
2030            f.close()
2031        return conf
2032
2033    def _set_configobj(self, configobj):
2034        out_file = BytesIO()
2035        configobj.write(out_file)
2036        out_file.seek(0)
2037        self._transport.put_file(self._filename, out_file)
2038        for hook in OldConfigHooks['save']:
2039            hook(self)
2040
2041
2042class Option(object):
2043    """An option definition.
2044
2045    The option *values* are stored in config files and found in sections.
2046
2047    Here we define various properties about the option itself, its default
2048    value, how to convert it from stores, what to do when invalid values are
2049    encoutered, in which config files it can be stored.
2050    """
2051
2052    def __init__(self, name, override_from_env=None,
2053                 default=None, default_from_env=None,
2054                 help=None, from_unicode=None, invalid=None, unquote=True):
2055        """Build an option definition.
2056
2057        :param name: the name used to refer to the option.
2058
2059        :param override_from_env: A list of environment variables which can
2060           provide override any configuration setting.
2061
2062        :param default: the default value to use when none exist in the config
2063            stores. This is either a string that ``from_unicode`` will convert
2064            into the proper type, a callable returning a unicode string so that
2065            ``from_unicode`` can be used on the return value, or a python
2066            object that can be stringified (so only the empty list is supported
2067            for example).
2068
2069        :param default_from_env: A list of environment variables which can
2070           provide a default value. 'default' will be used only if none of the
2071           variables specified here are set in the environment.
2072
2073        :param help: a doc string to explain the option to the user.
2074
2075        :param from_unicode: a callable to convert the unicode string
2076            representing the option value in a store or its default value.
2077
2078        :param invalid: the action to be taken when an invalid value is
2079            encountered in a store. This is called only when from_unicode is
2080            invoked to convert a string and returns None or raise ValueError or
2081            TypeError. Accepted values are: None (ignore invalid values),
2082            'warning' (emit a warning), 'error' (emit an error message and
2083            terminates).
2084
2085        :param unquote: should the unicode value be unquoted before conversion.
2086           This should be used only when the store providing the values cannot
2087           safely unquote them (see http://pad.lv/906897). It is provided so
2088           daughter classes can handle the quoting themselves.
2089        """
2090        if override_from_env is None:
2091            override_from_env = []
2092        if default_from_env is None:
2093            default_from_env = []
2094        self.name = name
2095        self.override_from_env = override_from_env
2096        # Convert the default value to a unicode string so all values are
2097        # strings internally before conversion (via from_unicode) is attempted.
2098        if default is None:
2099            self.default = None
2100        elif isinstance(default, list):
2101            # Only the empty list is supported
2102            if default:
2103                raise AssertionError(
2104                    'Only empty lists are supported as default values')
2105            self.default = u','
2106        elif isinstance(default, (bytes, str, bool, int, float)):
2107            # Rely on python to convert strings, booleans and integers
2108            self.default = u'%s' % (default,)
2109        elif callable(default):
2110            self.default = default
2111        else:
2112            # other python objects are not expected
2113            raise AssertionError('%r is not supported as a default value'
2114                                 % (default,))
2115        self.default_from_env = default_from_env
2116        self._help = help
2117        self.from_unicode = from_unicode
2118        self.unquote = unquote
2119        if invalid and invalid not in ('warning', 'error'):
2120            raise AssertionError("%s not supported for 'invalid'" % (invalid,))
2121        self.invalid = invalid
2122
2123    @property
2124    def help(self):
2125        return self._help
2126
2127    def convert_from_unicode(self, store, unicode_value):
2128        if self.unquote and store is not None and unicode_value is not None:
2129            unicode_value = store.unquote(unicode_value)
2130        if self.from_unicode is None or unicode_value is None:
2131            # Don't convert or nothing to convert
2132            return unicode_value
2133        try:
2134            converted = self.from_unicode(unicode_value)
2135        except (ValueError, TypeError):
2136            # Invalid values are ignored
2137            converted = None
2138        if converted is None and self.invalid is not None:
2139            # The conversion failed
2140            if self.invalid == 'warning':
2141                trace.warning('Value "%s" is not valid for "%s"',
2142                              unicode_value, self.name)
2143            elif self.invalid == 'error':
2144                raise ConfigOptionValueError(self.name, unicode_value)
2145        return converted
2146
2147    def get_override(self):
2148        value = None
2149        for var in self.override_from_env:
2150            try:
2151                # If the env variable is defined, its value takes precedence
2152                value = os.environ[var]
2153                break
2154            except KeyError:
2155                continue
2156        return value
2157
2158    def get_default(self):
2159        value = None
2160        for var in self.default_from_env:
2161            try:
2162                # If the env variable is defined, its value is the default one
2163                value = os.environ[var]
2164                break
2165            except KeyError:
2166                continue
2167        if value is None:
2168            # Otherwise, fallback to the value defined at registration
2169            if callable(self.default):
2170                value = self.default()
2171                if not isinstance(value, str):
2172                    raise AssertionError(
2173                        "Callable default value for '%s' should be unicode"
2174                        % (self.name))
2175            else:
2176                value = self.default
2177        return value
2178
2179    def get_help_topic(self):
2180        return self.name
2181
2182    def get_help_text(self, additional_see_also=None, plain=True):
2183        result = self.help
2184        from breezy import help_topics
2185        result += help_topics._format_see_also(additional_see_also)
2186        if plain:
2187            result = help_topics.help_as_plain_text(result)
2188        return result
2189
2190
2191# Predefined converters to get proper values from store
2192
2193def bool_from_store(unicode_str):
2194    return ui.bool_from_string(unicode_str)
2195
2196
2197def int_from_store(unicode_str):
2198    return int(unicode_str)
2199
2200
2201_unit_suffixes = dict(K=10**3, M=10**6, G=10**9)
2202
2203
2204def int_SI_from_store(unicode_str):
2205    """Convert a human readable size in SI units, e.g 10MB into an integer.
2206
2207    Accepted suffixes are K,M,G. It is case-insensitive and may be followed
2208    by a trailing b (i.e. Kb, MB). This is intended to be practical and not
2209    pedantic.
2210
2211    :return Integer, expanded to its base-10 value if a proper SI unit is
2212        found, None otherwise.
2213    """
2214    regexp = "^(\\d+)(([" + ''.join(_unit_suffixes) + "])b?)?$"
2215    p = re.compile(regexp, re.IGNORECASE)
2216    m = p.match(unicode_str)
2217    val = None
2218    if m is not None:
2219        val, _, unit = m.groups()
2220        val = int(val)
2221        if unit:
2222            try:
2223                coeff = _unit_suffixes[unit.upper()]
2224            except KeyError:
2225                raise ValueError(
2226                    gettext('{0} is not an SI unit.').format(unit))
2227            val *= coeff
2228    return val
2229
2230
2231def float_from_store(unicode_str):
2232    return float(unicode_str)
2233
2234
2235# Use an empty dict to initialize an empty configobj avoiding all parsing and
2236# encoding checks
2237_list_converter_config = configobj.ConfigObj(
2238    {}, encoding='utf-8', list_values=True, interpolation=False)
2239
2240
2241class ListOption(Option):
2242
2243    def __init__(self, name, default=None, default_from_env=None,
2244                 help=None, invalid=None):
2245        """A list Option definition.
2246
2247        This overrides the base class so the conversion from a unicode string
2248        can take quoting into account.
2249        """
2250        super(ListOption, self).__init__(
2251            name, default=default, default_from_env=default_from_env,
2252            from_unicode=self.from_unicode, help=help,
2253            invalid=invalid, unquote=False)
2254
2255    def from_unicode(self, unicode_str):
2256        if not isinstance(unicode_str, str):
2257            raise TypeError
2258        # Now inject our string directly as unicode. All callers got their
2259        # value from configobj, so values that need to be quoted are already
2260        # properly quoted.
2261        _list_converter_config.reset()
2262        _list_converter_config._parse([u"list=%s" % (unicode_str,)])
2263        maybe_list = _list_converter_config['list']
2264        if isinstance(maybe_list, str):
2265            if maybe_list:
2266                # A single value, most probably the user forgot (or didn't care
2267                # to add) the final ','
2268                l = [maybe_list]
2269            else:
2270                # The empty string, convert to empty list
2271                l = []
2272        else:
2273            # We rely on ConfigObj providing us with a list already
2274            l = maybe_list
2275        return l
2276
2277
2278class RegistryOption(Option):
2279    """Option for a choice from a registry."""
2280
2281    def __init__(self, name, registry, default_from_env=None,
2282                 help=None, invalid=None):
2283        """A registry based Option definition.
2284
2285        This overrides the base class so the conversion from a unicode string
2286        can take quoting into account.
2287        """
2288        super(RegistryOption, self).__init__(
2289            name, default=lambda: registry.default_key,
2290            default_from_env=default_from_env,
2291            from_unicode=self.from_unicode, help=help,
2292            invalid=invalid, unquote=False)
2293        self.registry = registry
2294
2295    def from_unicode(self, unicode_str):
2296        if not isinstance(unicode_str, str):
2297            raise TypeError
2298        try:
2299            return self.registry.get(unicode_str)
2300        except KeyError:
2301            raise ValueError(
2302                "Invalid value %s for %s."
2303                "See help for a list of possible values." % (unicode_str,
2304                                                             self.name))
2305
2306    @property
2307    def help(self):
2308        ret = [self._help, "\n\nThe following values are supported:\n"]
2309        for key in self.registry.keys():
2310            ret.append(" %s - %s\n" % (key, self.registry.get_help(key)))
2311        return "".join(ret)
2312
2313
2314_option_ref_re = lazy_regex.lazy_compile('({[^\\d\\W](?:\\.\\w|-\\w|\\w)*})')
2315"""Describes an expandable option reference.
2316
2317We want to match the most embedded reference first.
2318
2319I.e. for '{{foo}}' we will get '{foo}',
2320for '{bar{baz}}' we will get '{baz}'
2321"""
2322
2323
2324def iter_option_refs(string):
2325    # Split isolate refs so every other chunk is a ref
2326    is_ref = False
2327    for chunk in _option_ref_re.split(string):
2328        yield is_ref, chunk
2329        is_ref = not is_ref
2330
2331
2332class OptionRegistry(registry.Registry):
2333    """Register config options by their name.
2334
2335    This overrides ``registry.Registry`` to simplify registration by acquiring
2336    some information from the option object itself.
2337    """
2338
2339    def _check_option_name(self, option_name):
2340        """Ensures an option name is valid.
2341
2342        :param option_name: The name to validate.
2343        """
2344        if _option_ref_re.match('{%s}' % option_name) is None:
2345            raise IllegalOptionName(option_name)
2346
2347    def register(self, option):
2348        """Register a new option to its name.
2349
2350        :param option: The option to register. Its name is used as the key.
2351        """
2352        self._check_option_name(option.name)
2353        super(OptionRegistry, self).register(option.name, option,
2354                                             help=option.help)
2355
2356    def register_lazy(self, key, module_name, member_name):
2357        """Register a new option to be loaded on request.
2358
2359        :param key: the key to request the option later. Since the registration
2360            is lazy, it should be provided and match the option name.
2361
2362        :param module_name: the python path to the module. Such as 'os.path'.
2363
2364        :param member_name: the member of the module to return.  If empty or
2365                None, get() will return the module itself.
2366        """
2367        self._check_option_name(key)
2368        super(OptionRegistry, self).register_lazy(key,
2369                                                  module_name, member_name)
2370
2371    def get_help(self, key=None):
2372        """Get the help text associated with the given key"""
2373        option = self.get(key)
2374        the_help = option.help
2375        if callable(the_help):
2376            return the_help(self, key)
2377        return the_help
2378
2379
2380option_registry = OptionRegistry()
2381
2382
2383# Registered options in lexicographical order
2384
2385option_registry.register(
2386    Option('append_revisions_only',
2387           default=None, from_unicode=bool_from_store, invalid='warning',
2388           help='''\
2389Whether to only append revisions to the mainline.
2390
2391If this is set to true, then it is not possible to change the
2392existing mainline of the branch.
2393'''))
2394option_registry.register(
2395    ListOption('acceptable_keys',
2396               default=None,
2397               help="""\
2398List of GPG key patterns which are acceptable for verification.
2399"""))
2400option_registry.register(
2401    Option('add.maximum_file_size',
2402           default=u'20MB', from_unicode=int_SI_from_store,
2403           help="""\
2404Size above which files should be added manually.
2405
2406Files below this size are added automatically when using ``bzr add`` without
2407arguments.
2408
2409A negative value means disable the size check.
2410"""))
2411option_registry.register(
2412    Option('bound',
2413           default=None, from_unicode=bool_from_store,
2414           help="""\
2415Is the branch bound to ``bound_location``.
2416
2417If set to "True", the branch should act as a checkout, and push each commit to
2418the bound_location.  This option is normally set by ``bind``/``unbind``.
2419
2420See also: bound_location.
2421"""))
2422option_registry.register(
2423    Option('bound_location',
2424           default=None,
2425           help="""\
2426The location that commits should go to when acting as a checkout.
2427
2428This option is normally set by ``bind``.
2429
2430See also: bound.
2431"""))
2432option_registry.register(
2433    Option('branch.fetch_tags', default=False, from_unicode=bool_from_store,
2434           help="""\
2435Whether revisions associated with tags should be fetched.
2436"""))
2437option_registry.register_lazy(
2438    'transform.orphan_policy', 'breezy.transform', 'opt_transform_orphan')
2439option_registry.register(
2440    Option('bzr.workingtree.worth_saving_limit', default=10,
2441           from_unicode=int_from_store, invalid='warning',
2442           help='''\
2443How many changes before saving the dirstate.
2444
2445-1 means that we will never rewrite the dirstate file for only
2446stat-cache changes. Regardless of this setting, we will always rewrite
2447the dirstate file if a file is added/removed/renamed/etc. This flag only
2448affects the behavior of updating the dirstate file after we notice that
2449a file has been touched.
2450'''))
2451option_registry.register(
2452    Option('bugtracker', default=None,
2453           help='''\
2454Default bug tracker to use.
2455
2456This bug tracker will be used for example when marking bugs
2457as fixed using ``bzr commit --fixes``, if no explicit
2458bug tracker was specified.
2459'''))
2460option_registry.register(
2461    Option('calculate_revnos', default=True,
2462           from_unicode=bool_from_store,
2463           help='''\
2464Calculate revision numbers if they are not known.
2465
2466Always show revision numbers, even for branch formats that don't store them
2467natively (such as Git). Calculating the revision number requires traversing
2468the left hand ancestry of the branch and can be slow on very large branches.
2469'''))
2470option_registry.register(
2471    Option('check_signatures', default=CHECK_IF_POSSIBLE,
2472           from_unicode=signature_policy_from_unicode,
2473           help='''\
2474GPG checking policy.
2475
2476Possible values: require, ignore, check-available (default)
2477
2478this option will control whether bzr will require good gpg
2479signatures, ignore them, or check them if they are
2480present.
2481'''))
2482option_registry.register(
2483    Option('child_submit_format',
2484           help='''The preferred format of submissions to this branch.'''))
2485option_registry.register(
2486    Option('child_submit_to',
2487           help='''Where submissions to this branch are mailed to.'''))
2488option_registry.register(
2489    Option('create_signatures', default=SIGN_WHEN_REQUIRED,
2490           from_unicode=signing_policy_from_unicode,
2491           help='''\
2492GPG Signing policy.
2493
2494Possible values: always, never, when-required (default)
2495
2496This option controls whether bzr will always create
2497gpg signatures or not on commits.
2498'''))
2499option_registry.register(
2500    Option('dirstate.fdatasync', default=True,
2501           from_unicode=bool_from_store,
2502           help='''\
2503Flush dirstate changes onto physical disk?
2504
2505If true (default), working tree metadata changes are flushed through the
2506OS buffers to physical disk.  This is somewhat slower, but means data
2507should not be lost if the machine crashes.  See also repository.fdatasync.
2508'''))
2509option_registry.register(
2510    ListOption('debug_flags', default=[],
2511               help='Debug flags to activate.'))
2512option_registry.register(
2513    Option('default_format', default='2a',
2514           help='Format used when creating branches.'))
2515option_registry.register(
2516    Option('editor',
2517           help='The command called to launch an editor to enter a message.'))
2518option_registry.register(
2519    Option('email', override_from_env=['BRZ_EMAIL', 'BZR_EMAIL'],
2520           default=bedding.default_email, help='The users identity'))
2521option_registry.register(
2522    Option('gpg_signing_key',
2523           default=None,
2524           help="""\
2525GPG key to use for signing.
2526
2527This defaults to the first key associated with the users email.
2528"""))
2529option_registry.register(
2530    Option('language',
2531           help='Language to translate messages into.'))
2532option_registry.register(
2533    Option('locks.steal_dead', default=True, from_unicode=bool_from_store,
2534           help='''\
2535Steal locks that appears to be dead.
2536
2537If set to True, bzr will check if a lock is supposed to be held by an
2538active process from the same user on the same machine. If the user and
2539machine match, but no process with the given PID is active, then bzr
2540will automatically break the stale lock, and create a new lock for
2541this process.
2542Otherwise, bzr will prompt as normal to break the lock.
2543'''))
2544option_registry.register(
2545    Option('log_format', default='long',
2546           help='''\
2547Log format to use when displaying revisions.
2548
2549Standard log formats are ``long``, ``short`` and ``line``. Additional formats
2550may be provided by plugins.
2551'''))
2552option_registry.register_lazy('mail_client', 'breezy.mail_client',
2553                              'opt_mail_client')
2554option_registry.register(
2555    Option('output_encoding',
2556           help='Unicode encoding for output'
2557           ' (terminal encoding if not specified).'))
2558option_registry.register(
2559    Option('parent_location',
2560           default=None,
2561           help="""\
2562The location of the default branch for pull or merge.
2563
2564This option is normally set when creating a branch, the first ``pull`` or by
2565``pull --remember``.
2566"""))
2567option_registry.register(
2568    Option('post_commit', default=None,
2569           help='''\
2570Post commit functions.
2571
2572An ordered list of python functions to call, separated by spaces.
2573
2574Each function takes branch, rev_id as parameters.
2575'''))
2576option_registry.register_lazy('progress_bar', 'breezy.ui.text',
2577                              'opt_progress_bar')
2578option_registry.register(
2579    Option('public_branch',
2580           default=None,
2581           help="""\
2582A publically-accessible version of this branch.
2583
2584This implies that the branch setting this option is not publically-accessible.
2585Used and set by ``bzr send``.
2586"""))
2587option_registry.register(
2588    Option('push_location',
2589           default=None,
2590           help="""\
2591The location of the default branch for push.
2592
2593This option is normally set by the first ``push`` or ``push --remember``.
2594"""))
2595option_registry.register(
2596    Option('push_strict', default=None,
2597           from_unicode=bool_from_store,
2598           help='''\
2599The default value for ``push --strict``.
2600
2601If present, defines the ``--strict`` option default value for checking
2602uncommitted changes before sending a merge directive.
2603'''))
2604option_registry.register(
2605    Option('repository.fdatasync', default=True,
2606           from_unicode=bool_from_store,
2607           help='''\
2608Flush repository changes onto physical disk?
2609
2610If true (default), repository changes are flushed through the OS buffers
2611to physical disk.  This is somewhat slower, but means data should not be
2612lost if the machine crashes.  See also dirstate.fdatasync.
2613'''))
2614option_registry.register_lazy('smtp_server',
2615                              'breezy.smtp_connection', 'smtp_server')
2616option_registry.register_lazy('smtp_password',
2617                              'breezy.smtp_connection', 'smtp_password')
2618option_registry.register_lazy('smtp_username',
2619                              'breezy.smtp_connection', 'smtp_username')
2620option_registry.register(
2621    Option('selftest.timeout',
2622           default='600',
2623           from_unicode=int_from_store,
2624           help='Abort selftest if one test takes longer than this many seconds',
2625           ))
2626
2627option_registry.register(
2628    Option('send_strict', default=None,
2629           from_unicode=bool_from_store,
2630           help='''\
2631The default value for ``send --strict``.
2632
2633If present, defines the ``--strict`` option default value for checking
2634uncommitted changes before sending a bundle.
2635'''))
2636
2637option_registry.register(
2638    Option('serve.client_timeout',
2639           default=300.0, from_unicode=float_from_store,
2640           help="If we wait for a new request from a client for more than"
2641                " X seconds, consider the client idle, and hangup."))
2642option_registry.register(
2643    Option('ssh',
2644           default=None, override_from_env=['BRZ_SSH'],
2645           help='SSH vendor to use.'))
2646option_registry.register(
2647    Option('stacked_on_location',
2648           default=None,
2649           help="""The location where this branch is stacked on."""))
2650option_registry.register(
2651    Option('submit_branch',
2652           default=None,
2653           help="""\
2654The branch you intend to submit your current work to.
2655
2656This is automatically set by ``bzr send`` and ``bzr merge``, and is also used
2657by the ``submit:`` revision spec.
2658"""))
2659option_registry.register(
2660    Option('submit_to',
2661           help='''Where submissions from this branch are mailed to.'''))
2662option_registry.register(
2663    ListOption('suppress_warnings',
2664               default=[],
2665               help="List of warning classes to suppress."))
2666option_registry.register(
2667    Option('validate_signatures_in_log', default=False,
2668           from_unicode=bool_from_store, invalid='warning',
2669           help='''Whether to validate signatures in brz log.'''))
2670option_registry.register_lazy('ssl.ca_certs',
2671                              'breezy.transport.http', 'opt_ssl_ca_certs')
2672
2673option_registry.register_lazy('ssl.cert_reqs',
2674                              'breezy.transport.http', 'opt_ssl_cert_reqs')
2675
2676
2677class Section(object):
2678    """A section defines a dict of option name => value.
2679
2680    This is merely a read-only dict which can add some knowledge about the
2681    options. It is *not* a python dict object though and doesn't try to mimic
2682    its API.
2683    """
2684
2685    def __init__(self, section_id, options):
2686        self.id = section_id
2687        # We re-use the dict-like object received
2688        self.options = options
2689
2690    def get(self, name, default=None, expand=True):
2691        return self.options.get(name, default)
2692
2693    def iter_option_names(self):
2694        for k in self.options.keys():
2695            yield k
2696
2697    def __repr__(self):
2698        # Mostly for debugging use
2699        return "<config.%s id=%s>" % (self.__class__.__name__, self.id)
2700
2701
2702_NewlyCreatedOption = object()
2703"""Was the option created during the MutableSection lifetime"""
2704_DeletedOption = object()
2705"""Was the option deleted during the MutableSection lifetime"""
2706
2707
2708class MutableSection(Section):
2709    """A section allowing changes and keeping track of the original values."""
2710
2711    def __init__(self, section_id, options):
2712        super(MutableSection, self).__init__(section_id, options)
2713        self.reset_changes()
2714
2715    def set(self, name, value):
2716        if name not in self.options:
2717            # This is a new option
2718            self.orig[name] = _NewlyCreatedOption
2719        elif name not in self.orig:
2720            self.orig[name] = self.get(name, None)
2721        self.options[name] = value
2722
2723    def remove(self, name):
2724        if name not in self.orig and name in self.options:
2725            self.orig[name] = self.get(name, None)
2726        del self.options[name]
2727
2728    def reset_changes(self):
2729        self.orig = {}
2730
2731    def apply_changes(self, dirty, store):
2732        """Apply option value changes.
2733
2734        ``self`` has been reloaded from the persistent storage. ``dirty``
2735        contains the changes made since the previous loading.
2736
2737        :param dirty: the mutable section containing the changes.
2738
2739        :param store: the store containing the section
2740        """
2741        for k, expected in dirty.orig.items():
2742            actual = dirty.get(k, _DeletedOption)
2743            reloaded = self.get(k, _NewlyCreatedOption)
2744            if actual is _DeletedOption:
2745                if k in self.options:
2746                    self.remove(k)
2747            else:
2748                self.set(k, actual)
2749            # Report concurrent updates in an ad-hoc way. This should only
2750            # occurs when different processes try to update the same option
2751            # which is not supported (as in: the config framework is not meant
2752            # to be used as a sharing mechanism).
2753            if expected != reloaded:
2754                if actual is _DeletedOption:
2755                    actual = '<DELETED>'
2756                if reloaded is _NewlyCreatedOption:
2757                    reloaded = '<CREATED>'
2758                if expected is _NewlyCreatedOption:
2759                    expected = '<CREATED>'
2760                # Someone changed the value since we get it from the persistent
2761                # storage.
2762                trace.warning(gettext(
2763                    "Option {0} in section {1} of {2} was changed"
2764                    " from {3} to {4}. The {5} value will be saved.".format(
2765                        k, self.id, store.external_url(), expected,
2766                        reloaded, actual)))
2767        # No need to keep track of these changes
2768        self.reset_changes()
2769
2770
2771class Store(object):
2772    """Abstract interface to persistent storage for configuration options."""
2773
2774    readonly_section_class = Section
2775    mutable_section_class = MutableSection
2776
2777    def __init__(self):
2778        # Which sections need to be saved (by section id). We use a dict here
2779        # so the dirty sections can be shared by multiple callers.
2780        self.dirty_sections = {}
2781
2782    def is_loaded(self):
2783        """Returns True if the Store has been loaded.
2784
2785        This is used to implement lazy loading and ensure the persistent
2786        storage is queried only when needed.
2787        """
2788        raise NotImplementedError(self.is_loaded)
2789
2790    def load(self):
2791        """Loads the Store from persistent storage."""
2792        raise NotImplementedError(self.load)
2793
2794    def _load_from_string(self, bytes):
2795        """Create a store from a string in configobj syntax.
2796
2797        :param bytes: A string representing the file content.
2798        """
2799        raise NotImplementedError(self._load_from_string)
2800
2801    def unload(self):
2802        """Unloads the Store.
2803
2804        This should make is_loaded() return False. This is used when the caller
2805        knows that the persistent storage has changed or may have change since
2806        the last load.
2807        """
2808        raise NotImplementedError(self.unload)
2809
2810    def quote(self, value):
2811        """Quote a configuration option value for storing purposes.
2812
2813        This allows Stacks to present values as they will be stored.
2814        """
2815        return value
2816
2817    def unquote(self, value):
2818        """Unquote a configuration option value into unicode.
2819
2820        The received value is quoted as stored.
2821        """
2822        return value
2823
2824    def save(self):
2825        """Saves the Store to persistent storage."""
2826        raise NotImplementedError(self.save)
2827
2828    def _need_saving(self):
2829        for s in self.dirty_sections.values():
2830            if s.orig:
2831                # At least one dirty section contains a modification
2832                return True
2833        return False
2834
2835    def apply_changes(self, dirty_sections):
2836        """Apply changes from dirty sections while checking for coherency.
2837
2838        The Store content is discarded and reloaded from persistent storage to
2839        acquire up-to-date values.
2840
2841        Dirty sections are MutableSection which kept track of the value they
2842        are expected to update.
2843        """
2844        # We need an up-to-date version from the persistent storage, unload the
2845        # store. The reload will occur when needed (triggered by the first
2846        # get_mutable_section() call below.
2847        self.unload()
2848        # Apply the changes from the preserved dirty sections
2849        for section_id, dirty in dirty_sections.items():
2850            clean = self.get_mutable_section(section_id)
2851            clean.apply_changes(dirty, self)
2852        # Everything is clean now
2853        self.dirty_sections = {}
2854
2855    def save_changes(self):
2856        """Saves the Store to persistent storage if changes occurred.
2857
2858        Apply the changes recorded in the mutable sections to a store content
2859        refreshed from persistent storage.
2860        """
2861        raise NotImplementedError(self.save_changes)
2862
2863    def external_url(self):
2864        raise NotImplementedError(self.external_url)
2865
2866    def get_sections(self):
2867        """Returns an ordered iterable of existing sections.
2868
2869        :returns: An iterable of (store, section).
2870        """
2871        raise NotImplementedError(self.get_sections)
2872
2873    def get_mutable_section(self, section_id=None):
2874        """Returns the specified mutable section.
2875
2876        :param section_id: The section identifier
2877        """
2878        raise NotImplementedError(self.get_mutable_section)
2879
2880    def __repr__(self):
2881        # Mostly for debugging use
2882        return "<config.%s(%s)>" % (self.__class__.__name__,
2883                                    self.external_url())
2884
2885
2886class CommandLineStore(Store):
2887    "A store to carry command line overrides for the config options."""
2888
2889    def __init__(self, opts=None):
2890        super(CommandLineStore, self).__init__()
2891        if opts is None:
2892            opts = {}
2893        self.options = {}
2894        self.id = 'cmdline'
2895
2896    def _reset(self):
2897        # The dict should be cleared but not replaced so it can be shared.
2898        self.options.clear()
2899
2900    def _from_cmdline(self, overrides):
2901        # Reset before accepting new definitions
2902        self._reset()
2903        for over in overrides:
2904            try:
2905                name, value = over.split('=', 1)
2906            except ValueError:
2907                raise errors.CommandError(
2908                    gettext("Invalid '%s', should be of the form 'name=value'")
2909                    % (over,))
2910            self.options[name] = value
2911
2912    def external_url(self):
2913        # Not an url but it makes debugging easier and is never needed
2914        # otherwise
2915        return 'cmdline'
2916
2917    def get_sections(self):
2918        yield self, self.readonly_section_class(None, self.options)
2919
2920
2921class IniFileStore(Store):
2922    """A config Store using ConfigObj for storage.
2923
2924    :ivar _config_obj: Private member to hold the ConfigObj instance used to
2925        serialize/deserialize the config file.
2926    """
2927
2928    def __init__(self):
2929        """A config Store using ConfigObj for storage.
2930        """
2931        super(IniFileStore, self).__init__()
2932        self._config_obj = None
2933
2934    def is_loaded(self):
2935        return self._config_obj is not None
2936
2937    def unload(self):
2938        self._config_obj = None
2939        self.dirty_sections = {}
2940
2941    def _load_content(self):
2942        """Load the config file bytes.
2943
2944        This should be provided by subclasses
2945
2946        :return: Byte string
2947        """
2948        raise NotImplementedError(self._load_content)
2949
2950    def _save_content(self, content):
2951        """Save the config file bytes.
2952
2953        This should be provided by subclasses
2954
2955        :param content: Config file bytes to write
2956        """
2957        raise NotImplementedError(self._save_content)
2958
2959    def load(self):
2960        """Load the store from the associated file."""
2961        if self.is_loaded():
2962            return
2963        content = self._load_content()
2964        self._load_from_string(content)
2965        for hook in ConfigHooks['load']:
2966            hook(self)
2967
2968    def _load_from_string(self, bytes):
2969        """Create a config store from a string.
2970
2971        :param bytes: A string representing the file content.
2972        """
2973        if self.is_loaded():
2974            raise AssertionError('Already loaded: %r' % (self._config_obj,))
2975        co_input = BytesIO(bytes)
2976        try:
2977            # The config files are always stored utf8-encoded
2978            self._config_obj = ConfigObj(co_input, encoding='utf-8',
2979                                         list_values=False)
2980        except configobj.ConfigObjError as e:
2981            self._config_obj = None
2982            raise ParseConfigError(e.errors, self.external_url())
2983        except UnicodeDecodeError:
2984            raise ConfigContentError(self.external_url())
2985
2986    def save_changes(self):
2987        if not self.is_loaded():
2988            # Nothing to save
2989            return
2990        if not self._need_saving():
2991            return
2992        # Preserve the current version
2993        dirty_sections = self.dirty_sections.copy()
2994        self.apply_changes(dirty_sections)
2995        # Save to the persistent storage
2996        self.save()
2997
2998    def save(self):
2999        if not self.is_loaded():
3000            # Nothing to save
3001            return
3002        out = BytesIO()
3003        self._config_obj.write(out)
3004        self._save_content(out.getvalue())
3005        for hook in ConfigHooks['save']:
3006            hook(self)
3007
3008    def get_sections(self):
3009        """Get the configobj section in the file order.
3010
3011        :returns: An iterable of (store, section).
3012        """
3013        # We need a loaded store
3014        try:
3015            self.load()
3016        except (errors.NoSuchFile, errors.PermissionDenied):
3017            # If the file can't be read, there is no sections
3018            return
3019        cobj = self._config_obj
3020        if cobj.scalars:
3021            yield self, self.readonly_section_class(None, cobj)
3022        for section_name in cobj.sections:
3023            yield (self,
3024                   self.readonly_section_class(section_name,
3025                                               cobj[section_name]))
3026
3027    def get_mutable_section(self, section_id=None):
3028        # We need a loaded store
3029        try:
3030            self.load()
3031        except errors.NoSuchFile:
3032            # The file doesn't exist, let's pretend it was empty
3033            self._load_from_string(b'')
3034        if section_id in self.dirty_sections:
3035            # We already created a mutable section for this id
3036            return self.dirty_sections[section_id]
3037        if section_id is None:
3038            section = self._config_obj
3039        else:
3040            section = self._config_obj.setdefault(section_id, {})
3041        mutable_section = self.mutable_section_class(section_id, section)
3042        # All mutable sections can become dirty
3043        self.dirty_sections[section_id] = mutable_section
3044        return mutable_section
3045
3046    def quote(self, value):
3047        try:
3048            # configobj conflates automagical list values and quoting
3049            self._config_obj.list_values = True
3050            return self._config_obj._quote(value)
3051        finally:
3052            self._config_obj.list_values = False
3053
3054    def unquote(self, value):
3055        if value and isinstance(value, str):
3056            # _unquote doesn't handle None nor empty strings nor anything that
3057            # is not a string, really.
3058            value = self._config_obj._unquote(value)
3059        return value
3060
3061    def external_url(self):
3062        # Since an IniFileStore can be used without a file (at least in tests),
3063        # it's better to provide something than raising a NotImplementedError.
3064        # All daughter classes are supposed to provide an implementation
3065        # anyway.
3066        return 'In-Process Store, no URL'
3067
3068
3069class TransportIniFileStore(IniFileStore):
3070    """IniFileStore that loads files from a transport.
3071
3072    :ivar transport: The transport object where the config file is located.
3073
3074    :ivar file_name: The config file basename in the transport directory.
3075    """
3076
3077    def __init__(self, transport, file_name):
3078        """A Store using a ini file on a Transport
3079
3080        :param transport: The transport object where the config file is located.
3081        :param file_name: The config file basename in the transport directory.
3082        """
3083        super(TransportIniFileStore, self).__init__()
3084        self.transport = transport
3085        self.file_name = file_name
3086
3087    def _load_content(self):
3088        try:
3089            return self.transport.get_bytes(self.file_name)
3090        except errors.PermissionDenied:
3091            trace.warning("Permission denied while trying to load "
3092                          "configuration store %s.", self.external_url())
3093            raise
3094
3095    def _save_content(self, content):
3096        self.transport.put_bytes(self.file_name, content)
3097
3098    def external_url(self):
3099        # FIXME: external_url should really accepts an optional relpath
3100        # parameter (bug #750169) :-/ -- vila 2011-04-04
3101        # The following will do in the interim but maybe we don't want to
3102        # expose a path here but rather a config ID and its associated
3103        # object </hand wawe>.
3104        return urlutils.join(
3105            self.transport.external_url(), urlutils.escape(self.file_name))
3106
3107
3108# Note that LockableConfigObjStore inherits from ConfigObjStore because we need
3109# unlockable stores for use with objects that can already ensure the locking
3110# (think branches). If different stores (not based on ConfigObj) are created,
3111# they may face the same issue.
3112
3113
3114class LockableIniFileStore(TransportIniFileStore):
3115    """A ConfigObjStore using locks on save to ensure store integrity."""
3116
3117    def __init__(self, transport, file_name, lock_dir_name=None):
3118        """A config Store using ConfigObj for storage.
3119
3120        :param transport: The transport object where the config file is located.
3121
3122        :param file_name: The config file basename in the transport directory.
3123        """
3124        if lock_dir_name is None:
3125            lock_dir_name = 'lock'
3126        self.lock_dir_name = lock_dir_name
3127        super(LockableIniFileStore, self).__init__(transport, file_name)
3128        self._lock = lockdir.LockDir(self.transport, self.lock_dir_name)
3129
3130    def lock_write(self, token=None):
3131        """Takes a write lock in the directory containing the config file.
3132
3133        If the directory doesn't exist it is created.
3134        """
3135        # FIXME: This doesn't check the ownership of the created directories as
3136        # ensure_config_dir_exists does. It should if the transport is local
3137        # -- vila 2011-04-06
3138        self.transport.create_prefix()
3139        token = self._lock.lock_write(token)
3140        return lock.LogicalLockResult(self.unlock, token)
3141
3142    def unlock(self):
3143        self._lock.unlock()
3144
3145    def break_lock(self):
3146        self._lock.break_lock()
3147
3148    def save(self):
3149        with self.lock_write():
3150            # We need to be able to override the undecorated implementation
3151            self.save_without_locking()
3152
3153    def save_without_locking(self):
3154        super(LockableIniFileStore, self).save()
3155
3156
3157# FIXME: global, breezy, shouldn't that be 'user' instead or even
3158# 'user_defaults' as opposed to 'user_overrides', 'system_defaults'
3159# (/etc/bzr/bazaar.conf) and 'system_overrides' ? -- vila 2011-04-05
3160
3161# FIXME: Moreover, we shouldn't need classes for these stores either, factory
3162# functions or a registry will make it easier and clearer for tests, focusing
3163# on the relevant parts of the API that needs testing -- vila 20110503 (based
3164# on a poolie's remark)
3165class GlobalStore(LockableIniFileStore):
3166    """A config store for global options.
3167
3168    There is a single GlobalStore for a given process.
3169    """
3170
3171    def __init__(self, possible_transports=None):
3172        path, kind = bedding._config_dir()
3173        t = transport.get_transport_from_path(
3174            path, possible_transports=possible_transports)
3175        super(GlobalStore, self).__init__(t, kind + '.conf')
3176        self.id = 'breezy'
3177
3178
3179class LocationStore(LockableIniFileStore):
3180    """A config store for options specific to a location.
3181
3182    There is a single LocationStore for a given process.
3183    """
3184
3185    def __init__(self, possible_transports=None):
3186        t = transport.get_transport_from_path(
3187            bedding.config_dir(), possible_transports=possible_transports)
3188        super(LocationStore, self).__init__(t, 'locations.conf')
3189        self.id = 'locations'
3190
3191
3192class BranchStore(TransportIniFileStore):
3193    """A config store for branch options.
3194
3195    There is a single BranchStore for a given branch.
3196    """
3197
3198    def __init__(self, branch):
3199        super(BranchStore, self).__init__(branch.control_transport,
3200                                          'branch.conf')
3201        self.branch = branch
3202        self.id = 'branch'
3203
3204
3205class ControlStore(LockableIniFileStore):
3206
3207    def __init__(self, bzrdir):
3208        super(ControlStore, self).__init__(bzrdir.transport,
3209                                           'control.conf',
3210                                           lock_dir_name='branch_lock')
3211        self.id = 'control'
3212
3213
3214class SectionMatcher(object):
3215    """Select sections into a given Store.
3216
3217    This is intended to be used to postpone getting an iterable of sections
3218    from a store.
3219    """
3220
3221    def __init__(self, store):
3222        self.store = store
3223
3224    def get_sections(self):
3225        # This is where we require loading the store so we can see all defined
3226        # sections.
3227        sections = self.store.get_sections()
3228        # Walk the revisions in the order provided
3229        for store, s in sections:
3230            if self.match(s):
3231                yield store, s
3232
3233    def match(self, section):
3234        """Does the proposed section match.
3235
3236        :param section: A Section object.
3237
3238        :returns: True if the section matches, False otherwise.
3239        """
3240        raise NotImplementedError(self.match)
3241
3242
3243class NameMatcher(SectionMatcher):
3244
3245    def __init__(self, store, section_id):
3246        super(NameMatcher, self).__init__(store)
3247        self.section_id = section_id
3248
3249    def match(self, section):
3250        return section.id == self.section_id
3251
3252
3253class LocationSection(Section):
3254
3255    def __init__(self, section, extra_path, branch_name=None):
3256        super(LocationSection, self).__init__(section.id, section.options)
3257        self.extra_path = extra_path
3258        if branch_name is None:
3259            branch_name = ''
3260        self.locals = {'relpath': extra_path,
3261                       'basename': urlutils.basename(extra_path),
3262                       'branchname': branch_name}
3263
3264    def get(self, name, default=None, expand=True):
3265        value = super(LocationSection, self).get(name, default)
3266        if value is not None and expand:
3267            policy_name = self.get(name + ':policy', None)
3268            policy = _policy_value.get(policy_name, POLICY_NONE)
3269            if policy == POLICY_APPENDPATH:
3270                value = urlutils.join(value, self.extra_path)
3271            # expand section local options right now (since POLICY_APPENDPATH
3272            # will never add options references, it's ok to expand after it).
3273            chunks = []
3274            for is_ref, chunk in iter_option_refs(value):
3275                if not is_ref:
3276                    chunks.append(chunk)
3277                else:
3278                    ref = chunk[1:-1]
3279                    if ref in self.locals:
3280                        chunks.append(self.locals[ref])
3281                    else:
3282                        chunks.append(chunk)
3283            value = ''.join(chunks)
3284        return value
3285
3286
3287class StartingPathMatcher(SectionMatcher):
3288    """Select sections for a given location respecting the Store order."""
3289
3290    # FIXME: Both local paths and urls can be used for section names as well as
3291    # ``location`` to stay consistent with ``LocationMatcher`` which itself
3292    # inherited the fuzziness from the previous ``LocationConfig``
3293    # implementation. We probably need to revisit which encoding is allowed for
3294    # both ``location`` and section names and how we normalize
3295    # them. http://pad.lv/85479, http://pad.lv/437009 and http://359320 are
3296    # related too. -- vila 2012-01-04
3297
3298    def __init__(self, store, location):
3299        super(StartingPathMatcher, self).__init__(store)
3300        if location.startswith('file://'):
3301            location = urlutils.local_path_from_url(location)
3302        self.location = location
3303
3304    def get_sections(self):
3305        """Get all sections matching ``location`` in the store.
3306
3307        The most generic sections are described first in the store, then more
3308        specific ones can be provided for reduced scopes.
3309
3310        The returned section are therefore returned in the reversed order so
3311        the most specific ones can be found first.
3312        """
3313        location_parts = self.location.rstrip('/').split('/')
3314        store = self.store
3315        # Later sections are more specific, they should be returned first
3316        for _, section in reversed(list(store.get_sections())):
3317            if section.id is None:
3318                # The no-name section is always included if present
3319                yield store, LocationSection(section, self.location)
3320                continue
3321            section_path = section.id
3322            if section_path.startswith('file://'):
3323                # the location is already a local path or URL, convert the
3324                # section id to the same format
3325                section_path = urlutils.local_path_from_url(section_path)
3326            if (self.location.startswith(section_path) or
3327                    fnmatch.fnmatch(self.location, section_path)):
3328                section_parts = section_path.rstrip('/').split('/')
3329                extra_path = '/'.join(location_parts[len(section_parts):])
3330                yield store, LocationSection(section, extra_path)
3331
3332
3333class LocationMatcher(SectionMatcher):
3334
3335    def __init__(self, store, location):
3336        super(LocationMatcher, self).__init__(store)
3337        url, params = urlutils.split_segment_parameters(location)
3338        if location.startswith('file://'):
3339            location = urlutils.local_path_from_url(location)
3340        self.location = location
3341        branch_name = params.get('branch')
3342        if branch_name is None:
3343            self.branch_name = urlutils.basename(self.location)
3344        else:
3345            self.branch_name = urlutils.unescape(branch_name)
3346
3347    def _get_matching_sections(self):
3348        """Get all sections matching ``location``."""
3349        # We slightly diverge from LocalConfig here by allowing the no-name
3350        # section as the most generic one and the lower priority.
3351        no_name_section = None
3352        all_sections = []
3353        # Filter out the no_name_section so _iter_for_location_by_parts can be
3354        # used (it assumes all sections have a name).
3355        for _, section in self.store.get_sections():
3356            if section.id is None:
3357                no_name_section = section
3358            else:
3359                all_sections.append(section)
3360        # Unfortunately _iter_for_location_by_parts deals with section names so
3361        # we have to resync.
3362        filtered_sections = _iter_for_location_by_parts(
3363            [s.id for s in all_sections], self.location)
3364        iter_all_sections = iter(all_sections)
3365        matching_sections = []
3366        if no_name_section is not None:
3367            matching_sections.append(
3368                (0, LocationSection(no_name_section, self.location)))
3369        for section_id, extra_path, length in filtered_sections:
3370            # a section id is unique for a given store so it's safe to take the
3371            # first matching section while iterating. Also, all filtered
3372            # sections are part of 'all_sections' and will always be found
3373            # there.
3374            while True:
3375                section = next(iter_all_sections)
3376                if section_id == section.id:
3377                    section = LocationSection(section, extra_path,
3378                                              self.branch_name)
3379                    matching_sections.append((length, section))
3380                    break
3381        return matching_sections
3382
3383    def get_sections(self):
3384        # Override the default implementation as we want to change the order
3385        # We want the longest (aka more specific) locations first
3386        sections = sorted(self._get_matching_sections(),
3387                          key=lambda match: (match[0], match[1].id),
3388                          reverse=True)
3389        # Sections mentioning 'ignore_parents' restrict the selection
3390        for _, section in sections:
3391            # FIXME: We really want to use as_bool below -- vila 2011-04-07
3392            ignore = section.get('ignore_parents', None)
3393            if ignore is not None:
3394                ignore = ui.bool_from_string(ignore)
3395            if ignore:
3396                break
3397            # Finally, we have a valid section
3398            yield self.store, section
3399
3400
3401# FIXME: _shared_stores should be an attribute of a library state once a
3402# library_state object is always available.
3403_shared_stores = {}
3404_shared_stores_at_exit_installed = False
3405
3406
3407class Stack(object):
3408    """A stack of configurations where an option can be defined"""
3409
3410    def __init__(self, sections_def, store=None, mutable_section_id=None):
3411        """Creates a stack of sections with an optional store for changes.
3412
3413        :param sections_def: A list of Section or callables that returns an
3414            iterable of Section. This defines the Sections for the Stack and
3415            can be called repeatedly if needed.
3416
3417        :param store: The optional Store where modifications will be
3418            recorded. If none is specified, no modifications can be done.
3419
3420        :param mutable_section_id: The id of the MutableSection where changes
3421            are recorded. This requires the ``store`` parameter to be
3422            specified.
3423        """
3424        self.sections_def = sections_def
3425        self.store = store
3426        self.mutable_section_id = mutable_section_id
3427
3428    def iter_sections(self):
3429        """Iterate all the defined sections."""
3430        # Ensuring lazy loading is achieved by delaying section matching (which
3431        # implies querying the persistent storage) until it can't be avoided
3432        # anymore by using callables to describe (possibly empty) section
3433        # lists.
3434        for sections in self.sections_def:
3435            for store, section in sections():
3436                yield store, section
3437
3438    def get(self, name, expand=True, convert=True):
3439        """Return the *first* option value found in the sections.
3440
3441        This is where we guarantee that sections coming from Store are loaded
3442        lazily: the loading is delayed until we need to either check that an
3443        option exists or get its value, which in turn may require to discover
3444        in which sections it can be defined. Both of these (section and option
3445        existence) require loading the store (even partially).
3446
3447        :param name: The queried option.
3448
3449        :param expand: Whether options references should be expanded.
3450
3451        :param convert: Whether the option value should be converted from
3452            unicode (do nothing for non-registered options).
3453
3454        :returns: The value of the option.
3455        """
3456        # FIXME: No caching of options nor sections yet -- vila 20110503
3457        value = None
3458        found_store = None  # Where the option value has been found
3459        # If the option is registered, it may provide additional info about
3460        # value handling
3461        try:
3462            opt = option_registry.get(name)
3463        except KeyError:
3464            # Not registered
3465            opt = None
3466
3467        def expand_and_convert(val):
3468            # This may need to be called in different contexts if the value is
3469            # None or ends up being None during expansion or conversion.
3470            if val is not None:
3471                if expand:
3472                    if isinstance(val, str):
3473                        val = self._expand_options_in_string(val)
3474                    else:
3475                        trace.warning('Cannot expand "%s":'
3476                                      ' %s does not support option expansion'
3477                                      % (name, type(val)))
3478                if opt is None:
3479                    val = found_store.unquote(val)
3480                elif convert:
3481                    val = opt.convert_from_unicode(found_store, val)
3482            return val
3483
3484        # First of all, check if the environment can override the configuration
3485        # value
3486        if opt is not None and opt.override_from_env:
3487            value = opt.get_override()
3488            value = expand_and_convert(value)
3489        if value is None:
3490            for store, section in self.iter_sections():
3491                value = section.get(name)
3492                if value is not None:
3493                    found_store = store
3494                    break
3495            value = expand_and_convert(value)
3496            if opt is not None and value is None:
3497                # If the option is registered, it may provide a default value
3498                value = opt.get_default()
3499                value = expand_and_convert(value)
3500        for hook in ConfigHooks['get']:
3501            hook(self, name, value)
3502        return value
3503
3504    def expand_options(self, string, env=None):
3505        """Expand option references in the string in the configuration context.
3506
3507        :param string: The string containing option(s) to expand.
3508
3509        :param env: An option dict defining additional configuration options or
3510            overriding existing ones.
3511
3512        :returns: The expanded string.
3513        """
3514        return self._expand_options_in_string(string, env)
3515
3516    def _expand_options_in_string(self, string, env=None, _refs=None):
3517        """Expand options in the string in the configuration context.
3518
3519        :param string: The string to be expanded.
3520
3521        :param env: An option dict defining additional configuration options or
3522            overriding existing ones.
3523
3524        :param _refs: Private list (FIFO) containing the options being expanded
3525            to detect loops.
3526
3527        :returns: The expanded string.
3528        """
3529        if string is None:
3530            # Not much to expand there
3531            return None
3532        if _refs is None:
3533            # What references are currently resolved (to detect loops)
3534            _refs = []
3535        result = string
3536        # We need to iterate until no more refs appear ({{foo}} will need two
3537        # iterations for example).
3538        expanded = True
3539        while expanded:
3540            expanded = False
3541            chunks = []
3542            for is_ref, chunk in iter_option_refs(result):
3543                if not is_ref:
3544                    chunks.append(chunk)
3545                else:
3546                    expanded = True
3547                    name = chunk[1:-1]
3548                    if name in _refs:
3549                        raise OptionExpansionLoop(string, _refs)
3550                    _refs.append(name)
3551                    value = self._expand_option(name, env, _refs)
3552                    if value is None:
3553                        raise ExpandingUnknownOption(name, string)
3554                    chunks.append(value)
3555                    _refs.pop()
3556            result = ''.join(chunks)
3557        return result
3558
3559    def _expand_option(self, name, env, _refs):
3560        if env is not None and name in env:
3561            # Special case, values provided in env takes precedence over
3562            # anything else
3563            value = env[name]
3564        else:
3565            value = self.get(name, expand=False, convert=False)
3566            value = self._expand_options_in_string(value, env, _refs)
3567        return value
3568
3569    def _get_mutable_section(self):
3570        """Get the MutableSection for the Stack.
3571
3572        This is where we guarantee that the mutable section is lazily loaded:
3573        this means we won't load the corresponding store before setting a value
3574        or deleting an option. In practice the store will often be loaded but
3575        this helps catching some programming errors.
3576        """
3577        store = self.store
3578        section = store.get_mutable_section(self.mutable_section_id)
3579        return store, section
3580
3581    def set(self, name, value):
3582        """Set a new value for the option."""
3583        store, section = self._get_mutable_section()
3584        section.set(name, store.quote(value))
3585        for hook in ConfigHooks['set']:
3586            hook(self, name, value)
3587
3588    def remove(self, name):
3589        """Remove an existing option."""
3590        _, section = self._get_mutable_section()
3591        section.remove(name)
3592        for hook in ConfigHooks['remove']:
3593            hook(self, name)
3594
3595    def __repr__(self):
3596        # Mostly for debugging use
3597        return "<config.%s(%s)>" % (self.__class__.__name__, id(self))
3598
3599    def _get_overrides(self):
3600        if breezy._global_state is not None:
3601            # TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state()
3602            return breezy._global_state.cmdline_overrides.get_sections()
3603        return []
3604
3605    def get_shared_store(self, store, state=None):
3606        """Get a known shared store.
3607
3608        Store urls uniquely identify them and are used to ensure a single copy
3609        is shared across all users.
3610
3611        :param store: The store known to the caller.
3612
3613        :param state: The library state where the known stores are kept.
3614
3615        :returns: The store received if it's not a known one, an already known
3616            otherwise.
3617        """
3618        if state is None:
3619            # TODO(jelmer): Urgh, this is circular so we can't call breezy.get_global_state()
3620            state = breezy._global_state
3621        if state is None:
3622            global _shared_stores_at_exit_installed
3623            stores = _shared_stores
3624
3625            def save_config_changes():
3626                for k, store in stores.items():
3627                    store.save_changes()
3628            if not _shared_stores_at_exit_installed:
3629                # FIXME: Ugly hack waiting for library_state to always be
3630                # available. -- vila 20120731
3631                import atexit
3632                atexit.register(save_config_changes)
3633                _shared_stores_at_exit_installed = True
3634        else:
3635            stores = state.config_stores
3636        url = store.external_url()
3637        try:
3638            return stores[url]
3639        except KeyError:
3640            stores[url] = store
3641            return store
3642
3643
3644class MemoryStack(Stack):
3645    """A configuration stack defined from a string.
3646
3647    This is mainly intended for tests and requires no disk resources.
3648    """
3649
3650    def __init__(self, content=None):
3651        """Create an in-memory stack from a given content.
3652
3653        It uses a single store based on configobj and support reading and
3654        writing options.
3655
3656        :param content: The initial content of the store. If None, the store is
3657            not loaded and ``_load_from_string`` can and should be used if
3658            needed.
3659        """
3660        store = IniFileStore()
3661        if content is not None:
3662            store._load_from_string(content)
3663        super(MemoryStack, self).__init__(
3664            [store.get_sections], store)
3665
3666
3667class _CompatibleStack(Stack):
3668    """Place holder for compatibility with previous design.
3669
3670    This is intended to ease the transition from the Config-based design to the
3671    Stack-based design and should not be used nor relied upon by plugins.
3672
3673    One assumption made here is that the daughter classes will all use Stores
3674    derived from LockableIniFileStore).
3675
3676    It implements set() and remove () by re-loading the store before applying
3677    the modification and saving it.
3678
3679    The long term plan being to implement a single write by store to save
3680    all modifications, this class should not be used in the interim.
3681    """
3682
3683    def set(self, name, value):
3684        # Force a reload
3685        self.store.unload()
3686        super(_CompatibleStack, self).set(name, value)
3687        # Force a write to persistent storage
3688        self.store.save()
3689
3690    def remove(self, name):
3691        # Force a reload
3692        self.store.unload()
3693        super(_CompatibleStack, self).remove(name)
3694        # Force a write to persistent storage
3695        self.store.save()
3696
3697
3698class GlobalStack(Stack):
3699    """Global options only stack.
3700
3701    The following sections are queried:
3702
3703    * command-line overrides,
3704
3705    * the 'DEFAULT' section in bazaar.conf
3706
3707    This stack will use the ``DEFAULT`` section in bazaar.conf as its
3708    MutableSection.
3709    """
3710
3711    def __init__(self):
3712        gstore = self.get_shared_store(GlobalStore())
3713        super(GlobalStack, self).__init__(
3714            [self._get_overrides,
3715             NameMatcher(gstore, 'DEFAULT').get_sections],
3716            gstore, mutable_section_id='DEFAULT')
3717
3718
3719class LocationStack(Stack):
3720    """Per-location options falling back to global options stack.
3721
3722
3723    The following sections are queried:
3724
3725    * command-line overrides,
3726
3727    * the sections matching ``location`` in ``locations.conf``, the order being
3728      defined by the number of path components in the section glob, higher
3729      numbers first (from most specific section to most generic).
3730
3731    * the 'DEFAULT' section in bazaar.conf
3732
3733    This stack will use the ``location`` section in locations.conf as its
3734    MutableSection.
3735    """
3736
3737    def __init__(self, location):
3738        """Make a new stack for a location and global configuration.
3739
3740        :param location: A URL prefix to """
3741        lstore = self.get_shared_store(LocationStore())
3742        if location.startswith('file://'):
3743            location = urlutils.local_path_from_url(location)
3744        gstore = self.get_shared_store(GlobalStore())
3745        super(LocationStack, self).__init__(
3746            [self._get_overrides,
3747             LocationMatcher(lstore, location).get_sections,
3748             NameMatcher(gstore, 'DEFAULT').get_sections],
3749            lstore, mutable_section_id=location)
3750
3751
3752class BranchStack(Stack):
3753    """Per-location options falling back to branch then global options stack.
3754
3755    The following sections are queried:
3756
3757    * command-line overrides,
3758
3759    * the sections matching ``location`` in ``locations.conf``, the order being
3760      defined by the number of path components in the section glob, higher
3761      numbers first (from most specific section to most generic),
3762
3763    * the no-name section in branch.conf,
3764
3765    * the ``DEFAULT`` section in ``bazaar.conf``.
3766
3767    This stack will use the no-name section in ``branch.conf`` as its
3768    MutableSection.
3769    """
3770
3771    def __init__(self, branch):
3772        lstore = self.get_shared_store(LocationStore())
3773        bstore = branch._get_config_store()
3774        gstore = self.get_shared_store(GlobalStore())
3775        super(BranchStack, self).__init__(
3776            [self._get_overrides,
3777             LocationMatcher(lstore, branch.base).get_sections,
3778             NameMatcher(bstore, None).get_sections,
3779             NameMatcher(gstore, 'DEFAULT').get_sections],
3780            bstore)
3781        self.branch = branch
3782
3783    def lock_write(self, token=None):
3784        return self.branch.lock_write(token)
3785
3786    def unlock(self):
3787        return self.branch.unlock()
3788
3789    def set(self, name, value):
3790        with self.lock_write():
3791            super(BranchStack, self).set(name, value)
3792            # Unlocking the branch will trigger a store.save_changes() so the
3793            # last unlock saves all the changes.
3794
3795    def remove(self, name):
3796        with self.lock_write():
3797            super(BranchStack, self).remove(name)
3798            # Unlocking the branch will trigger a store.save_changes() so the
3799            # last unlock saves all the changes.
3800
3801
3802class RemoteControlStack(Stack):
3803    """Remote control-only options stack."""
3804
3805    # FIXME 2011-11-22 JRV This should probably be renamed to avoid confusion
3806    # with the stack used for remote bzr dirs. RemoteControlStack only uses
3807    # control.conf and is used only for stack options.
3808
3809    def __init__(self, bzrdir):
3810        cstore = bzrdir._get_config_store()
3811        super(RemoteControlStack, self).__init__(
3812            [NameMatcher(cstore, None).get_sections],
3813            cstore)
3814        self.controldir = bzrdir
3815
3816
3817class BranchOnlyStack(Stack):
3818    """Branch-only options stack."""
3819
3820    # FIXME: _BranchOnlyStack only uses branch.conf and is used only for the
3821    # stacked_on_location options waiting for http://pad.lv/832042 to be fixed.
3822    # -- vila 2011-12-16
3823
3824    def __init__(self, branch):
3825        bstore = branch._get_config_store()
3826        super(BranchOnlyStack, self).__init__(
3827            [NameMatcher(bstore, None).get_sections],
3828            bstore)
3829        self.branch = branch
3830
3831    def lock_write(self, token=None):
3832        return self.branch.lock_write(token)
3833
3834    def unlock(self):
3835        return self.branch.unlock()
3836
3837    def set(self, name, value):
3838        with self.lock_write():
3839            super(BranchOnlyStack, self).set(name, value)
3840            # Force a write to persistent storage
3841            self.store.save_changes()
3842
3843    def remove(self, name):
3844        with self.lock_write():
3845            super(BranchOnlyStack, self).remove(name)
3846            # Force a write to persistent storage
3847            self.store.save_changes()
3848
3849
3850class cmd_config(commands.Command):
3851    __doc__ = """Display, set or remove a configuration option.
3852
3853    Display the active value for option NAME.
3854
3855    If --all is specified, NAME is interpreted as a regular expression and all
3856    matching options are displayed mentioning their scope and without resolving
3857    option references in the value). The active value that bzr will take into
3858    account is the first one displayed for each option.
3859
3860    If NAME is not given, --all .* is implied (all options are displayed for the
3861    current scope).
3862
3863    Setting a value is achieved by using NAME=value without spaces. The value
3864    is set in the most relevant scope and can be checked by displaying the
3865    option again.
3866
3867    Removing a value is achieved by using --remove NAME.
3868    """
3869
3870    takes_args = ['name?']
3871
3872    takes_options = [
3873        'directory',
3874        # FIXME: This should be a registry option so that plugins can register
3875        # their own config files (or not) and will also address
3876        # http://pad.lv/788991 -- vila 20101115
3877        commands.Option('scope', help='Reduce the scope to the specified'
3878                        ' configuration file.',
3879                        type=str),
3880        commands.Option('all',
3881                        help='Display all the defined values for the matching options.',
3882                        ),
3883        commands.Option('remove', help='Remove the option from'
3884                        ' the configuration file.'),
3885        ]
3886
3887    _see_also = ['configuration']
3888
3889    @commands.display_command
3890    def run(self, name=None, all=False, directory=None, scope=None,
3891            remove=False):
3892        if directory is None:
3893            directory = '.'
3894        directory = directory_service.directories.dereference(directory)
3895        directory = urlutils.normalize_url(directory)
3896        if remove and all:
3897            raise errors.BzrError(
3898                '--all and --remove are mutually exclusive.')
3899        elif remove:
3900            # Delete the option in the given scope
3901            self._remove_config_option(name, directory, scope)
3902        elif name is None:
3903            # Defaults to all options
3904            self._show_matching_options('.*', directory, scope)
3905        else:
3906            try:
3907                name, value = name.split('=', 1)
3908            except ValueError:
3909                # Display the option(s) value(s)
3910                if all:
3911                    self._show_matching_options(name, directory, scope)
3912                else:
3913                    self._show_value(name, directory, scope)
3914            else:
3915                if all:
3916                    raise errors.BzrError(
3917                        'Only one option can be set.')
3918                # Set the option value
3919                self._set_config_option(name, value, directory, scope)
3920
3921    def _get_stack(self, directory, scope=None, write_access=False):
3922        """Get the configuration stack specified by ``directory`` and ``scope``.
3923
3924        :param directory: Where the configurations are derived from.
3925
3926        :param scope: A specific config to start from.
3927
3928        :param write_access: Whether a write access to the stack will be
3929            attempted.
3930        """
3931        # FIXME: scope should allow access to plugin-specific stacks (even
3932        # reduced to the plugin-specific store), related to
3933        # http://pad.lv/788991 -- vila 2011-11-15
3934        if scope is not None:
3935            if scope == 'breezy':
3936                return GlobalStack()
3937            elif scope == 'locations':
3938                return LocationStack(directory)
3939            elif scope == 'branch':
3940                (_, br, _) = (
3941                    controldir.ControlDir.open_containing_tree_or_branch(
3942                        directory))
3943                if write_access:
3944                    self.add_cleanup(br.lock_write().unlock)
3945                return br.get_config_stack()
3946            raise NoSuchConfig(scope)
3947        else:
3948            try:
3949                (_, br, _) = (
3950                    controldir.ControlDir.open_containing_tree_or_branch(
3951                        directory))
3952                if write_access:
3953                    self.add_cleanup(br.lock_write().unlock)
3954                return br.get_config_stack()
3955            except errors.NotBranchError:
3956                return LocationStack(directory)
3957
3958    def _quote_multiline(self, value):
3959        if '\n' in value:
3960            value = '"""' + value + '"""'
3961        return value
3962
3963    def _show_value(self, name, directory, scope):
3964        conf = self._get_stack(directory, scope)
3965        value = conf.get(name, expand=True, convert=False)
3966        if value is not None:
3967            # Quote the value appropriately
3968            value = self._quote_multiline(value)
3969            self.outf.write('%s\n' % (value,))
3970        else:
3971            raise NoSuchConfigOption(name)
3972
3973    def _show_matching_options(self, name, directory, scope):
3974        name = lazy_regex.lazy_compile(name)
3975        # We want any error in the regexp to be raised *now* so we need to
3976        # avoid the delay introduced by the lazy regexp.  But, we still do
3977        # want the nicer errors raised by lazy_regex.
3978        name._compile_and_collapse()
3979        cur_store_id = None
3980        cur_section = None
3981        conf = self._get_stack(directory, scope)
3982        for store, section in conf.iter_sections():
3983            for oname in section.iter_option_names():
3984                if name.search(oname):
3985                    if cur_store_id != store.id:
3986                        # Explain where the options are defined
3987                        self.outf.write('%s:\n' % (store.id,))
3988                        cur_store_id = store.id
3989                        cur_section = None
3990                    if (section.id is not None and cur_section != section.id):
3991                        # Display the section id as it appears in the store
3992                        # (None doesn't appear by definition)
3993                        self.outf.write('  [%s]\n' % (section.id,))
3994                        cur_section = section.id
3995                    value = section.get(oname, expand=False)
3996                    # Quote the value appropriately
3997                    value = self._quote_multiline(value)
3998                    self.outf.write('  %s = %s\n' % (oname, value))
3999
4000    def _set_config_option(self, name, value, directory, scope):
4001        conf = self._get_stack(directory, scope, write_access=True)
4002        conf.set(name, value)
4003        # Explicitly save the changes
4004        conf.store.save_changes()
4005
4006    def _remove_config_option(self, name, directory, scope):
4007        if name is None:
4008            raise errors.CommandError(
4009                '--remove expects an option to remove.')
4010        conf = self._get_stack(directory, scope, write_access=True)
4011        try:
4012            conf.remove(name)
4013            # Explicitly save the changes
4014            conf.store.save_changes()
4015        except KeyError:
4016            raise NoSuchConfigOption(name)
4017
4018
4019# Test registries
4020#
4021# We need adapters that can build a Store or a Stack in a test context. Test
4022# classes, based on TestCaseWithTransport, can use the registry to parametrize
4023# themselves. The builder will receive a test instance and should return a
4024# ready-to-use store or stack.  Plugins that define new store/stacks can also
4025# register themselves here to be tested against the tests defined in
4026# breezy.tests.test_config. Note that the builder can be called multiple times
4027# for the same test.
4028
4029# The registered object should be a callable receiving a test instance
4030# parameter (inheriting from tests.TestCaseWithTransport) and returning a Store
4031# object.
4032test_store_builder_registry = registry.Registry()
4033
4034# The registered object should be a callable receiving a test instance
4035# parameter (inheriting from tests.TestCaseWithTransport) and returning a Stack
4036# object.
4037test_stack_builder_registry = registry.Registry()
4038