Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/ui.py @ 3552:9b52239dc740
save settings from untrusted config files in a separate configparser
This untrusted configparser is a superset of the trusted configparser,
so that interpolation still works.
Also add an "untrusted" argument to ui.config* to allow querying
ui.ucdata.
With --debug, we print a warning when we read an untrusted config
file, and when we try to access a trusted setting that has one value
in the trusted configparser and another in the untrusted configparser.
author | Alexis S. L. Carvalho <alexis@cecm.usp.br> |
---|---|
date | Thu, 26 Oct 2006 19:25:45 +0200 |
parents | 3b07e223534b |
children | f7dee427cd14 |
comparison
equal
deleted
inserted
replaced
3551:3b07e223534b | 3552:9b52239dc740 |
---|---|
39 self.debugflag = debug | 39 self.debugflag = debug |
40 self.interactive = interactive | 40 self.interactive = interactive |
41 self.traceback = traceback | 41 self.traceback = traceback |
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 self.cdata = util.configparser() | 45 self.cdata = util.configparser() |
46 self.ucdata = None | |
45 self.readconfig(util.rcpath()) | 47 self.readconfig(util.rcpath()) |
46 self.updateopts(verbose, debug, quiet, interactive) | 48 self.updateopts(verbose, debug, quiet, interactive) |
47 else: | 49 else: |
48 # parentui may point to an ui object which is already a child | 50 # parentui may point to an ui object which is already a child |
49 self.parentui = parentui.parentui or parentui | 51 self.parentui = parentui.parentui or parentui |
50 self.readhooks = self.parentui.readhooks[:] | 52 self.readhooks = self.parentui.readhooks[:] |
51 self.trusted_users = parentui.trusted_users.copy() | 53 self.trusted_users = parentui.trusted_users.copy() |
52 self.trusted_groups = parentui.trusted_groups.copy() | 54 self.trusted_groups = parentui.trusted_groups.copy() |
53 self.cdata = dupconfig(self.parentui.cdata) | 55 self.cdata = dupconfig(self.parentui.cdata) |
56 if self.parentui.ucdata: | |
57 self.ucdata = dupconfig(self.parentui.ucdata) | |
54 if self.parentui.overlay: | 58 if self.parentui.overlay: |
55 self.overlay = dupconfig(self.parentui.overlay) | 59 self.overlay = dupconfig(self.parentui.overlay) |
56 | 60 |
57 def __getattr__(self, key): | 61 def __getattr__(self, key): |
58 return getattr(self.parentui, key) | 62 return getattr(self.parentui, key) |
93 st = util.fstat(fp) | 97 st = util.fstat(fp) |
94 user = util.username(st.st_uid) | 98 user = util.username(st.st_uid) |
95 group = util.groupname(st.st_gid) | 99 group = util.groupname(st.st_gid) |
96 if user not in tusers and group not in tgroups: | 100 if user not in tusers and group not in tgroups: |
97 if warn: | 101 if warn: |
98 self.warn(_('Not reading file %s from untrusted ' | 102 self.warn(_('Not trusting file %s from untrusted ' |
99 'user %s, group %s\n') % (f, user, group)) | 103 'user %s, group %s\n') % (f, user, group)) |
100 return False | 104 return False |
101 return True | 105 return True |
102 | 106 |
103 def readconfig(self, fn, root=None): | 107 def readconfig(self, fn, root=None): |
106 for f in fn: | 110 for f in fn: |
107 try: | 111 try: |
108 fp = open(f) | 112 fp = open(f) |
109 except IOError: | 113 except IOError: |
110 continue | 114 continue |
111 if not self._is_trusted(fp, f): | 115 cdata = self.cdata |
112 continue | 116 trusted = self._is_trusted(fp, f) |
117 if not trusted: | |
118 if self.ucdata is None: | |
119 self.ucdata = dupconfig(self.cdata) | |
120 cdata = self.ucdata | |
121 elif self.ucdata is not None: | |
122 # use a separate configparser, so that we don't accidentally | |
123 # override ucdata settings later on. | |
124 cdata = util.configparser() | |
125 | |
113 try: | 126 try: |
114 self.cdata.readfp(fp, f) | 127 cdata.readfp(fp, f) |
115 except ConfigParser.ParsingError, inst: | 128 except ConfigParser.ParsingError, inst: |
116 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst)) | 129 msg = _("Failed to parse %s\n%s") % (f, inst) |
130 if trusted: | |
131 raise util.Abort(msg) | |
132 self.warn(_("Ignored: %s\n") % msg) | |
133 | |
134 if trusted: | |
135 if cdata != self.cdata: | |
136 updateconfig(cdata, self.cdata) | |
137 if self.ucdata is not None: | |
138 updateconfig(cdata, self.ucdata) | |
117 # override data from config files with data set with ui.setconfig | 139 # override data from config files with data set with ui.setconfig |
118 if self.overlay: | 140 if self.overlay: |
119 updateconfig(self.overlay, self.cdata) | 141 updateconfig(self.overlay, self.cdata) |
120 if root is None: | 142 if root is None: |
121 root = os.path.expanduser('~') | 143 root = os.path.expanduser('~') |
125 | 147 |
126 def addreadhook(self, hook): | 148 def addreadhook(self, hook): |
127 self.readhooks.append(hook) | 149 self.readhooks.append(hook) |
128 | 150 |
129 def readsections(self, filename, *sections): | 151 def readsections(self, filename, *sections): |
130 "read filename and add only the specified sections to the config data" | 152 """Read filename and add only the specified sections to the config data |
153 | |
154 The settings are added to the trusted config data. | |
155 """ | |
131 if not sections: | 156 if not sections: |
132 return | 157 return |
133 | 158 |
134 cdata = util.configparser() | 159 cdata = util.configparser() |
135 try: | 160 try: |
141 for section in sections: | 166 for section in sections: |
142 if not cdata.has_section(section): | 167 if not cdata.has_section(section): |
143 cdata.add_section(section) | 168 cdata.add_section(section) |
144 | 169 |
145 updateconfig(cdata, self.cdata, sections) | 170 updateconfig(cdata, self.cdata, sections) |
171 if self.ucdata: | |
172 updateconfig(cdata, self.ucdata, sections) | |
146 | 173 |
147 def fixconfig(self, section=None, name=None, value=None, root=None): | 174 def fixconfig(self, section=None, name=None, value=None, root=None): |
148 # translate paths relative to root (or home) into absolute paths | 175 # translate paths relative to root (or home) into absolute paths |
149 if section is None or section == 'paths': | 176 if section is None or section == 'paths': |
150 if root is None: | 177 if root is None: |
151 root = os.getcwd() | 178 root = os.getcwd() |
152 items = section and [(name, value)] or [] | 179 items = section and [(name, value)] or [] |
153 for cdata in self.cdata, self.overlay: | 180 for cdata in self.cdata, self.ucdata, self.overlay: |
154 if not cdata: continue | 181 if not cdata: continue |
155 if not items and cdata.has_section('paths'): | 182 if not items and cdata.has_section('paths'): |
156 pathsitems = cdata.items('paths') | 183 pathsitems = cdata.items('paths') |
157 else: | 184 else: |
158 pathsitems = items | 185 pathsitems = items |
179 self.trusted_groups[group] = 1 | 206 self.trusted_groups[group] = 1 |
180 | 207 |
181 def setconfig(self, section, name, value): | 208 def setconfig(self, section, name, value): |
182 if not self.overlay: | 209 if not self.overlay: |
183 self.overlay = util.configparser() | 210 self.overlay = util.configparser() |
184 for cdata in (self.overlay, self.cdata): | 211 for cdata in (self.overlay, self.cdata, self.ucdata): |
212 if not cdata: continue | |
185 if not cdata.has_section(section): | 213 if not cdata.has_section(section): |
186 cdata.add_section(section) | 214 cdata.add_section(section) |
187 cdata.set(section, name, value) | 215 cdata.set(section, name, value) |
188 self.fixconfig(section, name, value) | 216 self.fixconfig(section, name, value) |
189 | 217 |
190 def _config(self, section, name, default, funcname): | 218 def _get_cdata(self, untrusted): |
191 if self.cdata.has_option(section, name): | 219 if untrusted and self.ucdata: |
220 return self.ucdata | |
221 return self.cdata | |
222 | |
223 def _config(self, section, name, default, funcname, untrusted, abort): | |
224 cdata = self._get_cdata(untrusted) | |
225 if cdata.has_option(section, name): | |
192 try: | 226 try: |
193 func = getattr(self.cdata, funcname) | 227 func = getattr(cdata, funcname) |
194 return func(section, name) | 228 return func(section, name) |
195 except ConfigParser.InterpolationError, inst: | 229 except ConfigParser.InterpolationError, inst: |
196 raise util.Abort(_("Error in configuration section [%s] " | 230 msg = _("Error in configuration section [%s] " |
197 "parameter '%s':\n%s") | 231 "parameter '%s':\n%s") % (section, name, inst) |
198 % (section, name, inst)) | 232 if abort: |
233 raise util.Abort(msg) | |
234 self.warn(_("Ignored: %s\n") % msg) | |
199 return default | 235 return default |
200 | 236 |
201 def config(self, section, name, default=None): | 237 def _configcommon(self, section, name, default, funcname, untrusted): |
202 return self._config(section, name, default, 'get') | 238 value = self._config(section, name, default, funcname, |
203 | 239 untrusted, abort=True) |
204 def configbool(self, section, name, default=False): | 240 if self.debugflag and not untrusted and self.ucdata: |
205 return self._config(section, name, default, 'getboolean') | 241 uvalue = self._config(section, name, None, funcname, |
206 | 242 untrusted=True, abort=False) |
207 def configlist(self, section, name, default=None): | 243 if uvalue is not None and uvalue != value: |
244 self.warn(_("Ignoring untrusted configuration option " | |
245 "%s.%s = %s\n") % (section, name, uvalue)) | |
246 return value | |
247 | |
248 def config(self, section, name, default=None, untrusted=False): | |
249 return self._configcommon(section, name, default, 'get', untrusted) | |
250 | |
251 def configbool(self, section, name, default=False, untrusted=False): | |
252 return self._configcommon(section, name, default, 'getboolean', | |
253 untrusted) | |
254 | |
255 def configlist(self, section, name, default=None, untrusted=False): | |
208 """Return a list of comma/space separated strings""" | 256 """Return a list of comma/space separated strings""" |
209 result = self.config(section, name) | 257 result = self.config(section, name, untrusted=untrusted) |
210 if result is None: | 258 if result is None: |
211 result = default or [] | 259 result = default or [] |
212 if isinstance(result, basestring): | 260 if isinstance(result, basestring): |
213 result = result.replace(",", " ").split() | 261 result = result.replace(",", " ").split() |
214 return result | 262 return result |
215 | 263 |
216 def has_config(self, section): | 264 def has_config(self, section, untrusted=False): |
217 '''tell whether section exists in config.''' | 265 '''tell whether section exists in config.''' |
218 return self.cdata.has_section(section) | 266 cdata = self._get_cdata(untrusted) |
219 | 267 return cdata.has_section(section) |
220 def configitems(self, section): | 268 |
269 def _configitems(self, section, untrusted, abort): | |
221 items = {} | 270 items = {} |
222 if self.cdata.has_section(section): | 271 cdata = self._get_cdata(untrusted) |
272 if cdata.has_section(section): | |
223 try: | 273 try: |
224 items.update(dict(self.cdata.items(section))) | 274 items.update(dict(cdata.items(section))) |
225 except ConfigParser.InterpolationError, inst: | 275 except ConfigParser.InterpolationError, inst: |
226 raise util.Abort(_("Error in configuration section [%s]:\n%s") | 276 msg = _("Error in configuration section [%s]:\n" |
227 % (section, inst)) | 277 "%s") % (section, inst) |
278 if abort: | |
279 raise util.Abort(msg) | |
280 self.warn(_("Ignored: %s\n") % msg) | |
281 return items | |
282 | |
283 def configitems(self, section, untrusted=False): | |
284 items = self._configitems(section, untrusted=untrusted, abort=True) | |
285 if self.debugflag and not untrusted and self.ucdata: | |
286 uitems = self._configitems(section, untrusted=True, abort=False) | |
287 keys = uitems.keys() | |
288 keys.sort() | |
289 for k in keys: | |
290 if uitems[k] != items.get(k): | |
291 self.warn(_("Ignoring untrusted configuration option " | |
292 "%s.%s = %s\n") % (section, k, uitems[k])) | |
228 x = items.items() | 293 x = items.items() |
229 x.sort() | 294 x.sort() |
230 return x | 295 return x |
231 | 296 |
232 def walkconfig(self): | 297 def walkconfig(self, untrusted=False): |
233 sections = self.cdata.sections() | 298 cdata = self._get_cdata(untrusted) |
299 sections = cdata.sections() | |
234 sections.sort() | 300 sections.sort() |
235 for section in sections: | 301 for section in sections: |
236 for name, value in self.configitems(section): | 302 for name, value in self.configitems(section, untrusted): |
237 yield section, name, value.replace('\n', '\\n') | 303 yield section, name, value.replace('\n', '\\n') |
238 | 304 |
239 def extensions(self): | 305 def extensions(self): |
240 result = self.configitems("extensions") | 306 result = self.configitems("extensions") |
241 for i, (key, value) in enumerate(result): | 307 for i, (key, value) in enumerate(result): |