~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Robert Collins
  • Date: 2009-04-24 05:08:51 UTC
  • mto: This revision was merged to the branch mainline in revision 4304.
  • Revision ID: robertc@robertcollins.net-20090424050851-sdfonaqerfs386t0
Reduce round trips pushing new branches substantially.

Show diffs side-by-side

added added

removed removed

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