/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Aaron Bentley
  • Date: 2009-10-23 03:35:32 UTC
  • mto: (4603.1.22 shelve-editor)
  • mto: This revision was merged to the branch mainline in revision 4795.
  • Revision ID: aaron@aaronbentley.com-20091023033532-exo2sfgc3rn1e434
Remove test code.

Show diffs side-by-side

added added

removed removed

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