~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/config.py

  • Committer: John Arbash Meinel
  • Date: 2006-08-23 22:16:27 UTC
  • mto: This revision was merged to the branch mainline in revision 1955.
  • Revision ID: john@arbash-meinel.com-20060823221627-fc64105bb12ae770
Ghozzy: Fix Bzr's support of Active FTP (aftp://)

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
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
 
237
179
        v = os.environ.get('BZR_EMAIL')
238
180
        if v:
239
181
            return v.decode(bzrlib.user_encoding)
240
 
 
 
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
    
241
187
        v = self._get_user_id()
242
188
        if v:
243
189
            return v
244
 
 
 
190
        
245
191
        v = os.environ.get('EMAIL')
246
192
        if v:
247
193
            return v.decode(bzrlib.user_encoding)
272
218
        if policy is None:
273
219
            policy = self._get_signature_checking()
274
220
            if policy is not None:
275
 
                trace.warning("Please use create_signatures,"
276
 
                              " not check_signatures to set signing policy.")
 
221
                warning("Please use create_signatures, not check_signatures "
 
222
                        "to set signing policy.")
277
223
            if policy == CHECK_ALWAYS:
278
224
                return True
279
225
        elif policy == SIGN_ALWAYS:
292
238
    def _get_nickname(self):
293
239
        return None
294
240
 
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
241
 
305
242
class IniBasedConfig(Config):
306
243
    """A configuration policy that draws from ini files."""
318
255
            raise errors.ParseConfigError(e.errors, e.config.filename)
319
256
        return self._parser
320
257
 
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
258
    def _get_section(self):
334
259
        """Override this to define the section used by the config."""
335
260
        return "DEFAULT"
336
261
 
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
262
    def _get_signature_checking(self):
342
263
        """See Config._get_signature_checking."""
343
264
        policy = self._get_user_option('check_signatures')
356
277
 
357
278
    def _get_user_option(self, option_name):
358
279
        """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
 
280
        try:
 
281
            return self._get_parser().get_value(self._get_section(),
 
282
                                                option_name)
 
283
        except KeyError:
 
284
            pass
381
285
 
382
286
    def _gpg_signing_command(self):
383
287
        """See Config.gpg_signing_command."""
457
361
 
458
362
    def __init__(self, location):
459
363
        name_generator = locations_config_filename
460
 
        if (not os.path.exists(name_generator()) and
 
364
        if (not os.path.exists(name_generator()) and 
461
365
                os.path.exists(branches_config_filename())):
462
366
            if sys.platform == 'win32':
463
 
                trace.warning('Please rename %s to %s'
464
 
                              % (branches_config_filename(),
465
 
                                 locations_config_filename()))
 
367
                warning('Please rename %s to %s' 
 
368
                         % (branches_config_filename(),
 
369
                            locations_config_filename()))
466
370
            else:
467
 
                trace.warning('Please rename ~/.bazaar/branches.conf'
468
 
                              ' to ~/.bazaar/locations.conf')
 
371
                warning('Please rename ~/.bazaar/branches.conf'
 
372
                        ' to ~/.bazaar/locations.conf')
469
373
            name_generator = branches_config_filename
470
374
        super(LocationConfig, self).__init__(name_generator)
471
375
        # local file locations are looked up by local path, rather than
475
379
            location = urlutils.local_path_from_url(location)
476
380
        self.location = location
477
381
 
478
 
    def _get_matching_sections(self):
479
 
        """Return an ordered list of section names matching this location."""
 
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
        """
480
389
        sections = self._get_parser()
481
390
        location_names = self.location.split('/')
482
391
        if self.location.endswith('/'):
506
415
            # if section is longer, no match.
507
416
            if len(section_names) > len(location_names):
508
417
                continue
509
 
            matches.append((len(section_names), section,
510
 
                            '/'.join(location_names[len(section_names):])))
 
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
511
428
        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):
 
429
        return matches[0][1]
 
430
 
 
431
    def set_user_option(self, option, value):
572
432
        """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
433
        # FIXME: RBC 20051029 This should refresh the parser and also take a
579
434
        # file lock on locations.conf.
580
435
        conf_dir = os.path.dirname(self._get_filename())
588
443
        elif location + '/' in self._get_parser():
589
444
            location = location + '/'
590
445
        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
446
        self._get_parser().write(file(self._get_filename(), 'wb'))
594
447
 
595
448
 
641
494
    def _get_user_id(self):
642
495
        """Return the full user id for the branch.
643
496
    
644
 
        e.g. "John Hacker <jhacker@example.com>"
 
497
        e.g. "John Hacker <jhacker@foo.org>"
645
498
        This is looked up in the email controlfile for the branch.
646
499
        """
647
500
        try:
648
 
            return (self.branch._transport.get_bytes("email")
 
501
            return (self.branch.control_files.get_utf8("email") 
 
502
                    .read()
649
503
                    .decode(bzrlib.user_encoding)
650
504
                    .rstrip("\r\n"))
651
505
        except errors.NoSuchFile, e:
669
523
                return value
670
524
        return None
671
525
 
672
 
    def set_user_option(self, name, value, store=STORE_BRANCH,
673
 
        warn_masked=False):
674
 
        if store == STORE_BRANCH:
 
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:
675
530
            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
531
 
695
532
 
696
533
    def _gpg_signing_command(self):
715
552
        value = self._get_explicit_nickname()
716
553
        if value is not None:
717
554
            return value
718
 
        return urlutils.unescape(self.branch.base.split('/')[-2])
 
555
        return self.branch.base.split('/')[-2]
719
556
 
720
557
    def has_explicit_nickname(self):
721
558
        """Return true if a nickname has been explicitly assigned."""
741
578
        if sys.platform == 'win32':
742
579
            parent_dir = os.path.dirname(path)
743
580
            if not os.path.isdir(parent_dir):
744
 
                trace.mutter('creating config parent directory: %r', parent_dir)
 
581
                mutter('creating config parent directory: %r', parent_dir)
745
582
            os.mkdir(parent_dir)
746
 
        trace.mutter('creating config directory: %r', path)
 
583
        mutter('creating config directory: %r', path)
747
584
        os.mkdir(path)
748
585
 
749
586
 
757
594
    base = os.environ.get('BZR_HOME', None)
758
595
    if sys.platform == 'win32':
759
596
        if base is None:
760
 
            base = win32utils.get_appdata_location_unicode()
 
597
            base = os.environ.get('APPDATA', None)
761
598
        if base is None:
762
599
            base = os.environ.get('HOME', None)
763
600
        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')
 
601
            raise errors.BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
 
602
        return pathjoin(base, 'bazaar', '2.0')
767
603
    else:
768
604
        # cygwin, linux, and darwin all have a $HOME directory
769
605
        if base is None:
770
606
            base = os.path.expanduser("~")
771
 
        return osutils.pathjoin(base, ".bazaar")
 
607
        return pathjoin(base, ".bazaar")
772
608
 
773
609
 
774
610
def config_filename():
775
611
    """Return per-user configuration ini file filename."""
776
 
    return osutils.pathjoin(config_dir(), 'bazaar.conf')
 
612
    return pathjoin(config_dir(), 'bazaar.conf')
777
613
 
778
614
 
779
615
def branches_config_filename():
780
616
    """Return per-user configuration ini file filename."""
781
 
    return osutils.pathjoin(config_dir(), 'branches.conf')
 
617
    return pathjoin(config_dir(), 'branches.conf')
782
618
 
783
619
 
784
620
def locations_config_filename():
785
621
    """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')
 
622
    return pathjoin(config_dir(), 'locations.conf')
792
623
 
793
624
 
794
625
def user_ignore_config_filename():
795
626
    """Return the user default ignore filename"""
796
 
    return osutils.pathjoin(config_dir(), 'ignore')
 
627
    return pathjoin(config_dir(), 'ignore')
797
628
 
798
629
 
799
630
def _auto_user_id():
809
640
    """
810
641
    import socket
811
642
 
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)
 
643
    # XXX: Any good way to get real user name on win32?
822
644
 
823
645
    try:
824
646
        import pwd
864
686
    return realname, (username + '@' + socket.gethostname())
865
687
 
866
688
 
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
689
def extract_email_address(e):
877
690
    """Return just the address part of an email string.
878
 
 
 
691
    
879
692
    That is just the user@domain part, nothing else. 
880
693
    This part is required to contain only ascii characters.
881
694
    If it can't be extracted, raises an error.
882
 
 
 
695
    
883
696
    >>> extract_email_address('Jane Tester <jane@test.com>')
884
697
    "jane@test.com"
885
698
    """
886
 
    name, email = parse_username(e)
887
 
    if not email:
888
 
        raise errors.NoEmailInUsername(e)
889
 
    return email
 
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)
890
704
 
891
705
 
892
706
class TreeConfig(IniBasedConfig):
893
707
    """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
708
    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
709
        self.branch = branch
903
710
 
904
711
    def _get_parser(self, file=None):
905
712
        if file is not None:
906
713
            return IniBasedConfig._get_parser(file)
907
 
        return self._config._get_configobj()
 
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
908
723
 
909
724
    def get_option(self, name, section=None, default=None):
910
725
        self.branch.lock_read()
911
726
        try:
912
 
            return self._config.get_option(name, section, default)
 
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
913
734
        finally:
914
735
            self.branch.unlock()
915
736
        return result
918
739
        """Set a per-branch configuration option"""
919
740
        self.branch.lock_write()
920
741
        try:
921
 
            self._config.set_option(value, name, section)
 
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)
922
756
        finally:
923
757
            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)