~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 16:43:12 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730164312-b025fd3ff0cee59e
rename  gpl.txt => COPYING.txt

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
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
log_format - This options set the default log format.  Options are long, 
 
53
             short, line, or a plugin can register new formats
55
54
 
56
55
In bazaar.conf you can also define aliases in the ALIASES sections, example
57
56
 
62
61
up=pull
63
62
"""
64
63
 
65
 
import os
66
 
import sys
67
64
 
68
 
from bzrlib.lazy_import import lazy_import
69
 
lazy_import(globals(), """
70
65
import errno
71
66
from fnmatch import fnmatch
 
67
import os
72
68
import re
73
 
from cStringIO import StringIO
 
69
import sys
 
70
from StringIO 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
 
""")
 
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
89
77
 
90
78
 
91
79
CHECK_IF_POSSIBLE=0
98
86
SIGN_NEVER=2
99
87
 
100
88
 
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
89
class ConfigObj(configobj.ConfigObj):
126
90
 
127
91
    def get_bool(self, section, key):
144
108
        """Get the users pop up editor."""
145
109
        raise NotImplementedError
146
110
 
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
111
    def _get_signature_checking(self):
170
112
        """Template method to override signature checking policy."""
171
113
 
225
167
    
226
168
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
227
169
        
228
 
        $BZR_EMAIL can be set to override this (as well as the
229
 
        deprecated $BZREMAIL), then
 
170
        $BZREMAIL can be set to override this, then
230
171
        the concrete policy type is checked, and finally
231
172
        $EMAIL is examined.
232
173
        If none is found, a reasonable default is (hopefully)
234
175
    
235
176
        TODO: Check it's reasonably well-formed.
236
177
        """
237
 
        v = os.environ.get('BZR_EMAIL')
 
178
        v = os.environ.get('BZREMAIL')
238
179
        if v:
239
180
            return v.decode(bzrlib.user_encoding)
240
 
 
 
181
    
241
182
        v = self._get_user_id()
242
183
        if v:
243
184
            return v
244
 
 
 
185
        
245
186
        v = os.environ.get('EMAIL')
246
187
        if v:
247
188
            return v.decode(bzrlib.user_encoding)
272
213
        if policy is None:
273
214
            policy = self._get_signature_checking()
274
215
            if policy is not None:
275
 
                trace.warning("Please use create_signatures,"
276
 
                              " not check_signatures to set signing policy.")
 
216
                warning("Please use create_signatures, not check_signatures "
 
217
                        "to set signing policy.")
277
218
            if policy == CHECK_ALWAYS:
278
219
                return True
279
220
        elif policy == SIGN_ALWAYS:
292
233
    def _get_nickname(self):
293
234
        return None
294
235
 
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
236
 
305
237
class IniBasedConfig(Config):
306
238
    """A configuration policy that draws from ini files."""
318
250
            raise errors.ParseConfigError(e.errors, e.config.filename)
319
251
        return self._parser
320
252
 
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
253
    def _get_section(self):
334
254
        """Override this to define the section used by the config."""
335
255
        return "DEFAULT"
336
256
 
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
257
    def _get_signature_checking(self):
342
258
        """See Config._get_signature_checking."""
343
259
        policy = self._get_user_option('check_signatures')
356
272
 
357
273
    def _get_user_option(self, option_name):
358
274
        """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
 
275
        try:
 
276
            return self._get_parser().get_value(self._get_section(),
 
277
                                                option_name)
 
278
        except KeyError:
 
279
            pass
381
280
 
382
281
    def _gpg_signing_command(self):
383
282
        """See Config.gpg_signing_command."""
457
356
 
458
357
    def __init__(self, location):
459
358
        name_generator = locations_config_filename
460
 
        if (not os.path.exists(name_generator()) and
 
359
        if (not os.path.exists(name_generator()) and 
461
360
                os.path.exists(branches_config_filename())):
462
361
            if sys.platform == 'win32':
463
 
                trace.warning('Please rename %s to %s'
464
 
                              % (branches_config_filename(),
465
 
                                 locations_config_filename()))
 
362
                warning('Please rename %s to %s' 
 
363
                         % (branches_config_filename(),
 
364
                            locations_config_filename()))
466
365
            else:
467
 
                trace.warning('Please rename ~/.bazaar/branches.conf'
468
 
                              ' to ~/.bazaar/locations.conf')
 
366
                warning('Please rename ~/.bazaar/branches.conf'
 
367
                        ' to ~/.bazaar/locations.conf')
469
368
            name_generator = branches_config_filename
470
369
        super(LocationConfig, self).__init__(name_generator)
471
370
        # local file locations are looked up by local path, rather than
475
374
            location = urlutils.local_path_from_url(location)
476
375
        self.location = location
477
376
 
478
 
    def _get_matching_sections(self):
479
 
        """Return an ordered list of section names matching this location."""
 
377
    def _get_section(self):
 
378
        """Get the section we should look in for config items.
 
379
 
 
380
        Returns None if none exists. 
 
381
        TODO: perhaps return a NullSection that thunks through to the 
 
382
              global config.
 
383
        """
480
384
        sections = self._get_parser()
481
385
        location_names = self.location.split('/')
482
386
        if self.location.endswith('/'):
506
410
            # if section is longer, no match.
507
411
            if len(section_names) > len(location_names):
508
412
                continue
509
 
            matches.append((len(section_names), section,
510
 
                            '/'.join(location_names[len(section_names):])))
 
413
            # if path is longer, and recurse is not true, no match
 
414
            if len(section_names) < len(location_names):
 
415
                try:
 
416
                    if not self._get_parser()[section].as_bool('recurse'):
 
417
                        continue
 
418
                except KeyError:
 
419
                    pass
 
420
            matches.append((len(section_names), section))
 
421
        if not len(matches):
 
422
            return None
511
423
        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):
 
424
        return matches[0][1]
 
425
 
 
426
    def set_user_option(self, option, value):
572
427
        """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
428
        # FIXME: RBC 20051029 This should refresh the parser and also take a
579
429
        # file lock on locations.conf.
580
430
        conf_dir = os.path.dirname(self._get_filename())
588
438
        elif location + '/' in self._get_parser():
589
439
            location = location + '/'
590
440
        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
441
        self._get_parser().write(file(self._get_filename(), 'wb'))
594
442
 
595
443
 
641
489
    def _get_user_id(self):
642
490
        """Return the full user id for the branch.
643
491
    
644
 
        e.g. "John Hacker <jhacker@example.com>"
 
492
        e.g. "John Hacker <jhacker@foo.org>"
645
493
        This is looked up in the email controlfile for the branch.
646
494
        """
647
495
        try:
648
 
            return (self.branch._transport.get_bytes("email")
 
496
            return (self.branch.control_files.get_utf8("email") 
 
497
                    .read()
649
498
                    .decode(bzrlib.user_encoding)
650
499
                    .rstrip("\r\n"))
651
500
        except errors.NoSuchFile, e:
669
518
                return value
670
519
        return None
671
520
 
672
 
    def set_user_option(self, name, value, store=STORE_BRANCH,
673
 
        warn_masked=False):
674
 
        if store == STORE_BRANCH:
 
521
    def set_user_option(self, name, value, local=False):
 
522
        if local is True:
 
523
            self._get_location_config().set_user_option(name, value)
 
524
        else:
675
525
            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
526
 
695
527
 
696
528
    def _gpg_signing_command(self):
715
547
        value = self._get_explicit_nickname()
716
548
        if value is not None:
717
549
            return value
718
 
        return urlutils.unescape(self.branch.base.split('/')[-2])
 
550
        return self.branch.base.split('/')[-2]
719
551
 
720
552
    def has_explicit_nickname(self):
721
553
        """Return true if a nickname has been explicitly assigned."""
741
573
        if sys.platform == 'win32':
742
574
            parent_dir = os.path.dirname(path)
743
575
            if not os.path.isdir(parent_dir):
744
 
                trace.mutter('creating config parent directory: %r', parent_dir)
 
576
                mutter('creating config parent directory: %r', parent_dir)
745
577
            os.mkdir(parent_dir)
746
 
        trace.mutter('creating config directory: %r', path)
 
578
        mutter('creating config directory: %r', path)
747
579
        os.mkdir(path)
748
580
 
749
581
 
757
589
    base = os.environ.get('BZR_HOME', None)
758
590
    if sys.platform == 'win32':
759
591
        if base is None:
760
 
            base = win32utils.get_appdata_location_unicode()
 
592
            base = os.environ.get('APPDATA', None)
761
593
        if base is None:
762
594
            base = os.environ.get('HOME', None)
763
595
        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')
 
596
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
 
597
        return pathjoin(base, 'bazaar', '2.0')
767
598
    else:
768
599
        # cygwin, linux, and darwin all have a $HOME directory
769
600
        if base is None:
770
601
            base = os.path.expanduser("~")
771
 
        return osutils.pathjoin(base, ".bazaar")
 
602
        return pathjoin(base, ".bazaar")
772
603
 
773
604
 
774
605
def config_filename():
775
606
    """Return per-user configuration ini file filename."""
776
 
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
 
607
    return pathjoin(config_dir(), 'bazaar.conf')
777
608
 
778
609
 
779
610
def branches_config_filename():
780
611
    """Return per-user configuration ini file filename."""
781
 
    return osutils.pathjoin(config_dir(), 'branches.conf')
 
612
    return pathjoin(config_dir(), 'branches.conf')
782
613
 
783
614
 
784
615
def locations_config_filename():
785
616
    """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')
 
617
    return pathjoin(config_dir(), 'locations.conf')
792
618
 
793
619
 
794
620
def user_ignore_config_filename():
795
621
    """Return the user default ignore filename"""
796
 
    return osutils.pathjoin(config_dir(), 'ignore')
 
622
    return pathjoin(config_dir(), 'ignore')
797
623
 
798
624
 
799
625
def _auto_user_id():
809
635
    """
810
636
    import socket
811
637
 
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)
 
638
    # XXX: Any good way to get real user name on win32?
822
639
 
823
640
    try:
824
641
        import pwd
864
681
    return realname, (username + '@' + socket.gethostname())
865
682
 
866
683
 
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
684
def extract_email_address(e):
877
685
    """Return just the address part of an email string.
878
 
 
 
686
    
879
687
    That is just the user@domain part, nothing else. 
880
688
    This part is required to contain only ascii characters.
881
689
    If it can't be extracted, raises an error.
882
 
 
 
690
    
883
691
    >>> extract_email_address('Jane Tester <jane@test.com>')
884
692
    "jane@test.com"
885
693
    """
886
 
    name, email = parse_username(e)
887
 
    if not email:
888
 
        raise errors.NoEmailInUsername(e)
889
 
    return email
 
694
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
695
    if not m:
 
696
        raise errors.BzrError("%r doesn't seem to contain "
 
697
                              "a reasonable email address" % e)
 
698
    return m.group(0)
890
699
 
891
700
 
892
701
class TreeConfig(IniBasedConfig):
893
702
    """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
703
    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
704
        self.branch = branch
903
705
 
904
706
    def _get_parser(self, file=None):
905
707
        if file is not None:
906
708
            return IniBasedConfig._get_parser(file)
907
 
        return self._config._get_configobj()
 
709
        return self._get_config()
 
710
 
 
711
    def _get_config(self):
 
712
        try:
 
713
            obj = ConfigObj(self.branch.control_files.get('branch.conf'), 
 
714
                            encoding='utf-8')
 
715
        except errors.NoSuchFile:
 
716
            obj = ConfigObj(encoding='utf=8')
 
717
        return obj
908
718
 
909
719
    def get_option(self, name, section=None, default=None):
910
720
        self.branch.lock_read()
911
721
        try:
912
 
            return self._config.get_option(name, section, default)
 
722
            obj = self._get_config()
 
723
            try:
 
724
                if section is not None:
 
725
                    obj[section]
 
726
                result = obj[name]
 
727
            except KeyError:
 
728
                result = default
913
729
        finally:
914
730
            self.branch.unlock()
915
731
        return result
918
734
        """Set a per-branch configuration option"""
919
735
        self.branch.lock_write()
920
736
        try:
921
 
            self._config.set_option(value, name, section)
 
737
            cfg_obj = self._get_config()
 
738
            if section is None:
 
739
                obj = cfg_obj
 
740
            else:
 
741
                try:
 
742
                    obj = cfg_obj[section]
 
743
                except KeyError:
 
744
                    cfg_obj[section] = {}
 
745
                    obj = cfg_obj[section]
 
746
            obj[name] = value
 
747
            out_file = StringIO()
 
748
            cfg_obj.write(out_file)
 
749
            out_file.seek(0)
 
750
            self.branch.control_files.put('branch.conf', out_file)
922
751
        finally:
923
752
            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)