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