##// END OF EJS Templates
configitems: register the 'web.collapse' config
Boris Feld -
r34231:f6d25ffc default
parent child Browse files
Show More
@@ -1,622 +1,625
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import functools
10 import functools
11
11
12 from . import (
12 from . import (
13 error,
13 error,
14 )
14 )
15
15
16 def loadconfigtable(ui, extname, configtable):
16 def loadconfigtable(ui, extname, configtable):
17 """update config item known to the ui with the extension ones"""
17 """update config item known to the ui with the extension ones"""
18 for section, items in configtable.items():
18 for section, items in configtable.items():
19 knownitems = ui._knownconfig.setdefault(section, {})
19 knownitems = ui._knownconfig.setdefault(section, {})
20 knownkeys = set(knownitems)
20 knownkeys = set(knownitems)
21 newkeys = set(items)
21 newkeys = set(items)
22 for key in sorted(knownkeys & newkeys):
22 for key in sorted(knownkeys & newkeys):
23 msg = "extension '%s' overwrite config item '%s.%s'"
23 msg = "extension '%s' overwrite config item '%s.%s'"
24 msg %= (extname, section, key)
24 msg %= (extname, section, key)
25 ui.develwarn(msg, config='warn-config')
25 ui.develwarn(msg, config='warn-config')
26
26
27 knownitems.update(items)
27 knownitems.update(items)
28
28
29 class configitem(object):
29 class configitem(object):
30 """represent a known config item
30 """represent a known config item
31
31
32 :section: the official config section where to find this item,
32 :section: the official config section where to find this item,
33 :name: the official name within the section,
33 :name: the official name within the section,
34 :default: default value for this item,
34 :default: default value for this item,
35 :alias: optional list of tuples as alternatives.
35 :alias: optional list of tuples as alternatives.
36 """
36 """
37
37
38 def __init__(self, section, name, default=None, alias=()):
38 def __init__(self, section, name, default=None, alias=()):
39 self.section = section
39 self.section = section
40 self.name = name
40 self.name = name
41 self.default = default
41 self.default = default
42 self.alias = list(alias)
42 self.alias = list(alias)
43
43
44 coreitems = {}
44 coreitems = {}
45
45
46 def _register(configtable, *args, **kwargs):
46 def _register(configtable, *args, **kwargs):
47 item = configitem(*args, **kwargs)
47 item = configitem(*args, **kwargs)
48 section = configtable.setdefault(item.section, {})
48 section = configtable.setdefault(item.section, {})
49 if item.name in section:
49 if item.name in section:
50 msg = "duplicated config item registration for '%s.%s'"
50 msg = "duplicated config item registration for '%s.%s'"
51 raise error.ProgrammingError(msg % (item.section, item.name))
51 raise error.ProgrammingError(msg % (item.section, item.name))
52 section[item.name] = item
52 section[item.name] = item
53
53
54 # special value for case where the default is derived from other values
54 # special value for case where the default is derived from other values
55 dynamicdefault = object()
55 dynamicdefault = object()
56
56
57 # Registering actual config items
57 # Registering actual config items
58
58
59 def getitemregister(configtable):
59 def getitemregister(configtable):
60 return functools.partial(_register, configtable)
60 return functools.partial(_register, configtable)
61
61
62 coreconfigitem = getitemregister(coreitems)
62 coreconfigitem = getitemregister(coreitems)
63
63
64 coreconfigitem('auth', 'cookiefile',
64 coreconfigitem('auth', 'cookiefile',
65 default=None,
65 default=None,
66 )
66 )
67 # bookmarks.pushing: internal hack for discovery
67 # bookmarks.pushing: internal hack for discovery
68 coreconfigitem('bookmarks', 'pushing',
68 coreconfigitem('bookmarks', 'pushing',
69 default=list,
69 default=list,
70 )
70 )
71 # bundle.mainreporoot: internal hack for bundlerepo
71 # bundle.mainreporoot: internal hack for bundlerepo
72 coreconfigitem('bundle', 'mainreporoot',
72 coreconfigitem('bundle', 'mainreporoot',
73 default='',
73 default='',
74 )
74 )
75 # bundle.reorder: experimental config
75 # bundle.reorder: experimental config
76 coreconfigitem('bundle', 'reorder',
76 coreconfigitem('bundle', 'reorder',
77 default='auto',
77 default='auto',
78 )
78 )
79 coreconfigitem('censor', 'policy',
79 coreconfigitem('censor', 'policy',
80 default='abort',
80 default='abort',
81 )
81 )
82 coreconfigitem('chgserver', 'idletimeout',
82 coreconfigitem('chgserver', 'idletimeout',
83 default=3600,
83 default=3600,
84 )
84 )
85 coreconfigitem('chgserver', 'skiphash',
85 coreconfigitem('chgserver', 'skiphash',
86 default=False,
86 default=False,
87 )
87 )
88 coreconfigitem('cmdserver', 'log',
88 coreconfigitem('cmdserver', 'log',
89 default=None,
89 default=None,
90 )
90 )
91 coreconfigitem('color', 'mode',
91 coreconfigitem('color', 'mode',
92 default='auto',
92 default='auto',
93 )
93 )
94 coreconfigitem('color', 'pagermode',
94 coreconfigitem('color', 'pagermode',
95 default=dynamicdefault,
95 default=dynamicdefault,
96 )
96 )
97 coreconfigitem('commands', 'status.relative',
97 coreconfigitem('commands', 'status.relative',
98 default=False,
98 default=False,
99 )
99 )
100 coreconfigitem('commands', 'status.skipstates',
100 coreconfigitem('commands', 'status.skipstates',
101 default=[],
101 default=[],
102 )
102 )
103 coreconfigitem('commands', 'status.verbose',
103 coreconfigitem('commands', 'status.verbose',
104 default=False,
104 default=False,
105 )
105 )
106 coreconfigitem('commands', 'update.requiredest',
106 coreconfigitem('commands', 'update.requiredest',
107 default=False,
107 default=False,
108 )
108 )
109 coreconfigitem('devel', 'all-warnings',
109 coreconfigitem('devel', 'all-warnings',
110 default=False,
110 default=False,
111 )
111 )
112 coreconfigitem('devel', 'bundle2.debug',
112 coreconfigitem('devel', 'bundle2.debug',
113 default=False,
113 default=False,
114 )
114 )
115 coreconfigitem('devel', 'check-locks',
115 coreconfigitem('devel', 'check-locks',
116 default=False,
116 default=False,
117 )
117 )
118 coreconfigitem('devel', 'check-relroot',
118 coreconfigitem('devel', 'check-relroot',
119 default=False,
119 default=False,
120 )
120 )
121 coreconfigitem('devel', 'default-date',
121 coreconfigitem('devel', 'default-date',
122 default=None,
122 default=None,
123 )
123 )
124 coreconfigitem('devel', 'deprec-warn',
124 coreconfigitem('devel', 'deprec-warn',
125 default=False,
125 default=False,
126 )
126 )
127 coreconfigitem('devel', 'disableloaddefaultcerts',
127 coreconfigitem('devel', 'disableloaddefaultcerts',
128 default=False,
128 default=False,
129 )
129 )
130 coreconfigitem('devel', 'legacy.exchange',
130 coreconfigitem('devel', 'legacy.exchange',
131 default=list,
131 default=list,
132 )
132 )
133 coreconfigitem('devel', 'servercafile',
133 coreconfigitem('devel', 'servercafile',
134 default='',
134 default='',
135 )
135 )
136 coreconfigitem('devel', 'serverexactprotocol',
136 coreconfigitem('devel', 'serverexactprotocol',
137 default='',
137 default='',
138 )
138 )
139 coreconfigitem('devel', 'serverrequirecert',
139 coreconfigitem('devel', 'serverrequirecert',
140 default=False,
140 default=False,
141 )
141 )
142 coreconfigitem('devel', 'strip-obsmarkers',
142 coreconfigitem('devel', 'strip-obsmarkers',
143 default=True,
143 default=True,
144 )
144 )
145 coreconfigitem('email', 'charsets',
145 coreconfigitem('email', 'charsets',
146 default=list,
146 default=list,
147 )
147 )
148 coreconfigitem('email', 'method',
148 coreconfigitem('email', 'method',
149 default='smtp',
149 default='smtp',
150 )
150 )
151 coreconfigitem('experimental', 'bundle-phases',
151 coreconfigitem('experimental', 'bundle-phases',
152 default=False,
152 default=False,
153 )
153 )
154 coreconfigitem('experimental', 'bundle2-advertise',
154 coreconfigitem('experimental', 'bundle2-advertise',
155 default=True,
155 default=True,
156 )
156 )
157 coreconfigitem('experimental', 'bundle2-output-capture',
157 coreconfigitem('experimental', 'bundle2-output-capture',
158 default=False,
158 default=False,
159 )
159 )
160 coreconfigitem('experimental', 'bundle2.pushback',
160 coreconfigitem('experimental', 'bundle2.pushback',
161 default=False,
161 default=False,
162 )
162 )
163 coreconfigitem('experimental', 'bundle2lazylocking',
163 coreconfigitem('experimental', 'bundle2lazylocking',
164 default=False,
164 default=False,
165 )
165 )
166 coreconfigitem('experimental', 'bundlecomplevel',
166 coreconfigitem('experimental', 'bundlecomplevel',
167 default=None,
167 default=None,
168 )
168 )
169 coreconfigitem('experimental', 'changegroup3',
169 coreconfigitem('experimental', 'changegroup3',
170 default=False,
170 default=False,
171 )
171 )
172 coreconfigitem('experimental', 'clientcompressionengines',
172 coreconfigitem('experimental', 'clientcompressionengines',
173 default=list,
173 default=list,
174 )
174 )
175 coreconfigitem('experimental', 'copytrace',
175 coreconfigitem('experimental', 'copytrace',
176 default='on',
176 default='on',
177 )
177 )
178 coreconfigitem('experimental', 'crecordtest',
178 coreconfigitem('experimental', 'crecordtest',
179 default=None,
179 default=None,
180 )
180 )
181 coreconfigitem('experimental', 'editortmpinhg',
181 coreconfigitem('experimental', 'editortmpinhg',
182 default=False,
182 default=False,
183 )
183 )
184 coreconfigitem('experimental', 'stabilization',
184 coreconfigitem('experimental', 'stabilization',
185 default=list,
185 default=list,
186 alias=[('experimental', 'evolution')],
186 alias=[('experimental', 'evolution')],
187 )
187 )
188 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
188 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
189 default=False,
189 default=False,
190 alias=[('experimental', 'evolution.bundle-obsmarker')],
190 alias=[('experimental', 'evolution.bundle-obsmarker')],
191 )
191 )
192 coreconfigitem('experimental', 'stabilization.track-operation',
192 coreconfigitem('experimental', 'stabilization.track-operation',
193 default=False,
193 default=False,
194 alias=[('experimental', 'evolution.track-operation')]
194 alias=[('experimental', 'evolution.track-operation')]
195 )
195 )
196 coreconfigitem('experimental', 'exportableenviron',
196 coreconfigitem('experimental', 'exportableenviron',
197 default=list,
197 default=list,
198 )
198 )
199 coreconfigitem('experimental', 'extendedheader.index',
199 coreconfigitem('experimental', 'extendedheader.index',
200 default=None,
200 default=None,
201 )
201 )
202 coreconfigitem('experimental', 'extendedheader.similarity',
202 coreconfigitem('experimental', 'extendedheader.similarity',
203 default=False,
203 default=False,
204 )
204 )
205 coreconfigitem('experimental', 'format.compression',
205 coreconfigitem('experimental', 'format.compression',
206 default='zlib',
206 default='zlib',
207 )
207 )
208 coreconfigitem('experimental', 'graphshorten',
208 coreconfigitem('experimental', 'graphshorten',
209 default=False,
209 default=False,
210 )
210 )
211 coreconfigitem('experimental', 'hook-track-tags',
211 coreconfigitem('experimental', 'hook-track-tags',
212 default=False,
212 default=False,
213 )
213 )
214 coreconfigitem('experimental', 'httppostargs',
214 coreconfigitem('experimental', 'httppostargs',
215 default=False,
215 default=False,
216 )
216 )
217 coreconfigitem('experimental', 'manifestv2',
217 coreconfigitem('experimental', 'manifestv2',
218 default=False,
218 default=False,
219 )
219 )
220 coreconfigitem('experimental', 'mergedriver',
220 coreconfigitem('experimental', 'mergedriver',
221 default=None,
221 default=None,
222 )
222 )
223 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
223 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
224 default=False,
224 default=False,
225 )
225 )
226 coreconfigitem('experimental', 'rebase.multidest',
226 coreconfigitem('experimental', 'rebase.multidest',
227 default=False,
227 default=False,
228 )
228 )
229 coreconfigitem('experimental', 'revertalternateinteractivemode',
229 coreconfigitem('experimental', 'revertalternateinteractivemode',
230 default=True,
230 default=True,
231 )
231 )
232 coreconfigitem('experimental', 'revlogv2',
232 coreconfigitem('experimental', 'revlogv2',
233 default=None,
233 default=None,
234 )
234 )
235 coreconfigitem('experimental', 'spacemovesdown',
235 coreconfigitem('experimental', 'spacemovesdown',
236 default=False,
236 default=False,
237 )
237 )
238 coreconfigitem('experimental', 'treemanifest',
238 coreconfigitem('experimental', 'treemanifest',
239 default=False,
239 default=False,
240 )
240 )
241 coreconfigitem('experimental', 'updatecheck',
241 coreconfigitem('experimental', 'updatecheck',
242 default=None,
242 default=None,
243 )
243 )
244 coreconfigitem('format', 'aggressivemergedeltas',
244 coreconfigitem('format', 'aggressivemergedeltas',
245 default=False,
245 default=False,
246 )
246 )
247 coreconfigitem('format', 'chunkcachesize',
247 coreconfigitem('format', 'chunkcachesize',
248 default=None,
248 default=None,
249 )
249 )
250 coreconfigitem('format', 'dotencode',
250 coreconfigitem('format', 'dotencode',
251 default=True,
251 default=True,
252 )
252 )
253 coreconfigitem('format', 'generaldelta',
253 coreconfigitem('format', 'generaldelta',
254 default=False,
254 default=False,
255 )
255 )
256 coreconfigitem('format', 'manifestcachesize',
256 coreconfigitem('format', 'manifestcachesize',
257 default=None,
257 default=None,
258 )
258 )
259 coreconfigitem('format', 'maxchainlen',
259 coreconfigitem('format', 'maxchainlen',
260 default=None,
260 default=None,
261 )
261 )
262 coreconfigitem('format', 'obsstore-version',
262 coreconfigitem('format', 'obsstore-version',
263 default=None,
263 default=None,
264 )
264 )
265 coreconfigitem('format', 'usefncache',
265 coreconfigitem('format', 'usefncache',
266 default=True,
266 default=True,
267 )
267 )
268 coreconfigitem('format', 'usegeneraldelta',
268 coreconfigitem('format', 'usegeneraldelta',
269 default=True,
269 default=True,
270 )
270 )
271 coreconfigitem('format', 'usestore',
271 coreconfigitem('format', 'usestore',
272 default=True,
272 default=True,
273 )
273 )
274 coreconfigitem('hostsecurity', 'ciphers',
274 coreconfigitem('hostsecurity', 'ciphers',
275 default=None,
275 default=None,
276 )
276 )
277 coreconfigitem('hostsecurity', 'disabletls10warning',
277 coreconfigitem('hostsecurity', 'disabletls10warning',
278 default=False,
278 default=False,
279 )
279 )
280 coreconfigitem('http_proxy', 'always',
280 coreconfigitem('http_proxy', 'always',
281 default=False,
281 default=False,
282 )
282 )
283 coreconfigitem('http_proxy', 'host',
283 coreconfigitem('http_proxy', 'host',
284 default=None,
284 default=None,
285 )
285 )
286 coreconfigitem('http_proxy', 'no',
286 coreconfigitem('http_proxy', 'no',
287 default=list,
287 default=list,
288 )
288 )
289 coreconfigitem('http_proxy', 'passwd',
289 coreconfigitem('http_proxy', 'passwd',
290 default=None,
290 default=None,
291 )
291 )
292 coreconfigitem('http_proxy', 'user',
292 coreconfigitem('http_proxy', 'user',
293 default=None,
293 default=None,
294 )
294 )
295 coreconfigitem('merge', 'followcopies',
295 coreconfigitem('merge', 'followcopies',
296 default=True,
296 default=True,
297 )
297 )
298 coreconfigitem('pager', 'ignore',
298 coreconfigitem('pager', 'ignore',
299 default=list,
299 default=list,
300 )
300 )
301 coreconfigitem('patch', 'eol',
301 coreconfigitem('patch', 'eol',
302 default='strict',
302 default='strict',
303 )
303 )
304 coreconfigitem('patch', 'fuzz',
304 coreconfigitem('patch', 'fuzz',
305 default=2,
305 default=2,
306 )
306 )
307 coreconfigitem('paths', 'default',
307 coreconfigitem('paths', 'default',
308 default=None,
308 default=None,
309 )
309 )
310 coreconfigitem('paths', 'default-push',
310 coreconfigitem('paths', 'default-push',
311 default=None,
311 default=None,
312 )
312 )
313 coreconfigitem('phases', 'checksubrepos',
313 coreconfigitem('phases', 'checksubrepos',
314 default='follow',
314 default='follow',
315 )
315 )
316 coreconfigitem('phases', 'publish',
316 coreconfigitem('phases', 'publish',
317 default=True,
317 default=True,
318 )
318 )
319 coreconfigitem('profiling', 'enabled',
319 coreconfigitem('profiling', 'enabled',
320 default=False,
320 default=False,
321 )
321 )
322 coreconfigitem('profiling', 'format',
322 coreconfigitem('profiling', 'format',
323 default='text',
323 default='text',
324 )
324 )
325 coreconfigitem('profiling', 'freq',
325 coreconfigitem('profiling', 'freq',
326 default=1000,
326 default=1000,
327 )
327 )
328 coreconfigitem('profiling', 'limit',
328 coreconfigitem('profiling', 'limit',
329 default=30,
329 default=30,
330 )
330 )
331 coreconfigitem('profiling', 'nested',
331 coreconfigitem('profiling', 'nested',
332 default=0,
332 default=0,
333 )
333 )
334 coreconfigitem('profiling', 'sort',
334 coreconfigitem('profiling', 'sort',
335 default='inlinetime',
335 default='inlinetime',
336 )
336 )
337 coreconfigitem('profiling', 'statformat',
337 coreconfigitem('profiling', 'statformat',
338 default='hotpath',
338 default='hotpath',
339 )
339 )
340 coreconfigitem('progress', 'assume-tty',
340 coreconfigitem('progress', 'assume-tty',
341 default=False,
341 default=False,
342 )
342 )
343 coreconfigitem('progress', 'changedelay',
343 coreconfigitem('progress', 'changedelay',
344 default=1,
344 default=1,
345 )
345 )
346 coreconfigitem('progress', 'clear-complete',
346 coreconfigitem('progress', 'clear-complete',
347 default=True,
347 default=True,
348 )
348 )
349 coreconfigitem('progress', 'debug',
349 coreconfigitem('progress', 'debug',
350 default=False,
350 default=False,
351 )
351 )
352 coreconfigitem('progress', 'delay',
352 coreconfigitem('progress', 'delay',
353 default=3,
353 default=3,
354 )
354 )
355 coreconfigitem('progress', 'disable',
355 coreconfigitem('progress', 'disable',
356 default=False,
356 default=False,
357 )
357 )
358 coreconfigitem('progress', 'estimate',
358 coreconfigitem('progress', 'estimate',
359 default=2,
359 default=2,
360 )
360 )
361 coreconfigitem('progress', 'refresh',
361 coreconfigitem('progress', 'refresh',
362 default=0.1,
362 default=0.1,
363 )
363 )
364 coreconfigitem('progress', 'width',
364 coreconfigitem('progress', 'width',
365 default=dynamicdefault,
365 default=dynamicdefault,
366 )
366 )
367 coreconfigitem('push', 'pushvars.server',
367 coreconfigitem('push', 'pushvars.server',
368 default=False,
368 default=False,
369 )
369 )
370 coreconfigitem('server', 'bundle1',
370 coreconfigitem('server', 'bundle1',
371 default=True,
371 default=True,
372 )
372 )
373 coreconfigitem('server', 'bundle1gd',
373 coreconfigitem('server', 'bundle1gd',
374 default=None,
374 default=None,
375 )
375 )
376 coreconfigitem('server', 'compressionengines',
376 coreconfigitem('server', 'compressionengines',
377 default=list,
377 default=list,
378 )
378 )
379 coreconfigitem('server', 'concurrent-push-mode',
379 coreconfigitem('server', 'concurrent-push-mode',
380 default='strict',
380 default='strict',
381 )
381 )
382 coreconfigitem('server', 'disablefullbundle',
382 coreconfigitem('server', 'disablefullbundle',
383 default=False,
383 default=False,
384 )
384 )
385 coreconfigitem('server', 'maxhttpheaderlen',
385 coreconfigitem('server', 'maxhttpheaderlen',
386 default=1024,
386 default=1024,
387 )
387 )
388 coreconfigitem('server', 'preferuncompressed',
388 coreconfigitem('server', 'preferuncompressed',
389 default=False,
389 default=False,
390 )
390 )
391 coreconfigitem('server', 'uncompressed',
391 coreconfigitem('server', 'uncompressed',
392 default=True,
392 default=True,
393 )
393 )
394 coreconfigitem('server', 'uncompressedallowsecret',
394 coreconfigitem('server', 'uncompressedallowsecret',
395 default=False,
395 default=False,
396 )
396 )
397 coreconfigitem('server', 'validate',
397 coreconfigitem('server', 'validate',
398 default=False,
398 default=False,
399 )
399 )
400 coreconfigitem('server', 'zliblevel',
400 coreconfigitem('server', 'zliblevel',
401 default=-1,
401 default=-1,
402 )
402 )
403 coreconfigitem('smtp', 'host',
403 coreconfigitem('smtp', 'host',
404 default=None,
404 default=None,
405 )
405 )
406 coreconfigitem('smtp', 'local_hostname',
406 coreconfigitem('smtp', 'local_hostname',
407 default=None,
407 default=None,
408 )
408 )
409 coreconfigitem('smtp', 'password',
409 coreconfigitem('smtp', 'password',
410 default=None,
410 default=None,
411 )
411 )
412 coreconfigitem('smtp', 'tls',
412 coreconfigitem('smtp', 'tls',
413 default='none',
413 default='none',
414 )
414 )
415 coreconfigitem('smtp', 'username',
415 coreconfigitem('smtp', 'username',
416 default=None,
416 default=None,
417 )
417 )
418 coreconfigitem('sparse', 'missingwarning',
418 coreconfigitem('sparse', 'missingwarning',
419 default=True,
419 default=True,
420 )
420 )
421 coreconfigitem('trusted', 'groups',
421 coreconfigitem('trusted', 'groups',
422 default=list,
422 default=list,
423 )
423 )
424 coreconfigitem('trusted', 'users',
424 coreconfigitem('trusted', 'users',
425 default=list,
425 default=list,
426 )
426 )
427 coreconfigitem('ui', '_usedassubrepo',
427 coreconfigitem('ui', '_usedassubrepo',
428 default=False,
428 default=False,
429 )
429 )
430 coreconfigitem('ui', 'allowemptycommit',
430 coreconfigitem('ui', 'allowemptycommit',
431 default=False,
431 default=False,
432 )
432 )
433 coreconfigitem('ui', 'archivemeta',
433 coreconfigitem('ui', 'archivemeta',
434 default=True,
434 default=True,
435 )
435 )
436 coreconfigitem('ui', 'askusername',
436 coreconfigitem('ui', 'askusername',
437 default=False,
437 default=False,
438 )
438 )
439 coreconfigitem('ui', 'clonebundlefallback',
439 coreconfigitem('ui', 'clonebundlefallback',
440 default=False,
440 default=False,
441 )
441 )
442 coreconfigitem('ui', 'clonebundleprefers',
442 coreconfigitem('ui', 'clonebundleprefers',
443 default=list,
443 default=list,
444 )
444 )
445 coreconfigitem('ui', 'clonebundles',
445 coreconfigitem('ui', 'clonebundles',
446 default=True,
446 default=True,
447 )
447 )
448 coreconfigitem('ui', 'color',
448 coreconfigitem('ui', 'color',
449 default='auto',
449 default='auto',
450 )
450 )
451 coreconfigitem('ui', 'commitsubrepos',
451 coreconfigitem('ui', 'commitsubrepos',
452 default=False,
452 default=False,
453 )
453 )
454 coreconfigitem('ui', 'debug',
454 coreconfigitem('ui', 'debug',
455 default=False,
455 default=False,
456 )
456 )
457 coreconfigitem('ui', 'debugger',
457 coreconfigitem('ui', 'debugger',
458 default=None,
458 default=None,
459 )
459 )
460 coreconfigitem('ui', 'fallbackencoding',
460 coreconfigitem('ui', 'fallbackencoding',
461 default=None,
461 default=None,
462 )
462 )
463 coreconfigitem('ui', 'forcecwd',
463 coreconfigitem('ui', 'forcecwd',
464 default=None,
464 default=None,
465 )
465 )
466 coreconfigitem('ui', 'forcemerge',
466 coreconfigitem('ui', 'forcemerge',
467 default=None,
467 default=None,
468 )
468 )
469 coreconfigitem('ui', 'formatdebug',
469 coreconfigitem('ui', 'formatdebug',
470 default=False,
470 default=False,
471 )
471 )
472 coreconfigitem('ui', 'formatjson',
472 coreconfigitem('ui', 'formatjson',
473 default=False,
473 default=False,
474 )
474 )
475 coreconfigitem('ui', 'formatted',
475 coreconfigitem('ui', 'formatted',
476 default=None,
476 default=None,
477 )
477 )
478 coreconfigitem('ui', 'graphnodetemplate',
478 coreconfigitem('ui', 'graphnodetemplate',
479 default=None,
479 default=None,
480 )
480 )
481 coreconfigitem('ui', 'http2debuglevel',
481 coreconfigitem('ui', 'http2debuglevel',
482 default=None,
482 default=None,
483 )
483 )
484 coreconfigitem('ui', 'interactive',
484 coreconfigitem('ui', 'interactive',
485 default=None,
485 default=None,
486 )
486 )
487 coreconfigitem('ui', 'interface',
487 coreconfigitem('ui', 'interface',
488 default=None,
488 default=None,
489 )
489 )
490 coreconfigitem('ui', 'logblockedtimes',
490 coreconfigitem('ui', 'logblockedtimes',
491 default=False,
491 default=False,
492 )
492 )
493 coreconfigitem('ui', 'logtemplate',
493 coreconfigitem('ui', 'logtemplate',
494 default=None,
494 default=None,
495 )
495 )
496 coreconfigitem('ui', 'merge',
496 coreconfigitem('ui', 'merge',
497 default=None,
497 default=None,
498 )
498 )
499 coreconfigitem('ui', 'mergemarkers',
499 coreconfigitem('ui', 'mergemarkers',
500 default='basic',
500 default='basic',
501 )
501 )
502 coreconfigitem('ui', 'mergemarkertemplate',
502 coreconfigitem('ui', 'mergemarkertemplate',
503 default=('{node|short} '
503 default=('{node|short} '
504 '{ifeq(tags, "tip", "", '
504 '{ifeq(tags, "tip", "", '
505 'ifeq(tags, "", "", "{tags} "))}'
505 'ifeq(tags, "", "", "{tags} "))}'
506 '{if(bookmarks, "{bookmarks} ")}'
506 '{if(bookmarks, "{bookmarks} ")}'
507 '{ifeq(branch, "default", "", "{branch} ")}'
507 '{ifeq(branch, "default", "", "{branch} ")}'
508 '- {author|user}: {desc|firstline}')
508 '- {author|user}: {desc|firstline}')
509 )
509 )
510 coreconfigitem('ui', 'nontty',
510 coreconfigitem('ui', 'nontty',
511 default=False,
511 default=False,
512 )
512 )
513 coreconfigitem('ui', 'origbackuppath',
513 coreconfigitem('ui', 'origbackuppath',
514 default=None,
514 default=None,
515 )
515 )
516 coreconfigitem('ui', 'paginate',
516 coreconfigitem('ui', 'paginate',
517 default=True,
517 default=True,
518 )
518 )
519 coreconfigitem('ui', 'patch',
519 coreconfigitem('ui', 'patch',
520 default=None,
520 default=None,
521 )
521 )
522 coreconfigitem('ui', 'portablefilenames',
522 coreconfigitem('ui', 'portablefilenames',
523 default='warn',
523 default='warn',
524 )
524 )
525 coreconfigitem('ui', 'promptecho',
525 coreconfigitem('ui', 'promptecho',
526 default=False,
526 default=False,
527 )
527 )
528 coreconfigitem('ui', 'quiet',
528 coreconfigitem('ui', 'quiet',
529 default=False,
529 default=False,
530 )
530 )
531 coreconfigitem('ui', 'quietbookmarkmove',
531 coreconfigitem('ui', 'quietbookmarkmove',
532 default=False,
532 default=False,
533 )
533 )
534 coreconfigitem('ui', 'remotecmd',
534 coreconfigitem('ui', 'remotecmd',
535 default='hg',
535 default='hg',
536 )
536 )
537 coreconfigitem('ui', 'report_untrusted',
537 coreconfigitem('ui', 'report_untrusted',
538 default=True,
538 default=True,
539 )
539 )
540 coreconfigitem('ui', 'rollback',
540 coreconfigitem('ui', 'rollback',
541 default=True,
541 default=True,
542 )
542 )
543 coreconfigitem('ui', 'slash',
543 coreconfigitem('ui', 'slash',
544 default=False,
544 default=False,
545 )
545 )
546 coreconfigitem('ui', 'ssh',
546 coreconfigitem('ui', 'ssh',
547 default='ssh',
547 default='ssh',
548 )
548 )
549 coreconfigitem('ui', 'statuscopies',
549 coreconfigitem('ui', 'statuscopies',
550 default=False,
550 default=False,
551 )
551 )
552 coreconfigitem('ui', 'strict',
552 coreconfigitem('ui', 'strict',
553 default=False,
553 default=False,
554 )
554 )
555 coreconfigitem('ui', 'style',
555 coreconfigitem('ui', 'style',
556 default='',
556 default='',
557 )
557 )
558 coreconfigitem('ui', 'supportcontact',
558 coreconfigitem('ui', 'supportcontact',
559 default=None,
559 default=None,
560 )
560 )
561 coreconfigitem('ui', 'textwidth',
561 coreconfigitem('ui', 'textwidth',
562 default=78,
562 default=78,
563 )
563 )
564 coreconfigitem('ui', 'timeout',
564 coreconfigitem('ui', 'timeout',
565 default='600',
565 default='600',
566 )
566 )
567 coreconfigitem('ui', 'traceback',
567 coreconfigitem('ui', 'traceback',
568 default=False,
568 default=False,
569 )
569 )
570 coreconfigitem('ui', 'tweakdefaults',
570 coreconfigitem('ui', 'tweakdefaults',
571 default=False,
571 default=False,
572 )
572 )
573 coreconfigitem('ui', 'usehttp2',
573 coreconfigitem('ui', 'usehttp2',
574 default=False,
574 default=False,
575 )
575 )
576 coreconfigitem('ui', 'username',
576 coreconfigitem('ui', 'username',
577 alias=[('ui', 'user')]
577 alias=[('ui', 'user')]
578 )
578 )
579 coreconfigitem('ui', 'verbose',
579 coreconfigitem('ui', 'verbose',
580 default=False,
580 default=False,
581 )
581 )
582 coreconfigitem('verify', 'skipflags',
582 coreconfigitem('verify', 'skipflags',
583 default=None,
583 default=None,
584 )
584 )
585 coreconfigitem('web', 'accesslog',
585 coreconfigitem('web', 'accesslog',
586 default='-',
586 default='-',
587 )
587 )
588 coreconfigitem('web', 'address',
588 coreconfigitem('web', 'address',
589 default='',
589 default='',
590 )
590 )
591 coreconfigitem('web', 'allow_archive',
591 coreconfigitem('web', 'allow_archive',
592 default=list,
592 default=list,
593 )
593 )
594 coreconfigitem('web', 'allow_read',
594 coreconfigitem('web', 'allow_read',
595 default=list,
595 default=list,
596 )
596 )
597 coreconfigitem('web', 'baseurl',
597 coreconfigitem('web', 'baseurl',
598 default=None,
598 default=None,
599 )
599 )
600 coreconfigitem('web', 'cacerts',
600 coreconfigitem('web', 'cacerts',
601 default=None,
601 default=None,
602 )
602 )
603 coreconfigitem('web', 'certificate',
603 coreconfigitem('web', 'certificate',
604 default=None,
604 default=None,
605 )
605 )
606 coreconfigitem('web', 'collapse',
607 default=False,
608 )
606 coreconfigitem('worker', 'backgroundclose',
609 coreconfigitem('worker', 'backgroundclose',
607 default=dynamicdefault,
610 default=dynamicdefault,
608 )
611 )
609 # Windows defaults to a limit of 512 open files. A buffer of 128
612 # Windows defaults to a limit of 512 open files. A buffer of 128
610 # should give us enough headway.
613 # should give us enough headway.
611 coreconfigitem('worker', 'backgroundclosemaxqueue',
614 coreconfigitem('worker', 'backgroundclosemaxqueue',
612 default=384,
615 default=384,
613 )
616 )
614 coreconfigitem('worker', 'backgroundcloseminfilecount',
617 coreconfigitem('worker', 'backgroundcloseminfilecount',
615 default=2048,
618 default=2048,
616 )
619 )
617 coreconfigitem('worker', 'backgroundclosethreadcount',
620 coreconfigitem('worker', 'backgroundclosethreadcount',
618 default=4,
621 default=4,
619 )
622 )
620 coreconfigitem('worker', 'numcpus',
623 coreconfigitem('worker', 'numcpus',
621 default=None,
624 default=None,
622 )
625 )
@@ -1,540 +1,540
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import os
11 import os
12 import re
12 import re
13 import time
13 import time
14
14
15 from ..i18n import _
15 from ..i18n import _
16
16
17 from .common import (
17 from .common import (
18 ErrorResponse,
18 ErrorResponse,
19 HTTP_NOT_FOUND,
19 HTTP_NOT_FOUND,
20 HTTP_OK,
20 HTTP_OK,
21 HTTP_SERVER_ERROR,
21 HTTP_SERVER_ERROR,
22 cspvalues,
22 cspvalues,
23 get_contact,
23 get_contact,
24 get_mtime,
24 get_mtime,
25 ismember,
25 ismember,
26 paritygen,
26 paritygen,
27 staticfile,
27 staticfile,
28 )
28 )
29 from .request import wsgirequest
29 from .request import wsgirequest
30
30
31 from .. import (
31 from .. import (
32 encoding,
32 encoding,
33 error,
33 error,
34 hg,
34 hg,
35 profiling,
35 profiling,
36 scmutil,
36 scmutil,
37 templater,
37 templater,
38 ui as uimod,
38 ui as uimod,
39 util,
39 util,
40 )
40 )
41
41
42 from . import (
42 from . import (
43 hgweb_mod,
43 hgweb_mod,
44 webutil,
44 webutil,
45 wsgicgi,
45 wsgicgi,
46 )
46 )
47
47
48 def cleannames(items):
48 def cleannames(items):
49 return [(util.pconvert(name).strip('/'), path) for name, path in items]
49 return [(util.pconvert(name).strip('/'), path) for name, path in items]
50
50
51 def findrepos(paths):
51 def findrepos(paths):
52 repos = []
52 repos = []
53 for prefix, root in cleannames(paths):
53 for prefix, root in cleannames(paths):
54 roothead, roottail = os.path.split(root)
54 roothead, roottail = os.path.split(root)
55 # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below
55 # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below
56 # /bar/ be served as as foo/N .
56 # /bar/ be served as as foo/N .
57 # '*' will not search inside dirs with .hg (except .hg/patches),
57 # '*' will not search inside dirs with .hg (except .hg/patches),
58 # '**' will search inside dirs with .hg (and thus also find subrepos).
58 # '**' will search inside dirs with .hg (and thus also find subrepos).
59 try:
59 try:
60 recurse = {'*': False, '**': True}[roottail]
60 recurse = {'*': False, '**': True}[roottail]
61 except KeyError:
61 except KeyError:
62 repos.append((prefix, root))
62 repos.append((prefix, root))
63 continue
63 continue
64 roothead = os.path.normpath(os.path.abspath(roothead))
64 roothead = os.path.normpath(os.path.abspath(roothead))
65 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
65 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
66 repos.extend(urlrepos(prefix, roothead, paths))
66 repos.extend(urlrepos(prefix, roothead, paths))
67 return repos
67 return repos
68
68
69 def urlrepos(prefix, roothead, paths):
69 def urlrepos(prefix, roothead, paths):
70 """yield url paths and filesystem paths from a list of repo paths
70 """yield url paths and filesystem paths from a list of repo paths
71
71
72 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
72 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
73 >>> conv(urlrepos(b'hg', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
73 >>> conv(urlrepos(b'hg', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
74 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
74 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
75 >>> conv(urlrepos(b'', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
75 >>> conv(urlrepos(b'', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
76 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
76 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
77 """
77 """
78 for path in paths:
78 for path in paths:
79 path = os.path.normpath(path)
79 path = os.path.normpath(path)
80 yield (prefix + '/' +
80 yield (prefix + '/' +
81 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
81 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
82
82
83 def geturlcgivars(baseurl, port):
83 def geturlcgivars(baseurl, port):
84 """
84 """
85 Extract CGI variables from baseurl
85 Extract CGI variables from baseurl
86
86
87 >>> geturlcgivars(b"http://host.org/base", b"80")
87 >>> geturlcgivars(b"http://host.org/base", b"80")
88 ('host.org', '80', '/base')
88 ('host.org', '80', '/base')
89 >>> geturlcgivars(b"http://host.org:8000/base", b"80")
89 >>> geturlcgivars(b"http://host.org:8000/base", b"80")
90 ('host.org', '8000', '/base')
90 ('host.org', '8000', '/base')
91 >>> geturlcgivars(b'/base', 8000)
91 >>> geturlcgivars(b'/base', 8000)
92 ('', '8000', '/base')
92 ('', '8000', '/base')
93 >>> geturlcgivars(b"base", b'8000')
93 >>> geturlcgivars(b"base", b'8000')
94 ('', '8000', '/base')
94 ('', '8000', '/base')
95 >>> geturlcgivars(b"http://host", b'8000')
95 >>> geturlcgivars(b"http://host", b'8000')
96 ('host', '8000', '/')
96 ('host', '8000', '/')
97 >>> geturlcgivars(b"http://host/", b'8000')
97 >>> geturlcgivars(b"http://host/", b'8000')
98 ('host', '8000', '/')
98 ('host', '8000', '/')
99 """
99 """
100 u = util.url(baseurl)
100 u = util.url(baseurl)
101 name = u.host or ''
101 name = u.host or ''
102 if u.port:
102 if u.port:
103 port = u.port
103 port = u.port
104 path = u.path or ""
104 path = u.path or ""
105 if not path.startswith('/'):
105 if not path.startswith('/'):
106 path = '/' + path
106 path = '/' + path
107
107
108 return name, str(port), path
108 return name, str(port), path
109
109
110 class hgwebdir(object):
110 class hgwebdir(object):
111 """HTTP server for multiple repositories.
111 """HTTP server for multiple repositories.
112
112
113 Given a configuration, different repositories will be served depending
113 Given a configuration, different repositories will be served depending
114 on the request path.
114 on the request path.
115
115
116 Instances are typically used as WSGI applications.
116 Instances are typically used as WSGI applications.
117 """
117 """
118 def __init__(self, conf, baseui=None):
118 def __init__(self, conf, baseui=None):
119 self.conf = conf
119 self.conf = conf
120 self.baseui = baseui
120 self.baseui = baseui
121 self.ui = None
121 self.ui = None
122 self.lastrefresh = 0
122 self.lastrefresh = 0
123 self.motd = None
123 self.motd = None
124 self.refresh()
124 self.refresh()
125
125
126 def refresh(self):
126 def refresh(self):
127 refreshinterval = 20
127 refreshinterval = 20
128 if self.ui:
128 if self.ui:
129 refreshinterval = self.ui.configint('web', 'refreshinterval',
129 refreshinterval = self.ui.configint('web', 'refreshinterval',
130 refreshinterval)
130 refreshinterval)
131
131
132 # refreshinterval <= 0 means to always refresh.
132 # refreshinterval <= 0 means to always refresh.
133 if (refreshinterval > 0 and
133 if (refreshinterval > 0 and
134 self.lastrefresh + refreshinterval > time.time()):
134 self.lastrefresh + refreshinterval > time.time()):
135 return
135 return
136
136
137 if self.baseui:
137 if self.baseui:
138 u = self.baseui.copy()
138 u = self.baseui.copy()
139 else:
139 else:
140 u = uimod.ui.load()
140 u = uimod.ui.load()
141 u.setconfig('ui', 'report_untrusted', 'off', 'hgwebdir')
141 u.setconfig('ui', 'report_untrusted', 'off', 'hgwebdir')
142 u.setconfig('ui', 'nontty', 'true', 'hgwebdir')
142 u.setconfig('ui', 'nontty', 'true', 'hgwebdir')
143 # displaying bundling progress bar while serving feels wrong and may
143 # displaying bundling progress bar while serving feels wrong and may
144 # break some wsgi implementations.
144 # break some wsgi implementations.
145 u.setconfig('progress', 'disable', 'true', 'hgweb')
145 u.setconfig('progress', 'disable', 'true', 'hgweb')
146
146
147 if not isinstance(self.conf, (dict, list, tuple)):
147 if not isinstance(self.conf, (dict, list, tuple)):
148 map = {'paths': 'hgweb-paths'}
148 map = {'paths': 'hgweb-paths'}
149 if not os.path.exists(self.conf):
149 if not os.path.exists(self.conf):
150 raise error.Abort(_('config file %s not found!') % self.conf)
150 raise error.Abort(_('config file %s not found!') % self.conf)
151 u.readconfig(self.conf, remap=map, trust=True)
151 u.readconfig(self.conf, remap=map, trust=True)
152 paths = []
152 paths = []
153 for name, ignored in u.configitems('hgweb-paths'):
153 for name, ignored in u.configitems('hgweb-paths'):
154 for path in u.configlist('hgweb-paths', name):
154 for path in u.configlist('hgweb-paths', name):
155 paths.append((name, path))
155 paths.append((name, path))
156 elif isinstance(self.conf, (list, tuple)):
156 elif isinstance(self.conf, (list, tuple)):
157 paths = self.conf
157 paths = self.conf
158 elif isinstance(self.conf, dict):
158 elif isinstance(self.conf, dict):
159 paths = self.conf.items()
159 paths = self.conf.items()
160
160
161 repos = findrepos(paths)
161 repos = findrepos(paths)
162 for prefix, root in u.configitems('collections'):
162 for prefix, root in u.configitems('collections'):
163 prefix = util.pconvert(prefix)
163 prefix = util.pconvert(prefix)
164 for path in scmutil.walkrepos(root, followsym=True):
164 for path in scmutil.walkrepos(root, followsym=True):
165 repo = os.path.normpath(path)
165 repo = os.path.normpath(path)
166 name = util.pconvert(repo)
166 name = util.pconvert(repo)
167 if name.startswith(prefix):
167 if name.startswith(prefix):
168 name = name[len(prefix):]
168 name = name[len(prefix):]
169 repos.append((name.lstrip('/'), repo))
169 repos.append((name.lstrip('/'), repo))
170
170
171 self.repos = repos
171 self.repos = repos
172 self.ui = u
172 self.ui = u
173 encoding.encoding = self.ui.config('web', 'encoding',
173 encoding.encoding = self.ui.config('web', 'encoding',
174 encoding.encoding)
174 encoding.encoding)
175 self.style = self.ui.config('web', 'style', 'paper')
175 self.style = self.ui.config('web', 'style', 'paper')
176 self.templatepath = self.ui.config('web', 'templates', None)
176 self.templatepath = self.ui.config('web', 'templates', None)
177 self.stripecount = self.ui.config('web', 'stripes', 1)
177 self.stripecount = self.ui.config('web', 'stripes', 1)
178 if self.stripecount:
178 if self.stripecount:
179 self.stripecount = int(self.stripecount)
179 self.stripecount = int(self.stripecount)
180 self._baseurl = self.ui.config('web', 'baseurl')
180 self._baseurl = self.ui.config('web', 'baseurl')
181 prefix = self.ui.config('web', 'prefix', '')
181 prefix = self.ui.config('web', 'prefix', '')
182 if prefix.startswith('/'):
182 if prefix.startswith('/'):
183 prefix = prefix[1:]
183 prefix = prefix[1:]
184 if prefix.endswith('/'):
184 if prefix.endswith('/'):
185 prefix = prefix[:-1]
185 prefix = prefix[:-1]
186 self.prefix = prefix
186 self.prefix = prefix
187 self.lastrefresh = time.time()
187 self.lastrefresh = time.time()
188
188
189 def run(self):
189 def run(self):
190 if not encoding.environ.get('GATEWAY_INTERFACE',
190 if not encoding.environ.get('GATEWAY_INTERFACE',
191 '').startswith("CGI/1."):
191 '').startswith("CGI/1."):
192 raise RuntimeError("This function is only intended to be "
192 raise RuntimeError("This function is only intended to be "
193 "called while running as a CGI script.")
193 "called while running as a CGI script.")
194 wsgicgi.launch(self)
194 wsgicgi.launch(self)
195
195
196 def __call__(self, env, respond):
196 def __call__(self, env, respond):
197 req = wsgirequest(env, respond)
197 req = wsgirequest(env, respond)
198 return self.run_wsgi(req)
198 return self.run_wsgi(req)
199
199
200 def read_allowed(self, ui, req):
200 def read_allowed(self, ui, req):
201 """Check allow_read and deny_read config options of a repo's ui object
201 """Check allow_read and deny_read config options of a repo's ui object
202 to determine user permissions. By default, with neither option set (or
202 to determine user permissions. By default, with neither option set (or
203 both empty), allow all users to read the repo. There are two ways a
203 both empty), allow all users to read the repo. There are two ways a
204 user can be denied read access: (1) deny_read is not empty, and the
204 user can be denied read access: (1) deny_read is not empty, and the
205 user is unauthenticated or deny_read contains user (or *), and (2)
205 user is unauthenticated or deny_read contains user (or *), and (2)
206 allow_read is not empty and the user is not in allow_read. Return True
206 allow_read is not empty and the user is not in allow_read. Return True
207 if user is allowed to read the repo, else return False."""
207 if user is allowed to read the repo, else return False."""
208
208
209 user = req.env.get('REMOTE_USER')
209 user = req.env.get('REMOTE_USER')
210
210
211 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
211 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
212 if deny_read and (not user or ismember(ui, user, deny_read)):
212 if deny_read and (not user or ismember(ui, user, deny_read)):
213 return False
213 return False
214
214
215 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
215 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
216 # by default, allow reading if no allow_read option has been set
216 # by default, allow reading if no allow_read option has been set
217 if (not allow_read) or ismember(ui, user, allow_read):
217 if (not allow_read) or ismember(ui, user, allow_read):
218 return True
218 return True
219
219
220 return False
220 return False
221
221
222 def run_wsgi(self, req):
222 def run_wsgi(self, req):
223 profile = self.ui.configbool('profiling', 'enabled')
223 profile = self.ui.configbool('profiling', 'enabled')
224 with profiling.profile(self.ui, enabled=profile):
224 with profiling.profile(self.ui, enabled=profile):
225 for r in self._runwsgi(req):
225 for r in self._runwsgi(req):
226 yield r
226 yield r
227
227
228 def _runwsgi(self, req):
228 def _runwsgi(self, req):
229 try:
229 try:
230 self.refresh()
230 self.refresh()
231
231
232 csp, nonce = cspvalues(self.ui)
232 csp, nonce = cspvalues(self.ui)
233 if csp:
233 if csp:
234 req.headers.append(('Content-Security-Policy', csp))
234 req.headers.append(('Content-Security-Policy', csp))
235
235
236 virtual = req.env.get("PATH_INFO", "").strip('/')
236 virtual = req.env.get("PATH_INFO", "").strip('/')
237 tmpl = self.templater(req, nonce)
237 tmpl = self.templater(req, nonce)
238 ctype = tmpl('mimetype', encoding=encoding.encoding)
238 ctype = tmpl('mimetype', encoding=encoding.encoding)
239 ctype = templater.stringify(ctype)
239 ctype = templater.stringify(ctype)
240
240
241 # a static file
241 # a static file
242 if virtual.startswith('static/') or 'static' in req.form:
242 if virtual.startswith('static/') or 'static' in req.form:
243 if virtual.startswith('static/'):
243 if virtual.startswith('static/'):
244 fname = virtual[7:]
244 fname = virtual[7:]
245 else:
245 else:
246 fname = req.form['static'][0]
246 fname = req.form['static'][0]
247 static = self.ui.config("web", "static", None,
247 static = self.ui.config("web", "static", None,
248 untrusted=False)
248 untrusted=False)
249 if not static:
249 if not static:
250 tp = self.templatepath or templater.templatepaths()
250 tp = self.templatepath or templater.templatepaths()
251 if isinstance(tp, str):
251 if isinstance(tp, str):
252 tp = [tp]
252 tp = [tp]
253 static = [os.path.join(p, 'static') for p in tp]
253 static = [os.path.join(p, 'static') for p in tp]
254 staticfile(static, fname, req)
254 staticfile(static, fname, req)
255 return []
255 return []
256
256
257 # top-level index
257 # top-level index
258
258
259 repos = dict(self.repos)
259 repos = dict(self.repos)
260
260
261 if (not virtual or virtual == 'index') and virtual not in repos:
261 if (not virtual or virtual == 'index') and virtual not in repos:
262 req.respond(HTTP_OK, ctype)
262 req.respond(HTTP_OK, ctype)
263 return self.makeindex(req, tmpl)
263 return self.makeindex(req, tmpl)
264
264
265 # nested indexes and hgwebs
265 # nested indexes and hgwebs
266
266
267 if virtual.endswith('/index') and virtual not in repos:
267 if virtual.endswith('/index') and virtual not in repos:
268 subdir = virtual[:-len('index')]
268 subdir = virtual[:-len('index')]
269 if any(r.startswith(subdir) for r in repos):
269 if any(r.startswith(subdir) for r in repos):
270 req.respond(HTTP_OK, ctype)
270 req.respond(HTTP_OK, ctype)
271 return self.makeindex(req, tmpl, subdir)
271 return self.makeindex(req, tmpl, subdir)
272
272
273 def _virtualdirs():
273 def _virtualdirs():
274 # Check the full virtual path, each parent, and the root ('')
274 # Check the full virtual path, each parent, and the root ('')
275 if virtual != '':
275 if virtual != '':
276 yield virtual
276 yield virtual
277
277
278 for p in util.finddirs(virtual):
278 for p in util.finddirs(virtual):
279 yield p
279 yield p
280
280
281 yield ''
281 yield ''
282
282
283 for virtualrepo in _virtualdirs():
283 for virtualrepo in _virtualdirs():
284 real = repos.get(virtualrepo)
284 real = repos.get(virtualrepo)
285 if real:
285 if real:
286 req.env['REPO_NAME'] = virtualrepo
286 req.env['REPO_NAME'] = virtualrepo
287 try:
287 try:
288 # ensure caller gets private copy of ui
288 # ensure caller gets private copy of ui
289 repo = hg.repository(self.ui.copy(), real)
289 repo = hg.repository(self.ui.copy(), real)
290 return hgweb_mod.hgweb(repo).run_wsgi(req)
290 return hgweb_mod.hgweb(repo).run_wsgi(req)
291 except IOError as inst:
291 except IOError as inst:
292 msg = encoding.strtolocal(inst.strerror)
292 msg = encoding.strtolocal(inst.strerror)
293 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
293 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
294 except error.RepoError as inst:
294 except error.RepoError as inst:
295 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
295 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
296
296
297 # browse subdirectories
297 # browse subdirectories
298 subdir = virtual + '/'
298 subdir = virtual + '/'
299 if [r for r in repos if r.startswith(subdir)]:
299 if [r for r in repos if r.startswith(subdir)]:
300 req.respond(HTTP_OK, ctype)
300 req.respond(HTTP_OK, ctype)
301 return self.makeindex(req, tmpl, subdir)
301 return self.makeindex(req, tmpl, subdir)
302
302
303 # prefixes not found
303 # prefixes not found
304 req.respond(HTTP_NOT_FOUND, ctype)
304 req.respond(HTTP_NOT_FOUND, ctype)
305 return tmpl("notfound", repo=virtual)
305 return tmpl("notfound", repo=virtual)
306
306
307 except ErrorResponse as err:
307 except ErrorResponse as err:
308 req.respond(err, ctype)
308 req.respond(err, ctype)
309 return tmpl('error', error=err.message or '')
309 return tmpl('error', error=err.message or '')
310 finally:
310 finally:
311 tmpl = None
311 tmpl = None
312
312
313 def makeindex(self, req, tmpl, subdir=""):
313 def makeindex(self, req, tmpl, subdir=""):
314
314
315 def archivelist(ui, nodeid, url):
315 def archivelist(ui, nodeid, url):
316 allowed = ui.configlist("web", "allow_archive", untrusted=True)
316 allowed = ui.configlist("web", "allow_archive", untrusted=True)
317 archives = []
317 archives = []
318 for typ, spec in hgweb_mod.archivespecs.iteritems():
318 for typ, spec in hgweb_mod.archivespecs.iteritems():
319 if typ in allowed or ui.configbool("web", "allow" + typ,
319 if typ in allowed or ui.configbool("web", "allow" + typ,
320 untrusted=True):
320 untrusted=True):
321 archives.append({"type" : typ, "extension": spec[2],
321 archives.append({"type" : typ, "extension": spec[2],
322 "node": nodeid, "url": url})
322 "node": nodeid, "url": url})
323 return archives
323 return archives
324
324
325 def rawentries(subdir="", **map):
325 def rawentries(subdir="", **map):
326
326
327 descend = self.ui.configbool('web', 'descend', True)
327 descend = self.ui.configbool('web', 'descend', True)
328 collapse = self.ui.configbool('web', 'collapse', False)
328 collapse = self.ui.configbool('web', 'collapse')
329 seenrepos = set()
329 seenrepos = set()
330 seendirs = set()
330 seendirs = set()
331 for name, path in self.repos:
331 for name, path in self.repos:
332
332
333 if not name.startswith(subdir):
333 if not name.startswith(subdir):
334 continue
334 continue
335 name = name[len(subdir):]
335 name = name[len(subdir):]
336 directory = False
336 directory = False
337
337
338 if '/' in name:
338 if '/' in name:
339 if not descend:
339 if not descend:
340 continue
340 continue
341
341
342 nameparts = name.split('/')
342 nameparts = name.split('/')
343 rootname = nameparts[0]
343 rootname = nameparts[0]
344
344
345 if not collapse:
345 if not collapse:
346 pass
346 pass
347 elif rootname in seendirs:
347 elif rootname in seendirs:
348 continue
348 continue
349 elif rootname in seenrepos:
349 elif rootname in seenrepos:
350 pass
350 pass
351 else:
351 else:
352 directory = True
352 directory = True
353 name = rootname
353 name = rootname
354
354
355 # redefine the path to refer to the directory
355 # redefine the path to refer to the directory
356 discarded = '/'.join(nameparts[1:])
356 discarded = '/'.join(nameparts[1:])
357
357
358 # remove name parts plus accompanying slash
358 # remove name parts plus accompanying slash
359 path = path[:-len(discarded) - 1]
359 path = path[:-len(discarded) - 1]
360
360
361 try:
361 try:
362 r = hg.repository(self.ui, path)
362 r = hg.repository(self.ui, path)
363 directory = False
363 directory = False
364 except (IOError, error.RepoError):
364 except (IOError, error.RepoError):
365 pass
365 pass
366
366
367 parts = [name]
367 parts = [name]
368 parts.insert(0, '/' + subdir.rstrip('/'))
368 parts.insert(0, '/' + subdir.rstrip('/'))
369 if req.env['SCRIPT_NAME']:
369 if req.env['SCRIPT_NAME']:
370 parts.insert(0, req.env['SCRIPT_NAME'])
370 parts.insert(0, req.env['SCRIPT_NAME'])
371 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
371 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
372
372
373 # show either a directory entry or a repository
373 # show either a directory entry or a repository
374 if directory:
374 if directory:
375 # get the directory's time information
375 # get the directory's time information
376 try:
376 try:
377 d = (get_mtime(path), util.makedate()[1])
377 d = (get_mtime(path), util.makedate()[1])
378 except OSError:
378 except OSError:
379 continue
379 continue
380
380
381 # add '/' to the name to make it obvious that
381 # add '/' to the name to make it obvious that
382 # the entry is a directory, not a regular repository
382 # the entry is a directory, not a regular repository
383 row = {'contact': "",
383 row = {'contact': "",
384 'contact_sort': "",
384 'contact_sort': "",
385 'name': name + '/',
385 'name': name + '/',
386 'name_sort': name,
386 'name_sort': name,
387 'url': url,
387 'url': url,
388 'description': "",
388 'description': "",
389 'description_sort': "",
389 'description_sort': "",
390 'lastchange': d,
390 'lastchange': d,
391 'lastchange_sort': d[1]-d[0],
391 'lastchange_sort': d[1]-d[0],
392 'archives': [],
392 'archives': [],
393 'isdirectory': True,
393 'isdirectory': True,
394 'labels': [],
394 'labels': [],
395 }
395 }
396
396
397 seendirs.add(name)
397 seendirs.add(name)
398 yield row
398 yield row
399 continue
399 continue
400
400
401 u = self.ui.copy()
401 u = self.ui.copy()
402 try:
402 try:
403 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
403 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
404 except Exception as e:
404 except Exception as e:
405 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
405 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
406 continue
406 continue
407 def get(section, name, default=uimod._unset):
407 def get(section, name, default=uimod._unset):
408 return u.config(section, name, default, untrusted=True)
408 return u.config(section, name, default, untrusted=True)
409
409
410 if u.configbool("web", "hidden", untrusted=True):
410 if u.configbool("web", "hidden", untrusted=True):
411 continue
411 continue
412
412
413 if not self.read_allowed(u, req):
413 if not self.read_allowed(u, req):
414 continue
414 continue
415
415
416 # update time with local timezone
416 # update time with local timezone
417 try:
417 try:
418 r = hg.repository(self.ui, path)
418 r = hg.repository(self.ui, path)
419 except IOError:
419 except IOError:
420 u.warn(_('error accessing repository at %s\n') % path)
420 u.warn(_('error accessing repository at %s\n') % path)
421 continue
421 continue
422 except error.RepoError:
422 except error.RepoError:
423 u.warn(_('error accessing repository at %s\n') % path)
423 u.warn(_('error accessing repository at %s\n') % path)
424 continue
424 continue
425 try:
425 try:
426 d = (get_mtime(r.spath), util.makedate()[1])
426 d = (get_mtime(r.spath), util.makedate()[1])
427 except OSError:
427 except OSError:
428 continue
428 continue
429
429
430 contact = get_contact(get)
430 contact = get_contact(get)
431 description = get("web", "description", "")
431 description = get("web", "description", "")
432 seenrepos.add(name)
432 seenrepos.add(name)
433 name = get("web", "name", name)
433 name = get("web", "name", name)
434 row = {'contact': contact or "unknown",
434 row = {'contact': contact or "unknown",
435 'contact_sort': contact.upper() or "unknown",
435 'contact_sort': contact.upper() or "unknown",
436 'name': name,
436 'name': name,
437 'name_sort': name,
437 'name_sort': name,
438 'url': url,
438 'url': url,
439 'description': description or "unknown",
439 'description': description or "unknown",
440 'description_sort': description.upper() or "unknown",
440 'description_sort': description.upper() or "unknown",
441 'lastchange': d,
441 'lastchange': d,
442 'lastchange_sort': d[1]-d[0],
442 'lastchange_sort': d[1]-d[0],
443 'archives': archivelist(u, "tip", url),
443 'archives': archivelist(u, "tip", url),
444 'isdirectory': None,
444 'isdirectory': None,
445 'labels': u.configlist('web', 'labels', untrusted=True),
445 'labels': u.configlist('web', 'labels', untrusted=True),
446 }
446 }
447
447
448 yield row
448 yield row
449
449
450 sortdefault = None, False
450 sortdefault = None, False
451 def entries(sortcolumn="", descending=False, subdir="", **map):
451 def entries(sortcolumn="", descending=False, subdir="", **map):
452 rows = rawentries(subdir=subdir, **map)
452 rows = rawentries(subdir=subdir, **map)
453
453
454 if sortcolumn and sortdefault != (sortcolumn, descending):
454 if sortcolumn and sortdefault != (sortcolumn, descending):
455 sortkey = '%s_sort' % sortcolumn
455 sortkey = '%s_sort' % sortcolumn
456 rows = sorted(rows, key=lambda x: x[sortkey],
456 rows = sorted(rows, key=lambda x: x[sortkey],
457 reverse=descending)
457 reverse=descending)
458 for row, parity in zip(rows, paritygen(self.stripecount)):
458 for row, parity in zip(rows, paritygen(self.stripecount)):
459 row['parity'] = parity
459 row['parity'] = parity
460 yield row
460 yield row
461
461
462 self.refresh()
462 self.refresh()
463 sortable = ["name", "description", "contact", "lastchange"]
463 sortable = ["name", "description", "contact", "lastchange"]
464 sortcolumn, descending = sortdefault
464 sortcolumn, descending = sortdefault
465 if 'sort' in req.form:
465 if 'sort' in req.form:
466 sortcolumn = req.form['sort'][0]
466 sortcolumn = req.form['sort'][0]
467 descending = sortcolumn.startswith('-')
467 descending = sortcolumn.startswith('-')
468 if descending:
468 if descending:
469 sortcolumn = sortcolumn[1:]
469 sortcolumn = sortcolumn[1:]
470 if sortcolumn not in sortable:
470 if sortcolumn not in sortable:
471 sortcolumn = ""
471 sortcolumn = ""
472
472
473 sort = [("sort_%s" % column,
473 sort = [("sort_%s" % column,
474 "%s%s" % ((not descending and column == sortcolumn)
474 "%s%s" % ((not descending and column == sortcolumn)
475 and "-" or "", column))
475 and "-" or "", column))
476 for column in sortable]
476 for column in sortable]
477
477
478 self.refresh()
478 self.refresh()
479 self.updatereqenv(req.env)
479 self.updatereqenv(req.env)
480
480
481 return tmpl("index", entries=entries, subdir=subdir,
481 return tmpl("index", entries=entries, subdir=subdir,
482 pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
482 pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
483 sortcolumn=sortcolumn, descending=descending,
483 sortcolumn=sortcolumn, descending=descending,
484 **dict(sort))
484 **dict(sort))
485
485
486 def templater(self, req, nonce):
486 def templater(self, req, nonce):
487
487
488 def motd(**map):
488 def motd(**map):
489 if self.motd is not None:
489 if self.motd is not None:
490 yield self.motd
490 yield self.motd
491 else:
491 else:
492 yield config('web', 'motd', '')
492 yield config('web', 'motd', '')
493
493
494 def config(section, name, default=uimod._unset, untrusted=True):
494 def config(section, name, default=uimod._unset, untrusted=True):
495 return self.ui.config(section, name, default, untrusted)
495 return self.ui.config(section, name, default, untrusted)
496
496
497 self.updatereqenv(req.env)
497 self.updatereqenv(req.env)
498
498
499 url = req.env.get('SCRIPT_NAME', '')
499 url = req.env.get('SCRIPT_NAME', '')
500 if not url.endswith('/'):
500 if not url.endswith('/'):
501 url += '/'
501 url += '/'
502
502
503 vars = {}
503 vars = {}
504 styles = (
504 styles = (
505 req.form.get('style', [None])[0],
505 req.form.get('style', [None])[0],
506 config('web', 'style'),
506 config('web', 'style'),
507 'paper'
507 'paper'
508 )
508 )
509 style, mapfile = templater.stylemap(styles, self.templatepath)
509 style, mapfile = templater.stylemap(styles, self.templatepath)
510 if style == styles[0]:
510 if style == styles[0]:
511 vars['style'] = style
511 vars['style'] = style
512
512
513 start = url[-1] == '?' and '&' or '?'
513 start = url[-1] == '?' and '&' or '?'
514 sessionvars = webutil.sessionvars(vars, start)
514 sessionvars = webutil.sessionvars(vars, start)
515 logourl = config('web', 'logourl', 'https://mercurial-scm.org/')
515 logourl = config('web', 'logourl', 'https://mercurial-scm.org/')
516 logoimg = config('web', 'logoimg', 'hglogo.png')
516 logoimg = config('web', 'logoimg', 'hglogo.png')
517 staticurl = config('web', 'staticurl') or url + 'static/'
517 staticurl = config('web', 'staticurl') or url + 'static/'
518 if not staticurl.endswith('/'):
518 if not staticurl.endswith('/'):
519 staticurl += '/'
519 staticurl += '/'
520
520
521 defaults = {
521 defaults = {
522 "encoding": encoding.encoding,
522 "encoding": encoding.encoding,
523 "motd": motd,
523 "motd": motd,
524 "url": url,
524 "url": url,
525 "logourl": logourl,
525 "logourl": logourl,
526 "logoimg": logoimg,
526 "logoimg": logoimg,
527 "staticurl": staticurl,
527 "staticurl": staticurl,
528 "sessionvars": sessionvars,
528 "sessionvars": sessionvars,
529 "style": style,
529 "style": style,
530 "nonce": nonce,
530 "nonce": nonce,
531 }
531 }
532 tmpl = templater.templater.frommapfile(mapfile, defaults=defaults)
532 tmpl = templater.templater.frommapfile(mapfile, defaults=defaults)
533 return tmpl
533 return tmpl
534
534
535 def updatereqenv(self, env):
535 def updatereqenv(self, env):
536 if self._baseurl is not None:
536 if self._baseurl is not None:
537 name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
537 name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
538 env['SERVER_NAME'] = name
538 env['SERVER_NAME'] = name
539 env['SERVER_PORT'] = port
539 env['SERVER_PORT'] = port
540 env['SCRIPT_NAME'] = path
540 env['SCRIPT_NAME'] = path
General Comments 0
You need to be logged in to leave comments. Login now