~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Aaron Bentley
  • Date: 2006-04-19 02:08:51 UTC
  • mto: This revision was merged to the branch mainline in revision 1672.
  • Revision ID: aaron.bentley@utoronto.ca-20060419020851-830a65e114637604
Clarifications from merge review

Show diffs side-by-side

added added

removed removed

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