/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: Canonical.com Patch Queue Manager
  • Date: 2008-09-26 05:14:51 UTC
  • mfrom: (3737.1.3 trivial_python_compat)
  • Revision ID: pqm@pqm.ubuntu.com-20080926051451-dvc1qg5inn7msjvr
(jam) Some win32 tweaks for the faster iter_changes code.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2007 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
    symbol_versioning,
 
82
    trace,
 
83
    ui,
 
84
    urlutils,
 
85
    win32utils,
 
86
    )
 
87
from bzrlib.util.configobj import configobj
 
88
""")
 
89
 
 
90
 
 
91
CHECK_IF_POSSIBLE=0
 
92
CHECK_ALWAYS=1
 
93
CHECK_NEVER=2
 
94
 
 
95
 
 
96
SIGN_WHEN_REQUIRED=0
 
97
SIGN_ALWAYS=1
 
98
SIGN_NEVER=2
 
99
 
 
100
 
 
101
POLICY_NONE = 0
 
102
POLICY_NORECURSE = 1
 
103
POLICY_APPENDPATH = 2
 
104
 
 
105
_policy_name = {
 
106
    POLICY_NONE: None,
 
107
    POLICY_NORECURSE: 'norecurse',
 
108
    POLICY_APPENDPATH: 'appendpath',
 
109
    }
 
110
_policy_value = {
 
111
    None: POLICY_NONE,
 
112
    'none': POLICY_NONE,
 
113
    'norecurse': POLICY_NORECURSE,
 
114
    'appendpath': POLICY_APPENDPATH,
 
115
    }
 
116
 
 
117
 
 
118
STORE_LOCATION = POLICY_NONE
 
119
STORE_LOCATION_NORECURSE = POLICY_NORECURSE
 
120
STORE_LOCATION_APPENDPATH = POLICY_APPENDPATH
 
121
STORE_BRANCH = 3
 
122
STORE_GLOBAL = 4
 
123
 
 
124
 
 
125
class ConfigObj(configobj.ConfigObj):
 
126
 
 
127
    def get_bool(self, section, key):
 
128
        return self[section].as_bool(key)
 
129
 
 
130
    def get_value(self, section, name):
 
131
        # Try [] for the old DEFAULT section.
 
132
        if section == "DEFAULT":
 
133
            try:
 
134
                return self[name]
 
135
            except KeyError:
 
136
                pass
 
137
        return self[section][name]
 
138
 
 
139
 
 
140
class Config(object):
 
141
    """A configuration policy - what username, editor, gpg needs etc."""
 
142
 
 
143
    def get_editor(self):
 
144
        """Get the users pop up editor."""
 
145
        raise NotImplementedError
 
146
 
 
147
    def get_mail_client(self):
 
148
        """Get a mail client to use"""
 
149
        selected_client = self.get_user_option('mail_client')
 
150
        _registry = mail_client.mail_client_registry
 
151
        try:
 
152
            mail_client_class = _registry.get(selected_client)
 
153
        except KeyError:
 
154
            raise errors.UnknownMailClient(selected_client)
 
155
        return mail_client_class(self)
 
156
 
 
157
    def _get_signature_checking(self):
 
158
        """Template method to override signature checking policy."""
 
159
 
 
160
    def _get_signing_policy(self):
 
161
        """Template method to override signature creation policy."""
 
162
 
 
163
    def _get_user_option(self, option_name):
 
164
        """Template method to provide a user option."""
 
165
        return None
 
166
 
 
167
    def get_user_option(self, option_name):
 
168
        """Get a generic option - no special process, no default."""
 
169
        return self._get_user_option(option_name)
 
170
 
 
171
    def gpg_signing_command(self):
 
172
        """What program should be used to sign signatures?"""
 
173
        result = self._gpg_signing_command()
 
174
        if result is None:
 
175
            result = "gpg"
 
176
        return result
 
177
 
 
178
    def _gpg_signing_command(self):
 
179
        """See gpg_signing_command()."""
 
180
        return None
 
181
 
 
182
    def log_format(self):
 
183
        """What log format should be used"""
 
184
        result = self._log_format()
 
185
        if result is None:
 
186
            result = "long"
 
187
        return result
 
188
 
 
189
    def _log_format(self):
 
190
        """See log_format()."""
 
191
        return None
 
192
 
 
193
    def __init__(self):
 
194
        super(Config, self).__init__()
 
195
 
 
196
    def post_commit(self):
 
197
        """An ordered list of python functions to call.
 
198
 
 
199
        Each function takes branch, rev_id as parameters.
 
200
        """
 
201
        return self._post_commit()
 
202
 
 
203
    def _post_commit(self):
 
204
        """See Config.post_commit."""
 
205
        return None
 
206
 
 
207
    def user_email(self):
 
208
        """Return just the email component of a username."""
 
209
        return extract_email_address(self.username())
 
210
 
 
211
    def username(self):
 
212
        """Return email-style username.
 
213
    
 
214
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
215
        
 
216
        $BZR_EMAIL can be set to override this (as well as the
 
217
        deprecated $BZREMAIL), then
 
218
        the concrete policy type is checked, and finally
 
219
        $EMAIL is examined.
 
220
        If none is found, a reasonable default is (hopefully)
 
221
        created.
 
222
    
 
223
        TODO: Check it's reasonably well-formed.
 
224
        """
 
225
        v = os.environ.get('BZR_EMAIL')
 
226
        if v:
 
227
            return v.decode(bzrlib.user_encoding)
 
228
 
 
229
        v = self._get_user_id()
 
230
        if v:
 
231
            return v
 
232
 
 
233
        v = os.environ.get('EMAIL')
 
234
        if v:
 
235
            return v.decode(bzrlib.user_encoding)
 
236
 
 
237
        name, email = _auto_user_id()
 
238
        if name:
 
239
            return '%s <%s>' % (name, email)
 
240
        else:
 
241
            return email
 
242
 
 
243
    def signature_checking(self):
 
244
        """What is the current policy for signature checking?."""
 
245
        policy = self._get_signature_checking()
 
246
        if policy is not None:
 
247
            return policy
 
248
        return CHECK_IF_POSSIBLE
 
249
 
 
250
    def signing_policy(self):
 
251
        """What is the current policy for signature checking?."""
 
252
        policy = self._get_signing_policy()
 
253
        if policy is not None:
 
254
            return policy
 
255
        return SIGN_WHEN_REQUIRED
 
256
 
 
257
    def signature_needed(self):
 
258
        """Is a signature needed when committing ?."""
 
259
        policy = self._get_signing_policy()
 
260
        if policy is None:
 
261
            policy = self._get_signature_checking()
 
262
            if policy is not None:
 
263
                trace.warning("Please use create_signatures,"
 
264
                              " not check_signatures to set signing policy.")
 
265
            if policy == CHECK_ALWAYS:
 
266
                return True
 
267
        elif policy == SIGN_ALWAYS:
 
268
            return True
 
269
        return False
 
270
 
 
271
    def get_alias(self, value):
 
272
        return self._get_alias(value)
 
273
 
 
274
    def _get_alias(self, value):
 
275
        pass
 
276
 
 
277
    def get_nickname(self):
 
278
        return self._get_nickname()
 
279
 
 
280
    def _get_nickname(self):
 
281
        return None
 
282
 
 
283
    def get_bzr_remote_path(self):
 
284
        try:
 
285
            return os.environ['BZR_REMOTE_PATH']
 
286
        except KeyError:
 
287
            path = self.get_user_option("bzr_remote_path")
 
288
            if path is None:
 
289
                path = 'bzr'
 
290
            return path
 
291
 
 
292
 
 
293
class IniBasedConfig(Config):
 
294
    """A configuration policy that draws from ini files."""
 
295
 
 
296
    def _get_parser(self, file=None):
 
297
        if self._parser is not None:
 
298
            return self._parser
 
299
        if file is None:
 
300
            input = self._get_filename()
 
301
        else:
 
302
            input = file
 
303
        try:
 
304
            self._parser = ConfigObj(input, encoding='utf-8')
 
305
        except configobj.ConfigObjError, e:
 
306
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
307
        return self._parser
 
308
 
 
309
    def _get_matching_sections(self):
 
310
        """Return an ordered list of (section_name, extra_path) pairs.
 
311
 
 
312
        If the section contains inherited configuration, extra_path is
 
313
        a string containing the additional path components.
 
314
        """
 
315
        section = self._get_section()
 
316
        if section is not None:
 
317
            return [(section, '')]
 
318
        else:
 
319
            return []
 
320
 
 
321
    def _get_section(self):
 
322
        """Override this to define the section used by the config."""
 
323
        return "DEFAULT"
 
324
 
 
325
    def _get_option_policy(self, section, option_name):
 
326
        """Return the policy for the given (section, option_name) pair."""
 
327
        return POLICY_NONE
 
328
 
 
329
    def _get_signature_checking(self):
 
330
        """See Config._get_signature_checking."""
 
331
        policy = self._get_user_option('check_signatures')
 
332
        if policy:
 
333
            return self._string_to_signature_policy(policy)
 
334
 
 
335
    def _get_signing_policy(self):
 
336
        """See Config._get_signing_policy"""
 
337
        policy = self._get_user_option('create_signatures')
 
338
        if policy:
 
339
            return self._string_to_signing_policy(policy)
 
340
 
 
341
    def _get_user_id(self):
 
342
        """Get the user id from the 'email' key in the current section."""
 
343
        return self._get_user_option('email')
 
344
 
 
345
    def _get_user_option(self, option_name):
 
346
        """See Config._get_user_option."""
 
347
        for (section, extra_path) in self._get_matching_sections():
 
348
            try:
 
349
                value = self._get_parser().get_value(section, option_name)
 
350
            except KeyError:
 
351
                continue
 
352
            policy = self._get_option_policy(section, option_name)
 
353
            if policy == POLICY_NONE:
 
354
                return value
 
355
            elif policy == POLICY_NORECURSE:
 
356
                # norecurse items only apply to the exact path
 
357
                if extra_path:
 
358
                    continue
 
359
                else:
 
360
                    return value
 
361
            elif policy == POLICY_APPENDPATH:
 
362
                if extra_path:
 
363
                    value = urlutils.join(value, extra_path)
 
364
                return value
 
365
            else:
 
366
                raise AssertionError('Unexpected config policy %r' % policy)
 
367
        else:
 
368
            return None
 
369
 
 
370
    def _gpg_signing_command(self):
 
371
        """See Config.gpg_signing_command."""
 
372
        return self._get_user_option('gpg_signing_command')
 
373
 
 
374
    def _log_format(self):
 
375
        """See Config.log_format."""
 
376
        return self._get_user_option('log_format')
 
377
 
 
378
    def __init__(self, get_filename):
 
379
        super(IniBasedConfig, self).__init__()
 
380
        self._get_filename = get_filename
 
381
        self._parser = None
 
382
        
 
383
    def _post_commit(self):
 
384
        """See Config.post_commit."""
 
385
        return self._get_user_option('post_commit')
 
386
 
 
387
    def _string_to_signature_policy(self, signature_string):
 
388
        """Convert a string to a signing policy."""
 
389
        if signature_string.lower() == 'check-available':
 
390
            return CHECK_IF_POSSIBLE
 
391
        if signature_string.lower() == 'ignore':
 
392
            return CHECK_NEVER
 
393
        if signature_string.lower() == 'require':
 
394
            return CHECK_ALWAYS
 
395
        raise errors.BzrError("Invalid signatures policy '%s'"
 
396
                              % signature_string)
 
397
 
 
398
    def _string_to_signing_policy(self, signature_string):
 
399
        """Convert a string to a signing policy."""
 
400
        if signature_string.lower() == 'when-required':
 
401
            return SIGN_WHEN_REQUIRED
 
402
        if signature_string.lower() == 'never':
 
403
            return SIGN_NEVER
 
404
        if signature_string.lower() == 'always':
 
405
            return SIGN_ALWAYS
 
406
        raise errors.BzrError("Invalid signing policy '%s'"
 
407
                              % signature_string)
 
408
 
 
409
    def _get_alias(self, value):
 
410
        try:
 
411
            return self._get_parser().get_value("ALIASES", 
 
412
                                                value)
 
413
        except KeyError:
 
414
            pass
 
415
 
 
416
    def _get_nickname(self):
 
417
        return self.get_user_option('nickname')
 
418
 
 
419
 
 
420
class GlobalConfig(IniBasedConfig):
 
421
    """The configuration that should be used for a specific location."""
 
422
 
 
423
    def get_editor(self):
 
424
        return self._get_user_option('editor')
 
425
 
 
426
    def __init__(self):
 
427
        super(GlobalConfig, self).__init__(config_filename)
 
428
 
 
429
    def set_user_option(self, option, value):
 
430
        """Save option and its value in the configuration."""
 
431
        self._set_option(option, value, 'DEFAULT')
 
432
 
 
433
    def get_aliases(self):
 
434
        """Return the aliases section."""
 
435
        if 'ALIASES' in self._get_parser():
 
436
            return self._get_parser()['ALIASES']
 
437
        else:
 
438
            return {}
 
439
 
 
440
    def set_alias(self, alias_name, alias_command):
 
441
        """Save the alias in the configuration."""
 
442
        self._set_option(alias_name, alias_command, 'ALIASES')
 
443
 
 
444
    def unset_alias(self, alias_name):
 
445
        """Unset an existing alias."""
 
446
        aliases = self._get_parser().get('ALIASES')
 
447
        if not aliases or alias_name not in aliases:
 
448
            raise errors.NoSuchAlias(alias_name)
 
449
        del aliases[alias_name]
 
450
        self._write_config_file()
 
451
 
 
452
    def _set_option(self, option, value, section):
 
453
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
454
        # file lock on bazaar.conf.
 
455
        conf_dir = os.path.dirname(self._get_filename())
 
456
        ensure_config_dir_exists(conf_dir)
 
457
        self._get_parser().setdefault(section, {})[option] = value
 
458
        self._write_config_file()
 
459
 
 
460
    def _write_config_file(self):
 
461
        f = open(self._get_filename(), 'wb')
 
462
        self._get_parser().write(f)
 
463
        f.close()
 
464
 
 
465
 
 
466
class LocationConfig(IniBasedConfig):
 
467
    """A configuration object that gives the policy for a location."""
 
468
 
 
469
    def __init__(self, location):
 
470
        name_generator = locations_config_filename
 
471
        if (not os.path.exists(name_generator()) and
 
472
                os.path.exists(branches_config_filename())):
 
473
            if sys.platform == 'win32':
 
474
                trace.warning('Please rename %s to %s'
 
475
                              % (branches_config_filename(),
 
476
                                 locations_config_filename()))
 
477
            else:
 
478
                trace.warning('Please rename ~/.bazaar/branches.conf'
 
479
                              ' to ~/.bazaar/locations.conf')
 
480
            name_generator = branches_config_filename
 
481
        super(LocationConfig, self).__init__(name_generator)
 
482
        # local file locations are looked up by local path, rather than
 
483
        # by file url. This is because the config file is a user
 
484
        # file, and we would rather not expose the user to file urls.
 
485
        if location.startswith('file://'):
 
486
            location = urlutils.local_path_from_url(location)
 
487
        self.location = location
 
488
 
 
489
    def _get_matching_sections(self):
 
490
        """Return an ordered list of section names matching this location."""
 
491
        sections = self._get_parser()
 
492
        location_names = self.location.split('/')
 
493
        if self.location.endswith('/'):
 
494
            del location_names[-1]
 
495
        matches=[]
 
496
        for section in sections:
 
497
            # location is a local path if possible, so we need
 
498
            # to convert 'file://' urls to local paths if necessary.
 
499
            # This also avoids having file:///path be a more exact
 
500
            # match than '/path'.
 
501
            if section.startswith('file://'):
 
502
                section_path = urlutils.local_path_from_url(section)
 
503
            else:
 
504
                section_path = section
 
505
            section_names = section_path.split('/')
 
506
            if section.endswith('/'):
 
507
                del section_names[-1]
 
508
            names = zip(location_names, section_names)
 
509
            matched = True
 
510
            for name in names:
 
511
                if not fnmatch(name[0], name[1]):
 
512
                    matched = False
 
513
                    break
 
514
            if not matched:
 
515
                continue
 
516
            # so, for the common prefix they matched.
 
517
            # if section is longer, no match.
 
518
            if len(section_names) > len(location_names):
 
519
                continue
 
520
            matches.append((len(section_names), section,
 
521
                            '/'.join(location_names[len(section_names):])))
 
522
        matches.sort(reverse=True)
 
523
        sections = []
 
524
        for (length, section, extra_path) in matches:
 
525
            sections.append((section, extra_path))
 
526
            # should we stop looking for parent configs here?
 
527
            try:
 
528
                if self._get_parser()[section].as_bool('ignore_parents'):
 
529
                    break
 
530
            except KeyError:
 
531
                pass
 
532
        return sections
 
533
 
 
534
    def _get_option_policy(self, section, option_name):
 
535
        """Return the policy for the given (section, option_name) pair."""
 
536
        # check for the old 'recurse=False' flag
 
537
        try:
 
538
            recurse = self._get_parser()[section].as_bool('recurse')
 
539
        except KeyError:
 
540
            recurse = True
 
541
        if not recurse:
 
542
            return POLICY_NORECURSE
 
543
 
 
544
        policy_key = option_name + ':policy'
 
545
        try:
 
546
            policy_name = self._get_parser()[section][policy_key]
 
547
        except KeyError:
 
548
            policy_name = None
 
549
 
 
550
        return _policy_value[policy_name]
 
551
 
 
552
    def _set_option_policy(self, section, option_name, option_policy):
 
553
        """Set the policy for the given option name in the given section."""
 
554
        # The old recurse=False option affects all options in the
 
555
        # section.  To handle multiple policies in the section, we
 
556
        # need to convert it to a policy_norecurse key.
 
557
        try:
 
558
            recurse = self._get_parser()[section].as_bool('recurse')
 
559
        except KeyError:
 
560
            pass
 
561
        else:
 
562
            symbol_versioning.warn(
 
563
                'The recurse option is deprecated as of 0.14.  '
 
564
                'The section "%s" has been converted to use policies.'
 
565
                % section,
 
566
                DeprecationWarning)
 
567
            del self._get_parser()[section]['recurse']
 
568
            if not recurse:
 
569
                for key in self._get_parser()[section].keys():
 
570
                    if not key.endswith(':policy'):
 
571
                        self._get_parser()[section][key +
 
572
                                                    ':policy'] = 'norecurse'
 
573
 
 
574
        policy_key = option_name + ':policy'
 
575
        policy_name = _policy_name[option_policy]
 
576
        if policy_name is not None:
 
577
            self._get_parser()[section][policy_key] = policy_name
 
578
        else:
 
579
            if policy_key in self._get_parser()[section]:
 
580
                del self._get_parser()[section][policy_key]
 
581
 
 
582
    def set_user_option(self, option, value, store=STORE_LOCATION):
 
583
        """Save option and its value in the configuration."""
 
584
        if store not in [STORE_LOCATION,
 
585
                         STORE_LOCATION_NORECURSE,
 
586
                         STORE_LOCATION_APPENDPATH]:
 
587
            raise ValueError('bad storage policy %r for %r' %
 
588
                (store, option))
 
589
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
590
        # file lock on locations.conf.
 
591
        conf_dir = os.path.dirname(self._get_filename())
 
592
        ensure_config_dir_exists(conf_dir)
 
593
        location = self.location
 
594
        if location.endswith('/'):
 
595
            location = location[:-1]
 
596
        if (not location in self._get_parser() and
 
597
            not location + '/' in self._get_parser()):
 
598
            self._get_parser()[location]={}
 
599
        elif location + '/' in self._get_parser():
 
600
            location = location + '/'
 
601
        self._get_parser()[location][option]=value
 
602
        # the allowed values of store match the config policies
 
603
        self._set_option_policy(location, option, store)
 
604
        self._get_parser().write(file(self._get_filename(), 'wb'))
 
605
 
 
606
 
 
607
class BranchConfig(Config):
 
608
    """A configuration object giving the policy for a branch."""
 
609
 
 
610
    def _get_branch_data_config(self):
 
611
        if self._branch_data_config is None:
 
612
            self._branch_data_config = TreeConfig(self.branch)
 
613
        return self._branch_data_config
 
614
 
 
615
    def _get_location_config(self):
 
616
        if self._location_config is None:
 
617
            self._location_config = LocationConfig(self.branch.base)
 
618
        return self._location_config
 
619
 
 
620
    def _get_global_config(self):
 
621
        if self._global_config is None:
 
622
            self._global_config = GlobalConfig()
 
623
        return self._global_config
 
624
 
 
625
    def _get_best_value(self, option_name):
 
626
        """This returns a user option from local, tree or global config.
 
627
 
 
628
        They are tried in that order.  Use get_safe_value if trusted values
 
629
        are necessary.
 
630
        """
 
631
        for source in self.option_sources:
 
632
            value = getattr(source(), option_name)()
 
633
            if value is not None:
 
634
                return value
 
635
        return None
 
636
 
 
637
    def _get_safe_value(self, option_name):
 
638
        """This variant of get_best_value never returns untrusted values.
 
639
        
 
640
        It does not return values from the branch data, because the branch may
 
641
        not be controlled by the user.
 
642
 
 
643
        We may wish to allow locations.conf to control whether branches are
 
644
        trusted in the future.
 
645
        """
 
646
        for source in (self._get_location_config, self._get_global_config):
 
647
            value = getattr(source(), option_name)()
 
648
            if value is not None:
 
649
                return value
 
650
        return None
 
651
 
 
652
    def _get_user_id(self):
 
653
        """Return the full user id for the branch.
 
654
    
 
655
        e.g. "John Hacker <jhacker@example.com>"
 
656
        This is looked up in the email controlfile for the branch.
 
657
        """
 
658
        try:
 
659
            return (self.branch._transport.get_bytes("email")
 
660
                    .decode(bzrlib.user_encoding)
 
661
                    .rstrip("\r\n"))
 
662
        except errors.NoSuchFile, e:
 
663
            pass
 
664
        
 
665
        return self._get_best_value('_get_user_id')
 
666
 
 
667
    def _get_signature_checking(self):
 
668
        """See Config._get_signature_checking."""
 
669
        return self._get_best_value('_get_signature_checking')
 
670
 
 
671
    def _get_signing_policy(self):
 
672
        """See Config._get_signing_policy."""
 
673
        return self._get_best_value('_get_signing_policy')
 
674
 
 
675
    def _get_user_option(self, option_name):
 
676
        """See Config._get_user_option."""
 
677
        for source in self.option_sources:
 
678
            value = source()._get_user_option(option_name)
 
679
            if value is not None:
 
680
                return value
 
681
        return None
 
682
 
 
683
    def set_user_option(self, name, value, store=STORE_BRANCH,
 
684
        warn_masked=False):
 
685
        if store == STORE_BRANCH:
 
686
            self._get_branch_data_config().set_option(value, name)
 
687
        elif store == STORE_GLOBAL:
 
688
            self._get_global_config().set_user_option(name, value)
 
689
        else:
 
690
            self._get_location_config().set_user_option(name, value, store)
 
691
        if not warn_masked:
 
692
            return
 
693
        if store in (STORE_GLOBAL, STORE_BRANCH):
 
694
            mask_value = self._get_location_config().get_user_option(name)
 
695
            if mask_value is not None:
 
696
                trace.warning('Value "%s" is masked by "%s" from'
 
697
                              ' locations.conf', value, mask_value)
 
698
            else:
 
699
                if store == STORE_GLOBAL:
 
700
                    branch_config = self._get_branch_data_config()
 
701
                    mask_value = branch_config.get_user_option(name)
 
702
                    if mask_value is not None:
 
703
                        trace.warning('Value "%s" is masked by "%s" from'
 
704
                                      ' branch.conf', value, mask_value)
 
705
 
 
706
 
 
707
    def _gpg_signing_command(self):
 
708
        """See Config.gpg_signing_command."""
 
709
        return self._get_safe_value('_gpg_signing_command')
 
710
        
 
711
    def __init__(self, branch):
 
712
        super(BranchConfig, self).__init__()
 
713
        self._location_config = None
 
714
        self._branch_data_config = None
 
715
        self._global_config = None
 
716
        self.branch = branch
 
717
        self.option_sources = (self._get_location_config, 
 
718
                               self._get_branch_data_config,
 
719
                               self._get_global_config)
 
720
 
 
721
    def _post_commit(self):
 
722
        """See Config.post_commit."""
 
723
        return self._get_safe_value('_post_commit')
 
724
 
 
725
    def _get_nickname(self):
 
726
        value = self._get_explicit_nickname()
 
727
        if value is not None:
 
728
            return value
 
729
        return urlutils.unescape(self.branch.base.split('/')[-2])
 
730
 
 
731
    def has_explicit_nickname(self):
 
732
        """Return true if a nickname has been explicitly assigned."""
 
733
        return self._get_explicit_nickname() is not None
 
734
 
 
735
    def _get_explicit_nickname(self):
 
736
        return self._get_best_value('_get_nickname')
 
737
 
 
738
    def _log_format(self):
 
739
        """See Config.log_format."""
 
740
        return self._get_best_value('_log_format')
 
741
 
 
742
 
 
743
def ensure_config_dir_exists(path=None):
 
744
    """Make sure a configuration directory exists.
 
745
    This makes sure that the directory exists.
 
746
    On windows, since configuration directories are 2 levels deep,
 
747
    it makes sure both the directory and the parent directory exists.
 
748
    """
 
749
    if path is None:
 
750
        path = config_dir()
 
751
    if not os.path.isdir(path):
 
752
        if sys.platform == 'win32':
 
753
            parent_dir = os.path.dirname(path)
 
754
            if not os.path.isdir(parent_dir):
 
755
                trace.mutter('creating config parent directory: %r', parent_dir)
 
756
            os.mkdir(parent_dir)
 
757
        trace.mutter('creating config directory: %r', path)
 
758
        os.mkdir(path)
 
759
 
 
760
 
 
761
def config_dir():
 
762
    """Return per-user configuration directory.
 
763
 
 
764
    By default this is ~/.bazaar/
 
765
    
 
766
    TODO: Global option --config-dir to override this.
 
767
    """
 
768
    base = os.environ.get('BZR_HOME', None)
 
769
    if sys.platform == 'win32':
 
770
        if base is None:
 
771
            base = win32utils.get_appdata_location_unicode()
 
772
        if base is None:
 
773
            base = os.environ.get('HOME', None)
 
774
        if base is None:
 
775
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA,'
 
776
                                  ' or HOME set')
 
777
        return osutils.pathjoin(base, 'bazaar', '2.0')
 
778
    else:
 
779
        # cygwin, linux, and darwin all have a $HOME directory
 
780
        if base is None:
 
781
            base = os.path.expanduser("~")
 
782
        return osutils.pathjoin(base, ".bazaar")
 
783
 
 
784
 
 
785
def config_filename():
 
786
    """Return per-user configuration ini file filename."""
 
787
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
 
788
 
 
789
 
 
790
def branches_config_filename():
 
791
    """Return per-user configuration ini file filename."""
 
792
    return osutils.pathjoin(config_dir(), 'branches.conf')
 
793
 
 
794
 
 
795
def locations_config_filename():
 
796
    """Return per-user configuration ini file filename."""
 
797
    return osutils.pathjoin(config_dir(), 'locations.conf')
 
798
 
 
799
 
 
800
def authentication_config_filename():
 
801
    """Return per-user authentication ini file filename."""
 
802
    return osutils.pathjoin(config_dir(), 'authentication.conf')
 
803
 
 
804
 
 
805
def user_ignore_config_filename():
 
806
    """Return the user default ignore filename"""
 
807
    return osutils.pathjoin(config_dir(), 'ignore')
 
808
 
 
809
 
 
810
def _auto_user_id():
 
811
    """Calculate automatic user identification.
 
812
 
 
813
    Returns (realname, email).
 
814
 
 
815
    Only used when none is set in the environment or the id file.
 
816
 
 
817
    This previously used the FQDN as the default domain, but that can
 
818
    be very slow on machines where DNS is broken.  So now we simply
 
819
    use the hostname.
 
820
    """
 
821
    import socket
 
822
 
 
823
    if sys.platform == 'win32':
 
824
        name = win32utils.get_user_name_unicode()
 
825
        if name is None:
 
826
            raise errors.BzrError("Cannot autodetect user name.\n"
 
827
                                  "Please, set your name with command like:\n"
 
828
                                  'bzr whoami "Your Name <name@domain.com>"')
 
829
        host = win32utils.get_host_name_unicode()
 
830
        if host is None:
 
831
            host = socket.gethostname()
 
832
        return name, (name + '@' + host)
 
833
 
 
834
    try:
 
835
        import pwd
 
836
        uid = os.getuid()
 
837
        w = pwd.getpwuid(uid)
 
838
 
 
839
        # we try utf-8 first, because on many variants (like Linux),
 
840
        # /etc/passwd "should" be in utf-8, and because it's unlikely to give
 
841
        # false positives.  (many users will have their user encoding set to
 
842
        # latin-1, which cannot raise UnicodeError.)
 
843
        try:
 
844
            gecos = w.pw_gecos.decode('utf-8')
 
845
            encoding = 'utf-8'
 
846
        except UnicodeError:
 
847
            try:
 
848
                gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
849
                encoding = bzrlib.user_encoding
 
850
            except UnicodeError:
 
851
                raise errors.BzrCommandError('Unable to determine your name.  '
 
852
                   'Use "bzr whoami" to set it.')
 
853
        try:
 
854
            username = w.pw_name.decode(encoding)
 
855
        except UnicodeError:
 
856
            raise errors.BzrCommandError('Unable to determine your name.  '
 
857
                'Use "bzr whoami" to set it.')
 
858
 
 
859
        comma = gecos.find(',')
 
860
        if comma == -1:
 
861
            realname = gecos
 
862
        else:
 
863
            realname = gecos[:comma]
 
864
        if not realname:
 
865
            realname = username
 
866
 
 
867
    except ImportError:
 
868
        import getpass
 
869
        try:
 
870
            realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
871
        except UnicodeDecodeError:
 
872
            raise errors.BzrError("Can't decode username as %s." % \
 
873
                    bzrlib.user_encoding)
 
874
 
 
875
    return realname, (username + '@' + socket.gethostname())
 
876
 
 
877
 
 
878
def parse_username(username):
 
879
    """Parse e-mail username and return a (name, address) tuple."""
 
880
    match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
 
881
    if match is None:
 
882
        return (username, '')
 
883
    else:
 
884
        return (match.group(1), match.group(2))
 
885
 
 
886
 
 
887
def extract_email_address(e):
 
888
    """Return just the address part of an email string.
 
889
 
 
890
    That is just the user@domain part, nothing else. 
 
891
    This part is required to contain only ascii characters.
 
892
    If it can't be extracted, raises an error.
 
893
 
 
894
    >>> extract_email_address('Jane Tester <jane@test.com>')
 
895
    "jane@test.com"
 
896
    """
 
897
    name, email = parse_username(e)
 
898
    if not email:
 
899
        raise errors.NoEmailInUsername(e)
 
900
    return email
 
901
 
 
902
 
 
903
class TreeConfig(IniBasedConfig):
 
904
    """Branch configuration data associated with its contents, not location"""
 
905
 
 
906
    # XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
 
907
 
 
908
    def __init__(self, branch):
 
909
        # XXX: Really this should be asking the branch for its configuration
 
910
        # data, rather than relying on a Transport, so that it can work 
 
911
        # more cleanly with a RemoteBranch that has no transport.
 
912
        self._config = TransportConfig(branch._transport, 'branch.conf')
 
913
        self.branch = branch
 
914
 
 
915
    def _get_parser(self, file=None):
 
916
        if file is not None:
 
917
            return IniBasedConfig._get_parser(file)
 
918
        return self._config._get_configobj()
 
919
 
 
920
    def get_option(self, name, section=None, default=None):
 
921
        self.branch.lock_read()
 
922
        try:
 
923
            return self._config.get_option(name, section, default)
 
924
        finally:
 
925
            self.branch.unlock()
 
926
        return result
 
927
 
 
928
    def set_option(self, value, name, section=None):
 
929
        """Set a per-branch configuration option"""
 
930
        self.branch.lock_write()
 
931
        try:
 
932
            self._config.set_option(value, name, section)
 
933
        finally:
 
934
            self.branch.unlock()
 
935
 
 
936
 
 
937
class AuthenticationConfig(object):
 
938
    """The authentication configuration file based on a ini file.
 
939
 
 
940
    Implements the authentication.conf file described in
 
941
    doc/developers/authentication-ring.txt.
 
942
    """
 
943
 
 
944
    def __init__(self, _file=None):
 
945
        self._config = None # The ConfigObj
 
946
        if _file is None:
 
947
            self._filename = authentication_config_filename()
 
948
            self._input = self._filename = authentication_config_filename()
 
949
        else:
 
950
            # Tests can provide a string as _file
 
951
            self._filename = None
 
952
            self._input = _file
 
953
 
 
954
    def _get_config(self):
 
955
        if self._config is not None:
 
956
            return self._config
 
957
        try:
 
958
            # FIXME: Should we validate something here ? Includes: empty
 
959
            # sections are useless, at least one of
 
960
            # user/password/password_encoding should be defined, etc.
 
961
 
 
962
            # Note: the encoding below declares that the file itself is utf-8
 
963
            # encoded, but the values in the ConfigObj are always Unicode.
 
964
            self._config = ConfigObj(self._input, encoding='utf-8')
 
965
        except configobj.ConfigObjError, e:
 
966
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
967
        return self._config
 
968
 
 
969
    def _save(self):
 
970
        """Save the config file, only tests should use it for now."""
 
971
        conf_dir = os.path.dirname(self._filename)
 
972
        ensure_config_dir_exists(conf_dir)
 
973
        self._get_config().write(file(self._filename, 'wb'))
 
974
 
 
975
    def _set_option(self, section_name, option_name, value):
 
976
        """Set an authentication configuration option"""
 
977
        conf = self._get_config()
 
978
        section = conf.get(section_name)
 
979
        if section is None:
 
980
            conf[section] = {}
 
981
            section = conf[section]
 
982
        section[option_name] = value
 
983
        self._save()
 
984
 
 
985
    def get_credentials(self, scheme, host, port=None, user=None, path=None):
 
986
        """Returns the matching credentials from authentication.conf file.
 
987
 
 
988
        :param scheme: protocol
 
989
 
 
990
        :param host: the server address
 
991
 
 
992
        :param port: the associated port (optional)
 
993
 
 
994
        :param user: login (optional)
 
995
 
 
996
        :param path: the absolute path on the server (optional)
 
997
 
 
998
        :return: A dict containing the matching credentials or None.
 
999
           This includes:
 
1000
           - name: the section name of the credentials in the
 
1001
             authentication.conf file,
 
1002
           - user: can't de different from the provided user if any,
 
1003
           - password: the decoded password, could be None if the credential
 
1004
             defines only the user
 
1005
           - verify_certificates: https specific, True if the server
 
1006
             certificate should be verified, False otherwise.
 
1007
        """
 
1008
        credentials = None
 
1009
        for auth_def_name, auth_def in self._get_config().items():
 
1010
            if type(auth_def) is not configobj.Section:
 
1011
                raise ValueError("%s defined outside a section" % auth_def_name)
 
1012
 
 
1013
            a_scheme, a_host, a_user, a_path = map(
 
1014
                auth_def.get, ['scheme', 'host', 'user', 'path'])
 
1015
 
 
1016
            try:
 
1017
                a_port = auth_def.as_int('port')
 
1018
            except KeyError:
 
1019
                a_port = None
 
1020
            except ValueError:
 
1021
                raise ValueError("'port' not numeric in %s" % auth_def_name)
 
1022
            try:
 
1023
                a_verify_certificates = auth_def.as_bool('verify_certificates')
 
1024
            except KeyError:
 
1025
                a_verify_certificates = True
 
1026
            except ValueError:
 
1027
                raise ValueError(
 
1028
                    "'verify_certificates' not boolean in %s" % auth_def_name)
 
1029
 
 
1030
            # Attempt matching
 
1031
            if a_scheme is not None and scheme != a_scheme:
 
1032
                continue
 
1033
            if a_host is not None:
 
1034
                if not (host == a_host
 
1035
                        or (a_host.startswith('.') and host.endswith(a_host))):
 
1036
                    continue
 
1037
            if a_port is not None and port != a_port:
 
1038
                continue
 
1039
            if (a_path is not None and path is not None
 
1040
                and not path.startswith(a_path)):
 
1041
                continue
 
1042
            if (a_user is not None and user is not None
 
1043
                and a_user != user):
 
1044
                # Never contradict the caller about the user to be used
 
1045
                continue
 
1046
            if a_user is None:
 
1047
                # Can't find a user
 
1048
                continue
 
1049
            credentials = dict(name=auth_def_name,
 
1050
                               user=a_user,
 
1051
                               password=auth_def.get('password', None),
 
1052
                               verify_certificates=a_verify_certificates)
 
1053
            self.decode_password(credentials,
 
1054
                                 auth_def.get('password_encoding', None))
 
1055
            if 'auth' in debug.debug_flags:
 
1056
                trace.mutter("Using authentication section: %r", auth_def_name)
 
1057
            break
 
1058
 
 
1059
        return credentials
 
1060
 
 
1061
    def get_user(self, scheme, host, port=None,
 
1062
                 realm=None, path=None, prompt=None):
 
1063
        """Get a user from authentication file.
 
1064
 
 
1065
        :param scheme: protocol
 
1066
 
 
1067
        :param host: the server address
 
1068
 
 
1069
        :param port: the associated port (optional)
 
1070
 
 
1071
        :param realm: the realm sent by the server (optional)
 
1072
 
 
1073
        :param path: the absolute path on the server (optional)
 
1074
 
 
1075
        :return: The found user.
 
1076
        """
 
1077
        credentials = self.get_credentials(scheme, host, port, user=None,
 
1078
                                           path=path)
 
1079
        if credentials is not None:
 
1080
            user = credentials['user']
 
1081
        else:
 
1082
            user = None
 
1083
        return user
 
1084
 
 
1085
    def get_password(self, scheme, host, user, port=None,
 
1086
                     realm=None, path=None, prompt=None):
 
1087
        """Get a password from authentication file or prompt the user for one.
 
1088
 
 
1089
        :param scheme: protocol
 
1090
 
 
1091
        :param host: the server address
 
1092
 
 
1093
        :param port: the associated port (optional)
 
1094
 
 
1095
        :param user: login
 
1096
 
 
1097
        :param realm: the realm sent by the server (optional)
 
1098
 
 
1099
        :param path: the absolute path on the server (optional)
 
1100
 
 
1101
        :return: The found password or the one entered by the user.
 
1102
        """
 
1103
        credentials = self.get_credentials(scheme, host, port, user, path)
 
1104
        if credentials is not None:
 
1105
            password = credentials['password']
 
1106
            if password is not None and scheme is 'ssh':
 
1107
                trace.warning('password ignored in section [%s],'
 
1108
                              ' use an ssh agent instead'
 
1109
                              % credentials['name'])
 
1110
                password = None
 
1111
        else:
 
1112
            password = None
 
1113
        # Prompt user only if we could't find a password
 
1114
        if password is None:
 
1115
            if prompt is None:
 
1116
                # Create a default prompt suitable for most cases
 
1117
                prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
 
1118
            # Special handling for optional fields in the prompt
 
1119
            if port is not None:
 
1120
                prompt_host = '%s:%d' % (host, port)
 
1121
            else:
 
1122
                prompt_host = host
 
1123
            password = ui.ui_factory.get_password(prompt,
 
1124
                                                  host=prompt_host, user=user)
 
1125
        return password
 
1126
 
 
1127
    def decode_password(self, credentials, encoding):
 
1128
        return credentials
 
1129
 
 
1130
 
 
1131
class BzrDirConfig(object):
 
1132
 
 
1133
    def __init__(self, transport):
 
1134
        self._config = TransportConfig(transport, 'control.conf')
 
1135
 
 
1136
    def set_default_stack_on(self, value):
 
1137
        """Set the default stacking location.
 
1138
 
 
1139
        It may be set to a location, or None.
 
1140
 
 
1141
        This policy affects all branches contained by this bzrdir, except for
 
1142
        those under repositories.
 
1143
        """
 
1144
        if value is None:
 
1145
            self._config.set_option('', 'default_stack_on')
 
1146
        else:
 
1147
            self._config.set_option(value, 'default_stack_on')
 
1148
 
 
1149
    def get_default_stack_on(self):
 
1150
        """Return the default stacking location.
 
1151
 
 
1152
        This will either be a location, or None.
 
1153
 
 
1154
        This policy affects all branches contained by this bzrdir, except for
 
1155
        those under repositories.
 
1156
        """
 
1157
        value = self._config.get_option('default_stack_on')
 
1158
        if value == '':
 
1159
            value = None
 
1160
        return value
 
1161
 
 
1162
 
 
1163
class TransportConfig(object):
 
1164
    """A Config that reads/writes a config file on a Transport.
 
1165
 
 
1166
    It is a low-level object that considers config data to be name/value pairs
 
1167
    that may be associated with a section.  Assigning meaning to the these
 
1168
    values is done at higher levels like TreeConfig.
 
1169
    """
 
1170
 
 
1171
    def __init__(self, transport, filename):
 
1172
        self._transport = transport
 
1173
        self._filename = filename
 
1174
 
 
1175
    def get_option(self, name, section=None, default=None):
 
1176
        """Return the value associated with a named option.
 
1177
 
 
1178
        :param name: The name of the value
 
1179
        :param section: The section the option is in (if any)
 
1180
        :param default: The value to return if the value is not set
 
1181
        :return: The value or default value
 
1182
        """
 
1183
        configobj = self._get_configobj()
 
1184
        if section is None:
 
1185
            section_obj = configobj
 
1186
        else:
 
1187
            try:
 
1188
                section_obj = configobj[section]
 
1189
            except KeyError:
 
1190
                return default
 
1191
        return section_obj.get(name, default)
 
1192
 
 
1193
    def set_option(self, value, name, section=None):
 
1194
        """Set the value associated with a named option.
 
1195
 
 
1196
        :param value: The value to set
 
1197
        :param name: The name of the value to set
 
1198
        :param section: The section the option is in (if any)
 
1199
        """
 
1200
        configobj = self._get_configobj()
 
1201
        if section is None:
 
1202
            configobj[name] = value
 
1203
        else:
 
1204
            configobj.setdefault(section, {})[name] = value
 
1205
        self._set_configobj(configobj)
 
1206
 
 
1207
    def _get_configobj(self):
 
1208
        try:
 
1209
            return ConfigObj(self._transport.get(self._filename),
 
1210
                             encoding='utf-8')
 
1211
        except errors.NoSuchFile:
 
1212
            return ConfigObj(encoding='utf-8')
 
1213
 
 
1214
    def _set_configobj(self, configobj):
 
1215
        out_file = StringIO()
 
1216
        configobj.write(out_file)
 
1217
        out_file.seek(0)
 
1218
        self._transport.put_file(self._filename, out_file)