comparison mercurial/ui.py @ 3538: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
3537:3b07e223534b 3538: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):