1
# Copyright (C) 2005 by Canonical Ltd
 
 
2
#   Authors: Robert Collins <robert.collins@canonical.com>
 
 
4
# This program is free software; you can redistribute it and/or modify
 
 
5
# it under the terms of the GNU General Public License as published by
 
 
6
# the Free Software Foundation; either version 2 of the License, or
 
 
7
# (at your option) any later version.
 
 
9
# This program is distributed in the hope that it will be useful,
 
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
12
# GNU General Public License for more details.
 
 
14
# You should have received a copy of the GNU General Public License
 
 
15
# along with this program; if not, write to the Free Software
 
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
18
"""Configuration that affects the behaviour of Bazaar.
 
 
20
Currently this configuration resides in ~/.bazaar/bazaar.conf
 
 
21
and ~/.bazaar/branches.conf, which is written to by bzr.
 
 
23
In bazaar.conf the following options may be set:
 
 
25
editor=name-of-program
 
 
26
email=Your Name <your@email.address>
 
 
27
check_signatures=require|ignore|check-available(default)
 
 
28
create_signatures=always|never|when-required(default)
 
 
29
gpg_signing_command=name-of-program
 
 
30
log_format=name-of-format
 
 
32
in branches.conf, you specify the url of a branch and options for it.
 
 
33
Wildcards may be used - * and ? as normal in shell completion. Options
 
 
34
set in both bazaar.conf and branches.conf are overriden by the branches.conf
 
 
36
[/home/robertc/source]
 
 
37
recurse=False|True(default)
 
 
39
check_signatures= as abive 
 
 
40
create_signatures= as above.
 
 
42
explanation of options
 
 
43
----------------------
 
 
44
editor - this option sets the pop up editor to use during commits.
 
 
45
email - this option sets the user id bzr will use when committing.
 
 
46
check_signatures - this option controls whether bzr will require good gpg
 
 
47
                   signatures, ignore them, or check them if they are 
 
 
49
create_signatures - this option controls whether bzr will always create 
 
 
50
                    gpg signatures, never create them, or create them if the
 
 
51
                    branch is configured to require them.
 
 
52
                    NB: This option is planned, but not implemented yet.
 
 
53
log_format - This options set the default log format.  Options are long, 
 
 
54
             short, line, or a plugin can register new formats
 
 
61
from fnmatch import fnmatch
 
 
65
import bzrlib.errors as errors
 
 
66
from bzrlib.osutils import pathjoin
 
 
67
from bzrlib.trace import mutter
 
 
68
import bzrlib.util.configobj.configobj as configobj
 
 
69
from StringIO import StringIO
 
 
76
class ConfigObj(configobj.ConfigObj):
 
 
78
    def get_bool(self, section, key):
 
 
79
        return self[section].as_bool(key)
 
 
81
    def get_value(self, section, name):
 
 
82
        # Try [] for the old DEFAULT section.
 
 
83
        if section == "DEFAULT":
 
 
88
        return self[section][name]
 
 
92
    """A configuration policy - what username, editor, gpg needs etc."""
 
 
95
        """Get the users pop up editor."""
 
 
96
        raise NotImplementedError
 
 
98
    def _get_signature_checking(self):
 
 
99
        """Template method to override signature checking policy."""
 
 
101
    def _get_user_option(self, option_name):
 
 
102
        """Template method to provide a user option."""
 
 
105
    def get_user_option(self, option_name):
 
 
106
        """Get a generic option - no special process, no default."""
 
 
107
        return self._get_user_option(option_name)
 
 
109
    def gpg_signing_command(self):
 
 
110
        """What program should be used to sign signatures?"""
 
 
111
        result = self._gpg_signing_command()
 
 
116
    def _gpg_signing_command(self):
 
 
117
        """See gpg_signing_command()."""
 
 
120
    def log_format(self):
 
 
121
        """What log format should be used"""
 
 
122
        result = self._log_format()
 
 
127
    def _log_format(self):
 
 
128
        """See log_format()."""
 
 
132
        super(Config, self).__init__()
 
 
134
    def post_commit(self):
 
 
135
        """An ordered list of python functions to call.
 
 
137
        Each function takes branch, rev_id as parameters.
 
 
139
        return self._post_commit()
 
 
141
    def _post_commit(self):
 
 
142
        """See Config.post_commit."""
 
 
145
    def user_email(self):
 
 
146
        """Return just the email component of a username."""
 
 
147
        return extract_email_address(self.username())
 
 
150
        """Return email-style username.
 
 
152
        Something similar to 'Martin Pool <mbp@sourcefrog.net>'
 
 
154
        $BZREMAIL can be set to override this, then
 
 
155
        the concrete policy type is checked, and finally
 
 
157
        If none is found, a reasonable default is (hopefully)
 
 
160
        TODO: Check it's reasonably well-formed.
 
 
162
        v = os.environ.get('BZREMAIL')
 
 
164
            return v.decode(bzrlib.user_encoding)
 
 
166
        v = self._get_user_id()
 
 
170
        v = os.environ.get('EMAIL')
 
 
172
            return v.decode(bzrlib.user_encoding)
 
 
174
        name, email = _auto_user_id()
 
 
176
            return '%s <%s>' % (name, email)
 
 
180
    def signature_checking(self):
 
 
181
        """What is the current policy for signature checking?."""
 
 
182
        policy = self._get_signature_checking()
 
 
183
        if policy is not None:
 
 
185
        return CHECK_IF_POSSIBLE
 
 
187
    def signature_needed(self):
 
 
188
        """Is a signature needed when committing ?."""
 
 
189
        policy = self._get_signature_checking()
 
 
190
        if policy == CHECK_ALWAYS:
 
 
195
class IniBasedConfig(Config):
 
 
196
    """A configuration policy that draws from ini files."""
 
 
198
    def _get_parser(self, file=None):
 
 
199
        if self._parser is not None:
 
 
202
            input = self._get_filename()
 
 
206
            self._parser = ConfigObj(input)
 
 
207
        except configobj.ConfigObjError, e:
 
 
208
            raise errors.ParseConfigError(e.errors, e.config.filename)
 
 
211
    def _get_section(self):
 
 
212
        """Override this to define the section used by the config."""
 
 
215
    def _get_signature_checking(self):
 
 
216
        """See Config._get_signature_checking."""
 
 
217
        policy = self._get_user_option('check_signatures')
 
 
219
            return self._string_to_signature_policy(policy)
 
 
221
    def _get_user_id(self):
 
 
222
        """Get the user id from the 'email' key in the current section."""
 
 
223
        return self._get_user_option('email')
 
 
225
    def _get_user_option(self, option_name):
 
 
226
        """See Config._get_user_option."""
 
 
228
            return self._get_parser().get_value(self._get_section(),
 
 
233
    def _gpg_signing_command(self):
 
 
234
        """See Config.gpg_signing_command."""
 
 
235
        return self._get_user_option('gpg_signing_command')
 
 
237
    def _log_format(self):
 
 
238
        """See Config.log_format."""
 
 
239
        return self._get_user_option('log_format')
 
 
241
    def __init__(self, get_filename):
 
 
242
        super(IniBasedConfig, self).__init__()
 
 
243
        self._get_filename = get_filename
 
 
246
    def _post_commit(self):
 
 
247
        """See Config.post_commit."""
 
 
248
        return self._get_user_option('post_commit')
 
 
250
    def _string_to_signature_policy(self, signature_string):
 
 
251
        """Convert a string to a signing policy."""
 
 
252
        if signature_string.lower() == 'check-available':
 
 
253
            return CHECK_IF_POSSIBLE
 
 
254
        if signature_string.lower() == 'ignore':
 
 
256
        if signature_string.lower() == 'require':
 
 
258
        raise errors.BzrError("Invalid signatures policy '%s'"
 
 
262
class GlobalConfig(IniBasedConfig):
 
 
263
    """The configuration that should be used for a specific location."""
 
 
265
    def get_editor(self):
 
 
266
        return self._get_user_option('editor')
 
 
269
        super(GlobalConfig, self).__init__(config_filename)
 
 
272
class LocationConfig(IniBasedConfig):
 
 
273
    """A configuration object that gives the policy for a location."""
 
 
275
    def __init__(self, location):
 
 
276
        super(LocationConfig, self).__init__(branches_config_filename)
 
 
277
        self._global_config = None
 
 
278
        self.location = location
 
 
280
    def _get_global_config(self):
 
 
281
        if self._global_config is None:
 
 
282
            self._global_config = GlobalConfig()
 
 
283
        return self._global_config
 
 
285
    def _get_section(self):
 
 
286
        """Get the section we should look in for config items.
 
 
288
        Returns None if none exists. 
 
 
289
        TODO: perhaps return a NullSection that thunks through to the 
 
 
292
        sections = self._get_parser()
 
 
293
        location_names = self.location.split('/')
 
 
294
        if self.location.endswith('/'):
 
 
295
            del location_names[-1]
 
 
297
        for section in sections:
 
 
298
            section_names = section.split('/')
 
 
299
            if section.endswith('/'):
 
 
300
                del section_names[-1]
 
 
301
            names = zip(location_names, section_names)
 
 
304
                if not fnmatch(name[0], name[1]):
 
 
309
            # so, for the common prefix they matched.
 
 
310
            # if section is longer, no match.
 
 
311
            if len(section_names) > len(location_names):
 
 
313
            # if path is longer, and recurse is not true, no match
 
 
314
            if len(section_names) < len(location_names):
 
 
316
                    if not self._get_parser().get_bool(section, 'recurse'):
 
 
320
            matches.append((len(section_names), section))
 
 
323
        matches.sort(reverse=True)
 
 
326
    def _gpg_signing_command(self):
 
 
327
        """See Config.gpg_signing_command."""
 
 
328
        command = super(LocationConfig, self)._gpg_signing_command()
 
 
329
        if command is not None:
 
 
331
        return self._get_global_config()._gpg_signing_command()
 
 
333
    def _log_format(self):
 
 
334
        """See Config.log_format."""
 
 
335
        command = super(LocationConfig, self)._log_format()
 
 
336
        if command is not None:
 
 
338
        return self._get_global_config()._log_format()
 
 
340
    def _get_user_id(self):
 
 
341
        user_id = super(LocationConfig, self)._get_user_id()
 
 
342
        if user_id is not None:
 
 
344
        return self._get_global_config()._get_user_id()
 
 
346
    def _get_user_option(self, option_name):
 
 
347
        """See Config._get_user_option."""
 
 
348
        option_value = super(LocationConfig, 
 
 
349
                             self)._get_user_option(option_name)
 
 
350
        if option_value is not None:
 
 
352
        return self._get_global_config()._get_user_option(option_name)
 
 
354
    def _get_signature_checking(self):
 
 
355
        """See Config._get_signature_checking."""
 
 
356
        check = super(LocationConfig, self)._get_signature_checking()
 
 
357
        if check is not None:
 
 
359
        return self._get_global_config()._get_signature_checking()
 
 
361
    def _post_commit(self):
 
 
362
        """See Config.post_commit."""
 
 
363
        hook = self._get_user_option('post_commit')
 
 
366
        return self._get_global_config()._post_commit()
 
 
368
    def set_user_option(self, option, value):
 
 
369
        """Save option and its value in the configuration."""
 
 
370
        # FIXME: RBC 20051029 This should refresh the parser and also take a
 
 
371
        # file lock on branches.conf.
 
 
372
        conf_dir = os.path.dirname(self._get_filename())
 
 
373
        ensure_config_dir_exists(conf_dir)
 
 
374
        location = self.location
 
 
375
        if location.endswith('/'):
 
 
376
            location = location[:-1]
 
 
377
        if (not location in self._get_parser() and
 
 
378
            not location + '/' in self._get_parser()):
 
 
379
            self._get_parser()[location]={}
 
 
380
        elif location + '/' in self._get_parser():
 
 
381
            location = location + '/'
 
 
382
        self._get_parser()[location][option]=value
 
 
383
        self._get_parser().write()
 
 
386
class BranchConfig(Config):
 
 
387
    """A configuration object giving the policy for a branch."""
 
 
389
    def _get_location_config(self):
 
 
390
        if self._location_config is None:
 
 
391
            self._location_config = LocationConfig(self.branch.base)
 
 
392
        return self._location_config
 
 
394
    def _get_user_id(self):
 
 
395
        """Return the full user id for the branch.
 
 
397
        e.g. "John Hacker <jhacker@foo.org>"
 
 
398
        This is looked up in the email controlfile for the branch.
 
 
401
            return (self.branch.control_files.get_utf8("email") 
 
 
403
                    .decode(bzrlib.user_encoding)
 
 
405
        except errors.NoSuchFile, e:
 
 
408
        return self._get_location_config()._get_user_id()
 
 
410
    def _get_signature_checking(self):
 
 
411
        """See Config._get_signature_checking."""
 
 
412
        return self._get_location_config()._get_signature_checking()
 
 
414
    def _get_user_option(self, option_name):
 
 
415
        """See Config._get_user_option."""
 
 
416
        return self._get_location_config()._get_user_option(option_name)
 
 
418
    def _gpg_signing_command(self):
 
 
419
        """See Config.gpg_signing_command."""
 
 
420
        return self._get_location_config()._gpg_signing_command()
 
 
422
    def __init__(self, branch):
 
 
423
        super(BranchConfig, self).__init__()
 
 
424
        self._location_config = None
 
 
427
    def _post_commit(self):
 
 
428
        """See Config.post_commit."""
 
 
429
        return self._get_location_config()._post_commit()
 
 
431
    def _log_format(self):
 
 
432
        """See Config.log_format."""
 
 
433
        return self._get_location_config()._log_format()
 
 
435
def ensure_config_dir_exists(path=None):
 
 
436
    """Make sure a configuration directory exists.
 
 
437
    This makes sure that the directory exists.
 
 
438
    On windows, since configuration directories are 2 levels deep,
 
 
439
    it makes sure both the directory and the parent directory exists.
 
 
443
    if not os.path.isdir(path):
 
 
444
        if sys.platform == 'win32':
 
 
445
            parent_dir = os.path.dirname(path)
 
 
446
            if not os.path.isdir(parent_dir):
 
 
447
                mutter('creating config parent directory: %r', parent_dir)
 
 
449
        mutter('creating config directory: %r', path)
 
 
454
    """Return per-user configuration directory.
 
 
456
    By default this is ~/.bazaar/
 
 
458
    TODO: Global option --config-dir to override this.
 
 
460
    base = os.environ.get('BZR_HOME', None)
 
 
461
    if sys.platform == 'win32':
 
 
463
            base = os.environ.get('APPDATA', None)
 
 
465
            base = os.environ.get('HOME', None)
 
 
467
            raise BzrError('You must have one of BZR_HOME, APPDATA, or HOME set')
 
 
468
        return pathjoin(base, 'bazaar', '2.0')
 
 
470
        # cygwin, linux, and darwin all have a $HOME directory
 
 
472
            base = os.path.expanduser("~")
 
 
473
        return pathjoin(base, ".bazaar")
 
 
476
def config_filename():
 
 
477
    """Return per-user configuration ini file filename."""
 
 
478
    return pathjoin(config_dir(), 'bazaar.conf')
 
 
481
def branches_config_filename():
 
 
482
    """Return per-user configuration ini file filename."""
 
 
483
    return pathjoin(config_dir(), 'branches.conf')
 
 
487
    """Calculate automatic user identification.
 
 
489
    Returns (realname, email).
 
 
491
    Only used when none is set in the environment or the id file.
 
 
493
    This previously used the FQDN as the default domain, but that can
 
 
494
    be very slow on machines where DNS is broken.  So now we simply
 
 
499
    # XXX: Any good way to get real user name on win32?
 
 
504
        w = pwd.getpwuid(uid)
 
 
505
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
 
 
506
        username = w.pw_name.decode(bzrlib.user_encoding)
 
 
507
        comma = gecos.find(',')
 
 
511
            realname = gecos[:comma]
 
 
517
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
 
 
519
    return realname, (username + '@' + socket.gethostname())
 
 
522
def extract_email_address(e):
 
 
523
    """Return just the address part of an email string.
 
 
525
    That is just the user@domain part, nothing else. 
 
 
526
    This part is required to contain only ascii characters.
 
 
527
    If it can't be extracted, raises an error.
 
 
529
    >>> extract_email_address('Jane Tester <jane@test.com>')
 
 
532
    m = re.search(r'[\w+.-]+@[\w+.-]+', e)
 
 
534
        raise errors.BzrError("%r doesn't seem to contain "
 
 
535
                              "a reasonable email address" % e)
 
 
538
class TreeConfig(object):
 
 
539
    """Branch configuration data associated with its contents, not location"""
 
 
540
    def __init__(self, branch):
 
 
543
    def _get_config(self):
 
 
545
            obj = ConfigObj(self.branch.control_files.get('branch.conf'), 
 
 
547
        except errors.NoSuchFile:
 
 
548
            obj = ConfigObj(encoding='utf=8')
 
 
551
    def get_option(self, name, section=None, default=None):
 
 
552
        self.branch.lock_read()
 
 
554
            obj = self._get_config()
 
 
556
                if section is not None:
 
 
565
    def set_option(self, value, name, section=None):
 
 
566
        """Set a per-branch configuration option"""
 
 
567
        self.branch.lock_write()
 
 
569
            cfg_obj = self._get_config()
 
 
574
                    obj = cfg_obj[section]
 
 
576
                    cfg_obj[section] = {}
 
 
577
                    obj = cfg_obj[section]
 
 
579
            out_file = StringIO()
 
 
580
            cfg_obj.write(out_file)
 
 
582
            self.branch.control_files.put('branch.conf', out_file)