##// END OF EJS Templates
ui: Rename has_config to has_section.
Bryan O'Sullivan -
r4487:1b5b9883 default
parent child Browse files
Show More
@@ -1,124 +1,124 b''
1 # acl.py - changeset access control for mercurial
1 # acl.py - changeset access control for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 #
7 #
8 # this hook allows to allow or deny access to parts of a repo when
8 # this hook allows to allow or deny access to parts of a repo when
9 # taking incoming changesets.
9 # taking incoming changesets.
10 #
10 #
11 # authorization is against local user name on system where hook is
11 # authorization is against local user name on system where hook is
12 # run, not committer of original changeset (since that is easy to
12 # run, not committer of original changeset (since that is easy to
13 # spoof).
13 # spoof).
14 #
14 #
15 # acl hook is best to use if you use hgsh to set up restricted shells
15 # acl hook is best to use if you use hgsh to set up restricted shells
16 # for authenticated users to only push to / pull from. not safe if
16 # for authenticated users to only push to / pull from. not safe if
17 # user has interactive shell access, because they can disable hook.
17 # user has interactive shell access, because they can disable hook.
18 # also not safe if remote users share one local account, because then
18 # also not safe if remote users share one local account, because then
19 # no way to tell remote users apart.
19 # no way to tell remote users apart.
20 #
20 #
21 # to use, configure acl extension in hgrc like this:
21 # to use, configure acl extension in hgrc like this:
22 #
22 #
23 # [extensions]
23 # [extensions]
24 # hgext.acl =
24 # hgext.acl =
25 #
25 #
26 # [hooks]
26 # [hooks]
27 # pretxnchangegroup.acl = python:hgext.acl.hook
27 # pretxnchangegroup.acl = python:hgext.acl.hook
28 #
28 #
29 # [acl]
29 # [acl]
30 # sources = serve # check if source of incoming changes in this list
30 # sources = serve # check if source of incoming changes in this list
31 # # ("serve" == ssh or http, "push", "pull", "bundle")
31 # # ("serve" == ssh or http, "push", "pull", "bundle")
32 #
32 #
33 # allow and deny lists have subtree pattern (default syntax is glob)
33 # allow and deny lists have subtree pattern (default syntax is glob)
34 # on left, user names on right. deny list checked before allow list.
34 # on left, user names on right. deny list checked before allow list.
35 #
35 #
36 # [acl.allow]
36 # [acl.allow]
37 # # if acl.allow not present, all users allowed by default
37 # # if acl.allow not present, all users allowed by default
38 # # empty acl.allow = no users allowed
38 # # empty acl.allow = no users allowed
39 # docs/** = doc_writer
39 # docs/** = doc_writer
40 # .hgtags = release_engineer
40 # .hgtags = release_engineer
41 #
41 #
42 # [acl.deny]
42 # [acl.deny]
43 # # if acl.deny not present, no users denied by default
43 # # if acl.deny not present, no users denied by default
44 # # empty acl.deny = all users allowed
44 # # empty acl.deny = all users allowed
45 # glob pattern = user4, user5
45 # glob pattern = user4, user5
46 # ** = user6
46 # ** = user6
47
47
48 from mercurial.i18n import _
48 from mercurial.i18n import _
49 from mercurial.node import *
49 from mercurial.node import *
50 from mercurial import util
50 from mercurial import util
51 import getpass
51 import getpass
52
52
53 class checker(object):
53 class checker(object):
54 '''acl checker.'''
54 '''acl checker.'''
55
55
56 def buildmatch(self, key):
56 def buildmatch(self, key):
57 '''return tuple of (match function, list enabled).'''
57 '''return tuple of (match function, list enabled).'''
58 if not self.ui.has_config(key):
58 if not self.ui.has_section(key):
59 self.ui.debug(_('acl: %s not enabled\n') % key)
59 self.ui.debug(_('acl: %s not enabled\n') % key)
60 return None, False
60 return None, False
61
61
62 thisuser = self.getuser()
62 thisuser = self.getuser()
63 pats = [pat for pat, users in self.ui.configitems(key)
63 pats = [pat for pat, users in self.ui.configitems(key)
64 if thisuser in users.replace(',', ' ').split()]
64 if thisuser in users.replace(',', ' ').split()]
65 self.ui.debug(_('acl: %s enabled, %d entries for user %s\n') %
65 self.ui.debug(_('acl: %s enabled, %d entries for user %s\n') %
66 (key, len(pats), thisuser))
66 (key, len(pats), thisuser))
67 if pats:
67 if pats:
68 match = util.matcher(self.repo.root, names=pats)[1]
68 match = util.matcher(self.repo.root, names=pats)[1]
69 else:
69 else:
70 match = util.never
70 match = util.never
71 return match, True
71 return match, True
72
72
73 def getuser(self):
73 def getuser(self):
74 '''return name of authenticated user.'''
74 '''return name of authenticated user.'''
75 return self.user
75 return self.user
76
76
77 def __init__(self, ui, repo):
77 def __init__(self, ui, repo):
78 self.ui = ui
78 self.ui = ui
79 self.repo = repo
79 self.repo = repo
80 self.user = getpass.getuser()
80 self.user = getpass.getuser()
81 cfg = self.ui.config('acl', 'config')
81 cfg = self.ui.config('acl', 'config')
82 if cfg:
82 if cfg:
83 self.ui.readsections(cfg, 'acl.allow', 'acl.deny')
83 self.ui.readsections(cfg, 'acl.allow', 'acl.deny')
84 self.allow, self.allowable = self.buildmatch('acl.allow')
84 self.allow, self.allowable = self.buildmatch('acl.allow')
85 self.deny, self.deniable = self.buildmatch('acl.deny')
85 self.deny, self.deniable = self.buildmatch('acl.deny')
86
86
87 def skipsource(self, source):
87 def skipsource(self, source):
88 '''true if incoming changes from this source should be skipped.'''
88 '''true if incoming changes from this source should be skipped.'''
89 ok_sources = self.ui.config('acl', 'sources', 'serve').split()
89 ok_sources = self.ui.config('acl', 'sources', 'serve').split()
90 return source not in ok_sources
90 return source not in ok_sources
91
91
92 def check(self, node):
92 def check(self, node):
93 '''return if access allowed, raise exception if not.'''
93 '''return if access allowed, raise exception if not.'''
94 files = self.repo.changectx(node).files()
94 files = self.repo.changectx(node).files()
95 if self.deniable:
95 if self.deniable:
96 for f in files:
96 for f in files:
97 if self.deny(f):
97 if self.deny(f):
98 self.ui.debug(_('acl: user %s denied on %s\n') %
98 self.ui.debug(_('acl: user %s denied on %s\n') %
99 (self.getuser(), f))
99 (self.getuser(), f))
100 raise util.Abort(_('acl: access denied for changeset %s') %
100 raise util.Abort(_('acl: access denied for changeset %s') %
101 short(node))
101 short(node))
102 if self.allowable:
102 if self.allowable:
103 for f in files:
103 for f in files:
104 if not self.allow(f):
104 if not self.allow(f):
105 self.ui.debug(_('acl: user %s not allowed on %s\n') %
105 self.ui.debug(_('acl: user %s not allowed on %s\n') %
106 (self.getuser(), f))
106 (self.getuser(), f))
107 raise util.Abort(_('acl: access denied for changeset %s') %
107 raise util.Abort(_('acl: access denied for changeset %s') %
108 short(node))
108 short(node))
109 self.ui.debug(_('acl: allowing changeset %s\n') % short(node))
109 self.ui.debug(_('acl: allowing changeset %s\n') % short(node))
110
110
111 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
111 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
112 if hooktype != 'pretxnchangegroup':
112 if hooktype != 'pretxnchangegroup':
113 raise util.Abort(_('config error - hook type "%s" cannot stop '
113 raise util.Abort(_('config error - hook type "%s" cannot stop '
114 'incoming changesets') % hooktype)
114 'incoming changesets') % hooktype)
115
115
116 c = checker(ui, repo)
116 c = checker(ui, repo)
117 if c.skipsource(source):
117 if c.skipsource(source):
118 ui.debug(_('acl: changes have source "%s" - skipping\n') % source)
118 ui.debug(_('acl: changes have source "%s" - skipping\n') % source)
119 return
119 return
120
120
121 start = repo.changelog.rev(bin(node))
121 start = repo.changelog.rev(bin(node))
122 end = repo.changelog.count()
122 end = repo.changelog.count()
123 for rev in xrange(start, end):
123 for rev in xrange(start, end):
124 c.check(repo.changelog.node(rev))
124 c.check(repo.changelog.node(rev))
@@ -1,450 +1,450 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, re, socket, sys, tempfile
9 import errno, getpass, os, re, socket, sys, tempfile
10 import ConfigParser, traceback, util
10 import ConfigParser, traceback, util
11
11
12 def dupconfig(orig):
12 def dupconfig(orig):
13 new = util.configparser(orig.defaults())
13 new = util.configparser(orig.defaults())
14 updateconfig(orig, new)
14 updateconfig(orig, new)
15 return new
15 return new
16
16
17 def updateconfig(source, dest, sections=None):
17 def updateconfig(source, dest, sections=None):
18 if not sections:
18 if not sections:
19 sections = source.sections()
19 sections = source.sections()
20 for section in sections:
20 for section in sections:
21 if not dest.has_section(section):
21 if not dest.has_section(section):
22 dest.add_section(section)
22 dest.add_section(section)
23 for name, value in source.items(section, raw=True):
23 for name, value in source.items(section, raw=True):
24 dest.set(section, name, value)
24 dest.set(section, name, value)
25
25
26 class ui(object):
26 class ui(object):
27 def __init__(self, verbose=False, debug=False, quiet=False,
27 def __init__(self, verbose=False, debug=False, quiet=False,
28 interactive=True, traceback=False, report_untrusted=True,
28 interactive=True, traceback=False, report_untrusted=True,
29 parentui=None):
29 parentui=None):
30 self.overlay = None
30 self.overlay = None
31 self.buffers = []
31 self.buffers = []
32 if parentui is None:
32 if parentui is None:
33 # this is the parent of all ui children
33 # this is the parent of all ui children
34 self.parentui = None
34 self.parentui = None
35 self.readhooks = []
35 self.readhooks = []
36 self.quiet = quiet
36 self.quiet = quiet
37 self.verbose = verbose
37 self.verbose = verbose
38 self.debugflag = debug
38 self.debugflag = debug
39 self.interactive = interactive
39 self.interactive = interactive
40 self.traceback = traceback
40 self.traceback = traceback
41 self.report_untrusted = report_untrusted
41 self.report_untrusted = report_untrusted
42 self.trusted_users = {}
42 self.trusted_users = {}
43 self.trusted_groups = {}
43 self.trusted_groups = {}
44 # if ucdata is not None, its keys must be a superset of cdata's
44 # if ucdata is not None, its keys must be a superset of cdata's
45 self.cdata = util.configparser()
45 self.cdata = util.configparser()
46 self.ucdata = None
46 self.ucdata = None
47 # we always trust global config files
47 # we always trust global config files
48 self.check_trusted = False
48 self.check_trusted = False
49 self.readconfig(util.rcpath())
49 self.readconfig(util.rcpath())
50 self.check_trusted = True
50 self.check_trusted = True
51 self.updateopts(verbose, debug, quiet, interactive)
51 self.updateopts(verbose, debug, quiet, interactive)
52 else:
52 else:
53 # parentui may point to an ui object which is already a child
53 # parentui may point to an ui object which is already a child
54 self.parentui = parentui.parentui or parentui
54 self.parentui = parentui.parentui or parentui
55 self.readhooks = self.parentui.readhooks[:]
55 self.readhooks = self.parentui.readhooks[:]
56 self.trusted_users = parentui.trusted_users.copy()
56 self.trusted_users = parentui.trusted_users.copy()
57 self.trusted_groups = parentui.trusted_groups.copy()
57 self.trusted_groups = parentui.trusted_groups.copy()
58 self.cdata = dupconfig(self.parentui.cdata)
58 self.cdata = dupconfig(self.parentui.cdata)
59 if self.parentui.ucdata:
59 if self.parentui.ucdata:
60 self.ucdata = dupconfig(self.parentui.ucdata)
60 self.ucdata = dupconfig(self.parentui.ucdata)
61 if self.parentui.overlay:
61 if self.parentui.overlay:
62 self.overlay = dupconfig(self.parentui.overlay)
62 self.overlay = dupconfig(self.parentui.overlay)
63
63
64 def __getattr__(self, key):
64 def __getattr__(self, key):
65 return getattr(self.parentui, key)
65 return getattr(self.parentui, key)
66
66
67 def updateopts(self, verbose=False, debug=False, quiet=False,
67 def updateopts(self, verbose=False, debug=False, quiet=False,
68 interactive=True, traceback=False, config=[]):
68 interactive=True, traceback=False, config=[]):
69 for section, name, value in config:
69 for section, name, value in config:
70 self.setconfig(section, name, value)
70 self.setconfig(section, name, value)
71
71
72 if quiet or verbose or debug:
72 if quiet or verbose or debug:
73 self.setconfig('ui', 'quiet', str(bool(quiet)))
73 self.setconfig('ui', 'quiet', str(bool(quiet)))
74 self.setconfig('ui', 'verbose', str(bool(verbose)))
74 self.setconfig('ui', 'verbose', str(bool(verbose)))
75 self.setconfig('ui', 'debug', str(bool(debug)))
75 self.setconfig('ui', 'debug', str(bool(debug)))
76
76
77 self.verbosity_constraints()
77 self.verbosity_constraints()
78
78
79 if not interactive:
79 if not interactive:
80 self.setconfig('ui', 'interactive', 'False')
80 self.setconfig('ui', 'interactive', 'False')
81 self.interactive = False
81 self.interactive = False
82
82
83 self.traceback = self.traceback or traceback
83 self.traceback = self.traceback or traceback
84
84
85 def verbosity_constraints(self):
85 def verbosity_constraints(self):
86 self.quiet = self.configbool('ui', 'quiet')
86 self.quiet = self.configbool('ui', 'quiet')
87 self.verbose = self.configbool('ui', 'verbose')
87 self.verbose = self.configbool('ui', 'verbose')
88 self.debugflag = self.configbool('ui', 'debug')
88 self.debugflag = self.configbool('ui', 'debug')
89
89
90 if self.debugflag:
90 if self.debugflag:
91 self.verbose = True
91 self.verbose = True
92 self.quiet = False
92 self.quiet = False
93 elif self.verbose and self.quiet:
93 elif self.verbose and self.quiet:
94 self.quiet = self.verbose = False
94 self.quiet = self.verbose = False
95
95
96 def _is_trusted(self, fp, f, warn=True):
96 def _is_trusted(self, fp, f, warn=True):
97 if not self.check_trusted:
97 if not self.check_trusted:
98 return True
98 return True
99 st = util.fstat(fp)
99 st = util.fstat(fp)
100 if util.isowner(fp, st):
100 if util.isowner(fp, st):
101 return True
101 return True
102 tusers = self.trusted_users
102 tusers = self.trusted_users
103 tgroups = self.trusted_groups
103 tgroups = self.trusted_groups
104 if not tusers:
104 if not tusers:
105 user = util.username()
105 user = util.username()
106 if user is not None:
106 if user is not None:
107 self.trusted_users[user] = 1
107 self.trusted_users[user] = 1
108 self.fixconfig(section='trusted')
108 self.fixconfig(section='trusted')
109 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
109 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
110 user = util.username(st.st_uid)
110 user = util.username(st.st_uid)
111 group = util.groupname(st.st_gid)
111 group = util.groupname(st.st_gid)
112 if user not in tusers and group not in tgroups:
112 if user not in tusers and group not in tgroups:
113 if warn and self.report_untrusted:
113 if warn and self.report_untrusted:
114 self.warn(_('Not trusting file %s from untrusted '
114 self.warn(_('Not trusting file %s from untrusted '
115 'user %s, group %s\n') % (f, user, group))
115 'user %s, group %s\n') % (f, user, group))
116 return False
116 return False
117 return True
117 return True
118
118
119 def readconfig(self, fn, root=None):
119 def readconfig(self, fn, root=None):
120 if isinstance(fn, basestring):
120 if isinstance(fn, basestring):
121 fn = [fn]
121 fn = [fn]
122 for f in fn:
122 for f in fn:
123 try:
123 try:
124 fp = open(f)
124 fp = open(f)
125 except IOError:
125 except IOError:
126 continue
126 continue
127 cdata = self.cdata
127 cdata = self.cdata
128 trusted = self._is_trusted(fp, f)
128 trusted = self._is_trusted(fp, f)
129 if not trusted:
129 if not trusted:
130 if self.ucdata is None:
130 if self.ucdata is None:
131 self.ucdata = dupconfig(self.cdata)
131 self.ucdata = dupconfig(self.cdata)
132 cdata = self.ucdata
132 cdata = self.ucdata
133 elif self.ucdata is not None:
133 elif self.ucdata is not None:
134 # use a separate configparser, so that we don't accidentally
134 # use a separate configparser, so that we don't accidentally
135 # override ucdata settings later on.
135 # override ucdata settings later on.
136 cdata = util.configparser()
136 cdata = util.configparser()
137
137
138 try:
138 try:
139 cdata.readfp(fp, f)
139 cdata.readfp(fp, f)
140 except ConfigParser.ParsingError, inst:
140 except ConfigParser.ParsingError, inst:
141 msg = _("Failed to parse %s\n%s") % (f, inst)
141 msg = _("Failed to parse %s\n%s") % (f, inst)
142 if trusted:
142 if trusted:
143 raise util.Abort(msg)
143 raise util.Abort(msg)
144 self.warn(_("Ignored: %s\n") % msg)
144 self.warn(_("Ignored: %s\n") % msg)
145
145
146 if trusted:
146 if trusted:
147 if cdata != self.cdata:
147 if cdata != self.cdata:
148 updateconfig(cdata, self.cdata)
148 updateconfig(cdata, self.cdata)
149 if self.ucdata is not None:
149 if self.ucdata is not None:
150 updateconfig(cdata, self.ucdata)
150 updateconfig(cdata, self.ucdata)
151 # override data from config files with data set with ui.setconfig
151 # override data from config files with data set with ui.setconfig
152 if self.overlay:
152 if self.overlay:
153 updateconfig(self.overlay, self.cdata)
153 updateconfig(self.overlay, self.cdata)
154 if root is None:
154 if root is None:
155 root = os.path.expanduser('~')
155 root = os.path.expanduser('~')
156 self.fixconfig(root=root)
156 self.fixconfig(root=root)
157 for hook in self.readhooks:
157 for hook in self.readhooks:
158 hook(self)
158 hook(self)
159
159
160 def addreadhook(self, hook):
160 def addreadhook(self, hook):
161 self.readhooks.append(hook)
161 self.readhooks.append(hook)
162
162
163 def readsections(self, filename, *sections):
163 def readsections(self, filename, *sections):
164 """Read filename and add only the specified sections to the config data
164 """Read filename and add only the specified sections to the config data
165
165
166 The settings are added to the trusted config data.
166 The settings are added to the trusted config data.
167 """
167 """
168 if not sections:
168 if not sections:
169 return
169 return
170
170
171 cdata = util.configparser()
171 cdata = util.configparser()
172 try:
172 try:
173 cdata.read(filename)
173 cdata.read(filename)
174 except ConfigParser.ParsingError, inst:
174 except ConfigParser.ParsingError, inst:
175 raise util.Abort(_("failed to parse %s\n%s") % (filename,
175 raise util.Abort(_("failed to parse %s\n%s") % (filename,
176 inst))
176 inst))
177
177
178 for section in sections:
178 for section in sections:
179 if not cdata.has_section(section):
179 if not cdata.has_section(section):
180 cdata.add_section(section)
180 cdata.add_section(section)
181
181
182 updateconfig(cdata, self.cdata, sections)
182 updateconfig(cdata, self.cdata, sections)
183 if self.ucdata:
183 if self.ucdata:
184 updateconfig(cdata, self.ucdata, sections)
184 updateconfig(cdata, self.ucdata, sections)
185
185
186 def fixconfig(self, section=None, name=None, value=None, root=None):
186 def fixconfig(self, section=None, name=None, value=None, root=None):
187 # translate paths relative to root (or home) into absolute paths
187 # translate paths relative to root (or home) into absolute paths
188 if section is None or section == 'paths':
188 if section is None or section == 'paths':
189 if root is None:
189 if root is None:
190 root = os.getcwd()
190 root = os.getcwd()
191 items = section and [(name, value)] or []
191 items = section and [(name, value)] or []
192 for cdata in self.cdata, self.ucdata, self.overlay:
192 for cdata in self.cdata, self.ucdata, self.overlay:
193 if not cdata: continue
193 if not cdata: continue
194 if not items and cdata.has_section('paths'):
194 if not items and cdata.has_section('paths'):
195 pathsitems = cdata.items('paths')
195 pathsitems = cdata.items('paths')
196 else:
196 else:
197 pathsitems = items
197 pathsitems = items
198 for n, path in pathsitems:
198 for n, path in pathsitems:
199 if path and "://" not in path and not os.path.isabs(path):
199 if path and "://" not in path and not os.path.isabs(path):
200 cdata.set("paths", n, os.path.join(root, path))
200 cdata.set("paths", n, os.path.join(root, path))
201
201
202 # update quiet/verbose/debug and interactive status
202 # update quiet/verbose/debug and interactive status
203 if section is None or section == 'ui':
203 if section is None or section == 'ui':
204 if name is None or name in ('quiet', 'verbose', 'debug'):
204 if name is None or name in ('quiet', 'verbose', 'debug'):
205 self.verbosity_constraints()
205 self.verbosity_constraints()
206
206
207 if name is None or name == 'interactive':
207 if name is None or name == 'interactive':
208 self.interactive = self.configbool("ui", "interactive", True)
208 self.interactive = self.configbool("ui", "interactive", True)
209
209
210 # update trust information
210 # update trust information
211 if (section is None or section == 'trusted') and self.trusted_users:
211 if (section is None or section == 'trusted') and self.trusted_users:
212 for user in self.configlist('trusted', 'users'):
212 for user in self.configlist('trusted', 'users'):
213 self.trusted_users[user] = 1
213 self.trusted_users[user] = 1
214 for group in self.configlist('trusted', 'groups'):
214 for group in self.configlist('trusted', 'groups'):
215 self.trusted_groups[group] = 1
215 self.trusted_groups[group] = 1
216
216
217 def setconfig(self, section, name, value):
217 def setconfig(self, section, name, value):
218 if not self.overlay:
218 if not self.overlay:
219 self.overlay = util.configparser()
219 self.overlay = util.configparser()
220 for cdata in (self.overlay, self.cdata, self.ucdata):
220 for cdata in (self.overlay, self.cdata, self.ucdata):
221 if not cdata: continue
221 if not cdata: continue
222 if not cdata.has_section(section):
222 if not cdata.has_section(section):
223 cdata.add_section(section)
223 cdata.add_section(section)
224 cdata.set(section, name, value)
224 cdata.set(section, name, value)
225 self.fixconfig(section, name, value)
225 self.fixconfig(section, name, value)
226
226
227 def _get_cdata(self, untrusted):
227 def _get_cdata(self, untrusted):
228 if untrusted and self.ucdata:
228 if untrusted and self.ucdata:
229 return self.ucdata
229 return self.ucdata
230 return self.cdata
230 return self.cdata
231
231
232 def _config(self, section, name, default, funcname, untrusted, abort):
232 def _config(self, section, name, default, funcname, untrusted, abort):
233 cdata = self._get_cdata(untrusted)
233 cdata = self._get_cdata(untrusted)
234 if cdata.has_option(section, name):
234 if cdata.has_option(section, name):
235 try:
235 try:
236 func = getattr(cdata, funcname)
236 func = getattr(cdata, funcname)
237 return func(section, name)
237 return func(section, name)
238 except ConfigParser.InterpolationError, inst:
238 except ConfigParser.InterpolationError, inst:
239 msg = _("Error in configuration section [%s] "
239 msg = _("Error in configuration section [%s] "
240 "parameter '%s':\n%s") % (section, name, inst)
240 "parameter '%s':\n%s") % (section, name, inst)
241 if abort:
241 if abort:
242 raise util.Abort(msg)
242 raise util.Abort(msg)
243 self.warn(_("Ignored: %s\n") % msg)
243 self.warn(_("Ignored: %s\n") % msg)
244 return default
244 return default
245
245
246 def _configcommon(self, section, name, default, funcname, untrusted):
246 def _configcommon(self, section, name, default, funcname, untrusted):
247 value = self._config(section, name, default, funcname,
247 value = self._config(section, name, default, funcname,
248 untrusted, abort=True)
248 untrusted, abort=True)
249 if self.debugflag and not untrusted and self.ucdata:
249 if self.debugflag and not untrusted and self.ucdata:
250 uvalue = self._config(section, name, None, funcname,
250 uvalue = self._config(section, name, None, funcname,
251 untrusted=True, abort=False)
251 untrusted=True, abort=False)
252 if uvalue is not None and uvalue != value:
252 if uvalue is not None and uvalue != value:
253 self.warn(_("Ignoring untrusted configuration option "
253 self.warn(_("Ignoring untrusted configuration option "
254 "%s.%s = %s\n") % (section, name, uvalue))
254 "%s.%s = %s\n") % (section, name, uvalue))
255 return value
255 return value
256
256
257 def config(self, section, name, default=None, untrusted=False):
257 def config(self, section, name, default=None, untrusted=False):
258 return self._configcommon(section, name, default, 'get', untrusted)
258 return self._configcommon(section, name, default, 'get', untrusted)
259
259
260 def configbool(self, section, name, default=False, untrusted=False):
260 def configbool(self, section, name, default=False, untrusted=False):
261 return self._configcommon(section, name, default, 'getboolean',
261 return self._configcommon(section, name, default, 'getboolean',
262 untrusted)
262 untrusted)
263
263
264 def configlist(self, section, name, default=None, untrusted=False):
264 def configlist(self, section, name, default=None, untrusted=False):
265 """Return a list of comma/space separated strings"""
265 """Return a list of comma/space separated strings"""
266 result = self.config(section, name, untrusted=untrusted)
266 result = self.config(section, name, untrusted=untrusted)
267 if result is None:
267 if result is None:
268 result = default or []
268 result = default or []
269 if isinstance(result, basestring):
269 if isinstance(result, basestring):
270 result = result.replace(",", " ").split()
270 result = result.replace(",", " ").split()
271 return result
271 return result
272
272
273 def has_config(self, section, untrusted=False):
273 def has_section(self, section, untrusted=False):
274 '''tell whether section exists in config.'''
274 '''tell whether section exists in config.'''
275 cdata = self._get_cdata(untrusted)
275 cdata = self._get_cdata(untrusted)
276 return cdata.has_section(section)
276 return cdata.has_section(section)
277
277
278 def _configitems(self, section, untrusted, abort):
278 def _configitems(self, section, untrusted, abort):
279 items = {}
279 items = {}
280 cdata = self._get_cdata(untrusted)
280 cdata = self._get_cdata(untrusted)
281 if cdata.has_section(section):
281 if cdata.has_section(section):
282 try:
282 try:
283 items.update(dict(cdata.items(section)))
283 items.update(dict(cdata.items(section)))
284 except ConfigParser.InterpolationError, inst:
284 except ConfigParser.InterpolationError, inst:
285 msg = _("Error in configuration section [%s]:\n"
285 msg = _("Error in configuration section [%s]:\n"
286 "%s") % (section, inst)
286 "%s") % (section, inst)
287 if abort:
287 if abort:
288 raise util.Abort(msg)
288 raise util.Abort(msg)
289 self.warn(_("Ignored: %s\n") % msg)
289 self.warn(_("Ignored: %s\n") % msg)
290 return items
290 return items
291
291
292 def configitems(self, section, untrusted=False):
292 def configitems(self, section, untrusted=False):
293 items = self._configitems(section, untrusted=untrusted, abort=True)
293 items = self._configitems(section, untrusted=untrusted, abort=True)
294 if self.debugflag and not untrusted and self.ucdata:
294 if self.debugflag and not untrusted and self.ucdata:
295 uitems = self._configitems(section, untrusted=True, abort=False)
295 uitems = self._configitems(section, untrusted=True, abort=False)
296 keys = uitems.keys()
296 keys = uitems.keys()
297 keys.sort()
297 keys.sort()
298 for k in keys:
298 for k in keys:
299 if uitems[k] != items.get(k):
299 if uitems[k] != items.get(k):
300 self.warn(_("Ignoring untrusted configuration option "
300 self.warn(_("Ignoring untrusted configuration option "
301 "%s.%s = %s\n") % (section, k, uitems[k]))
301 "%s.%s = %s\n") % (section, k, uitems[k]))
302 x = items.items()
302 x = items.items()
303 x.sort()
303 x.sort()
304 return x
304 return x
305
305
306 def walkconfig(self, untrusted=False):
306 def walkconfig(self, untrusted=False):
307 cdata = self._get_cdata(untrusted)
307 cdata = self._get_cdata(untrusted)
308 sections = cdata.sections()
308 sections = cdata.sections()
309 sections.sort()
309 sections.sort()
310 for section in sections:
310 for section in sections:
311 for name, value in self.configitems(section, untrusted):
311 for name, value in self.configitems(section, untrusted):
312 yield section, name, str(value).replace('\n', '\\n')
312 yield section, name, str(value).replace('\n', '\\n')
313
313
314 def extensions(self):
314 def extensions(self):
315 result = self.configitems("extensions")
315 result = self.configitems("extensions")
316 for i, (key, value) in enumerate(result):
316 for i, (key, value) in enumerate(result):
317 if value:
317 if value:
318 result[i] = (key, os.path.expanduser(value))
318 result[i] = (key, os.path.expanduser(value))
319 return result
319 return result
320
320
321 def hgignorefiles(self):
321 def hgignorefiles(self):
322 result = []
322 result = []
323 for key, value in self.configitems("ui"):
323 for key, value in self.configitems("ui"):
324 if key == 'ignore' or key.startswith('ignore.'):
324 if key == 'ignore' or key.startswith('ignore.'):
325 result.append(os.path.expanduser(value))
325 result.append(os.path.expanduser(value))
326 return result
326 return result
327
327
328 def username(self):
328 def username(self):
329 """Return default username to be used in commits.
329 """Return default username to be used in commits.
330
330
331 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
331 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
332 and stop searching if one of these is set.
332 and stop searching if one of these is set.
333 If not found, use ($LOGNAME or $USER or $LNAME or
333 If not found, use ($LOGNAME or $USER or $LNAME or
334 $USERNAME) +"@full.hostname".
334 $USERNAME) +"@full.hostname".
335 """
335 """
336 user = os.environ.get("HGUSER")
336 user = os.environ.get("HGUSER")
337 if user is None:
337 if user is None:
338 user = self.config("ui", "username")
338 user = self.config("ui", "username")
339 if user is None:
339 if user is None:
340 user = os.environ.get("EMAIL")
340 user = os.environ.get("EMAIL")
341 if user is None:
341 if user is None:
342 try:
342 try:
343 user = '%s@%s' % (util.getuser(), socket.getfqdn())
343 user = '%s@%s' % (util.getuser(), socket.getfqdn())
344 self.warn(_("No username found, using '%s' instead\n") % user)
344 self.warn(_("No username found, using '%s' instead\n") % user)
345 except KeyError:
345 except KeyError:
346 pass
346 pass
347 if not user:
347 if not user:
348 raise util.Abort(_("Please specify a username."))
348 raise util.Abort(_("Please specify a username."))
349 return user
349 return user
350
350
351 def shortuser(self, user):
351 def shortuser(self, user):
352 """Return a short representation of a user name or email address."""
352 """Return a short representation of a user name or email address."""
353 if not self.verbose: user = util.shortuser(user)
353 if not self.verbose: user = util.shortuser(user)
354 return user
354 return user
355
355
356 def expandpath(self, loc, default=None):
356 def expandpath(self, loc, default=None):
357 """Return repository location relative to cwd or from [paths]"""
357 """Return repository location relative to cwd or from [paths]"""
358 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
358 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
359 return loc
359 return loc
360
360
361 path = self.config("paths", loc)
361 path = self.config("paths", loc)
362 if not path and default is not None:
362 if not path and default is not None:
363 path = self.config("paths", default)
363 path = self.config("paths", default)
364 return path or loc
364 return path or loc
365
365
366 def pushbuffer(self):
366 def pushbuffer(self):
367 self.buffers.append([])
367 self.buffers.append([])
368
368
369 def popbuffer(self):
369 def popbuffer(self):
370 return "".join(self.buffers.pop())
370 return "".join(self.buffers.pop())
371
371
372 def write(self, *args):
372 def write(self, *args):
373 if self.buffers:
373 if self.buffers:
374 self.buffers[-1].extend([str(a) for a in args])
374 self.buffers[-1].extend([str(a) for a in args])
375 else:
375 else:
376 for a in args:
376 for a in args:
377 sys.stdout.write(str(a))
377 sys.stdout.write(str(a))
378
378
379 def write_err(self, *args):
379 def write_err(self, *args):
380 try:
380 try:
381 if not sys.stdout.closed: sys.stdout.flush()
381 if not sys.stdout.closed: sys.stdout.flush()
382 for a in args:
382 for a in args:
383 sys.stderr.write(str(a))
383 sys.stderr.write(str(a))
384 # stderr may be buffered under win32 when redirected to files,
384 # stderr may be buffered under win32 when redirected to files,
385 # including stdout.
385 # including stdout.
386 if not sys.stderr.closed: sys.stderr.flush()
386 if not sys.stderr.closed: sys.stderr.flush()
387 except IOError, inst:
387 except IOError, inst:
388 if inst.errno != errno.EPIPE:
388 if inst.errno != errno.EPIPE:
389 raise
389 raise
390
390
391 def flush(self):
391 def flush(self):
392 try: sys.stdout.flush()
392 try: sys.stdout.flush()
393 except: pass
393 except: pass
394 try: sys.stderr.flush()
394 try: sys.stderr.flush()
395 except: pass
395 except: pass
396
396
397 def readline(self):
397 def readline(self):
398 return sys.stdin.readline()[:-1]
398 return sys.stdin.readline()[:-1]
399 def prompt(self, msg, pat=None, default="y"):
399 def prompt(self, msg, pat=None, default="y"):
400 if not self.interactive: return default
400 if not self.interactive: return default
401 while 1:
401 while 1:
402 self.write(msg, " ")
402 self.write(msg, " ")
403 r = self.readline()
403 r = self.readline()
404 if not pat or re.match(pat, r):
404 if not pat or re.match(pat, r):
405 return r
405 return r
406 else:
406 else:
407 self.write(_("unrecognized response\n"))
407 self.write(_("unrecognized response\n"))
408 def getpass(self, prompt=None, default=None):
408 def getpass(self, prompt=None, default=None):
409 if not self.interactive: return default
409 if not self.interactive: return default
410 return getpass.getpass(prompt or _('password: '))
410 return getpass.getpass(prompt or _('password: '))
411 def status(self, *msg):
411 def status(self, *msg):
412 if not self.quiet: self.write(*msg)
412 if not self.quiet: self.write(*msg)
413 def warn(self, *msg):
413 def warn(self, *msg):
414 self.write_err(*msg)
414 self.write_err(*msg)
415 def note(self, *msg):
415 def note(self, *msg):
416 if self.verbose: self.write(*msg)
416 if self.verbose: self.write(*msg)
417 def debug(self, *msg):
417 def debug(self, *msg):
418 if self.debugflag: self.write(*msg)
418 if self.debugflag: self.write(*msg)
419 def edit(self, text, user):
419 def edit(self, text, user):
420 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
420 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
421 text=True)
421 text=True)
422 try:
422 try:
423 f = os.fdopen(fd, "w")
423 f = os.fdopen(fd, "w")
424 f.write(text)
424 f.write(text)
425 f.close()
425 f.close()
426
426
427 editor = (os.environ.get("HGEDITOR") or
427 editor = (os.environ.get("HGEDITOR") or
428 self.config("ui", "editor") or
428 self.config("ui", "editor") or
429 os.environ.get("EDITOR", "vi"))
429 os.environ.get("EDITOR", "vi"))
430
430
431 util.system("%s \"%s\"" % (editor, name),
431 util.system("%s \"%s\"" % (editor, name),
432 environ={'HGUSER': user},
432 environ={'HGUSER': user},
433 onerr=util.Abort, errprefix=_("edit failed"))
433 onerr=util.Abort, errprefix=_("edit failed"))
434
434
435 f = open(name)
435 f = open(name)
436 t = f.read()
436 t = f.read()
437 f.close()
437 f.close()
438 t = re.sub("(?m)^HG:.*\n", "", t)
438 t = re.sub("(?m)^HG:.*\n", "", t)
439 finally:
439 finally:
440 os.unlink(name)
440 os.unlink(name)
441
441
442 return t
442 return t
443
443
444 def print_exc(self):
444 def print_exc(self):
445 '''print exception traceback if traceback printing enabled.
445 '''print exception traceback if traceback printing enabled.
446 only to call in exception handler. returns true if traceback
446 only to call in exception handler. returns true if traceback
447 printed.'''
447 printed.'''
448 if self.traceback:
448 if self.traceback:
449 traceback.print_exc()
449 traceback.print_exc()
450 return self.traceback
450 return self.traceback
General Comments 0
You need to be logged in to leave comments. Login now