##// END OF EJS Templates
hgweb: support for deny_read/allow_read options...
Mark Edgington -
r7336:2dc86871 default
parent child Browse files
Show More
@@ -1,751 +1,773 b''
1 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4
5 5 NAME
6 6 ----
7 7 hgrc - configuration files for Mercurial
8 8
9 9 SYNOPSIS
10 10 --------
11 11
12 12 The Mercurial system uses a set of configuration files to control
13 13 aspects of its behaviour.
14 14
15 15 FILES
16 16 -----
17 17
18 18 Mercurial reads configuration data from several files, if they exist.
19 19 The names of these files depend on the system on which Mercurial is
20 20 installed. *.rc files from a single directory are read in
21 21 alphabetical order, later ones overriding earlier ones. Where
22 22 multiple paths are given below, settings from later paths override
23 23 earlier ones.
24 24
25 25 (Unix) <install-root>/etc/mercurial/hgrc.d/*.rc::
26 26 (Unix) <install-root>/etc/mercurial/hgrc::
27 27 Per-installation configuration files, searched for in the
28 28 directory where Mercurial is installed. <install-root> is the
29 29 parent directory of the hg executable (or symlink) being run.
30 30 For example, if installed in /shared/tools/bin/hg, Mercurial will
31 31 look in /shared/tools/etc/mercurial/hgrc. Options in these files
32 32 apply to all Mercurial commands executed by any user in any
33 33 directory.
34 34
35 35 (Unix) /etc/mercurial/hgrc.d/*.rc::
36 36 (Unix) /etc/mercurial/hgrc::
37 37 Per-system configuration files, for the system on which Mercurial
38 38 is running. Options in these files apply to all Mercurial
39 39 commands executed by any user in any directory. Options in these
40 40 files override per-installation options.
41 41
42 42 (Windows) <install-dir>\Mercurial.ini::
43 43 or else::
44 44 (Windows) HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial::
45 45 or else::
46 46 (Windows) C:\Mercurial\Mercurial.ini::
47 47 Per-installation/system configuration files, for the system on
48 48 which Mercurial is running. Options in these files apply to all
49 49 Mercurial commands executed by any user in any directory.
50 50 Registry keys contain PATH-like strings, every part of which must
51 51 reference a Mercurial.ini file or be a directory where *.rc files
52 52 will be read.
53 53
54 54 (Unix) $HOME/.hgrc::
55 55 (Windows) %HOME%\Mercurial.ini::
56 56 (Windows) %HOME%\.hgrc::
57 57 (Windows) %USERPROFILE%\Mercurial.ini::
58 58 (Windows) %USERPROFILE%\.hgrc::
59 59 Per-user configuration file(s), for the user running Mercurial.
60 60 On Windows 9x, %HOME% is replaced by %APPDATA%.
61 61 Options in these files apply to all Mercurial commands executed
62 62 by this user in any directory. Options in thes files override
63 63 per-installation and per-system options.
64 64
65 65 (Unix, Windows) <repo>/.hg/hgrc::
66 66 Per-repository configuration options that only apply in a
67 67 particular repository. This file is not version-controlled, and
68 68 will not get transferred during a "clone" operation. Options in
69 69 this file override options in all other configuration files.
70 70 On Unix, most of this file will be ignored if it doesn't belong
71 71 to a trusted user or to a trusted group. See the documentation
72 72 for the trusted section below for more details.
73 73
74 74 SYNTAX
75 75 ------
76 76
77 77 A configuration file consists of sections, led by a "[section]" header
78 78 and followed by "name: value" entries; "name=value" is also accepted.
79 79
80 80 [spam]
81 81 eggs=ham
82 82 green=
83 83 eggs
84 84
85 85 Each line contains one entry. If the lines that follow are indented,
86 86 they are treated as continuations of that entry.
87 87
88 88 Leading whitespace is removed from values. Empty lines are skipped.
89 89
90 90 The optional values can contain format strings which refer to other
91 91 values in the same section, or values in a special DEFAULT section.
92 92
93 93 Lines beginning with "#" or ";" are ignored and may be used to provide
94 94 comments.
95 95
96 96 SECTIONS
97 97 --------
98 98
99 99 This section describes the different sections that may appear in a
100 100 Mercurial "hgrc" file, the purpose of each section, its possible
101 101 keys, and their possible values.
102 102
103 103 [[decode]]
104 104 decode/encode::
105 105 Filters for transforming files on checkout/checkin. This would
106 106 typically be used for newline processing or other
107 107 localization/canonicalization of files.
108 108
109 109 Filters consist of a filter pattern followed by a filter command.
110 110 Filter patterns are globs by default, rooted at the repository
111 111 root. For example, to match any file ending in ".txt" in the root
112 112 directory only, use the pattern "*.txt". To match any file ending
113 113 in ".c" anywhere in the repository, use the pattern "**.c".
114 114
115 115 The filter command can start with a specifier, either "pipe:" or
116 116 "tempfile:". If no specifier is given, "pipe:" is used by default.
117 117
118 118 A "pipe:" command must accept data on stdin and return the
119 119 transformed data on stdout.
120 120
121 121 Pipe example:
122 122
123 123 [encode]
124 124 # uncompress gzip files on checkin to improve delta compression
125 125 # note: not necessarily a good idea, just an example
126 126 *.gz = pipe: gunzip
127 127
128 128 [decode]
129 129 # recompress gzip files when writing them to the working dir (we
130 130 # can safely omit "pipe:", because it's the default)
131 131 *.gz = gzip
132 132
133 133 A "tempfile:" command is a template. The string INFILE is replaced
134 134 with the name of a temporary file that contains the data to be
135 135 filtered by the command. The string OUTFILE is replaced with the
136 136 name of an empty temporary file, where the filtered data must be
137 137 written by the command.
138 138
139 139 NOTE: the tempfile mechanism is recommended for Windows systems,
140 140 where the standard shell I/O redirection operators often have
141 141 strange effects and may corrupt the contents of your files.
142 142
143 143 The most common usage is for LF <-> CRLF translation on Windows.
144 144 For this, use the "smart" convertors which check for binary files:
145 145
146 146 [extensions]
147 147 hgext.win32text =
148 148 [encode]
149 149 ** = cleverencode:
150 150 [decode]
151 151 ** = cleverdecode:
152 152
153 153 or if you only want to translate certain files:
154 154
155 155 [extensions]
156 156 hgext.win32text =
157 157 [encode]
158 158 **.txt = dumbencode:
159 159 [decode]
160 160 **.txt = dumbdecode:
161 161
162 162 [[defaults]]
163 163 defaults::
164 164 Use the [defaults] section to define command defaults, i.e. the
165 165 default options/arguments to pass to the specified commands.
166 166
167 167 The following example makes 'hg log' run in verbose mode, and
168 168 'hg status' show only the modified files, by default.
169 169
170 170 [defaults]
171 171 log = -v
172 172 status = -m
173 173
174 174 The actual commands, instead of their aliases, must be used when
175 175 defining command defaults. The command defaults will also be
176 176 applied to the aliases of the commands defined.
177 177
178 178 [[diff]]
179 179 diff::
180 180 Settings used when displaying diffs. They are all boolean and
181 181 defaults to False.
182 182 git;;
183 183 Use git extended diff format.
184 184 nodates;;
185 185 Don't include dates in diff headers.
186 186 showfunc;;
187 187 Show which function each change is in.
188 188 ignorews;;
189 189 Ignore white space when comparing lines.
190 190 ignorewsamount;;
191 191 Ignore changes in the amount of white space.
192 192 ignoreblanklines;;
193 193 Ignore changes whose lines are all blank.
194 194
195 195 [[email]]
196 196 email::
197 197 Settings for extensions that send email messages.
198 198 from;;
199 199 Optional. Email address to use in "From" header and SMTP envelope
200 200 of outgoing messages.
201 201 to;;
202 202 Optional. Comma-separated list of recipients' email addresses.
203 203 cc;;
204 204 Optional. Comma-separated list of carbon copy recipients'
205 205 email addresses.
206 206 bcc;;
207 207 Optional. Comma-separated list of blind carbon copy
208 208 recipients' email addresses. Cannot be set interactively.
209 209 method;;
210 210 Optional. Method to use to send email messages. If value is
211 211 "smtp" (default), use SMTP (see section "[smtp]" for
212 212 configuration). Otherwise, use as name of program to run that
213 213 acts like sendmail (takes "-f" option for sender, list of
214 214 recipients on command line, message on stdin). Normally, setting
215 215 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
216 216 sendmail to send messages.
217 217 charsets;;
218 218 Optional. Comma-separated list of charsets considered
219 219 convenient for recipients. Addresses, headers, and parts not
220 220 containing patches of outgoing messages will be encoded in
221 221 the first charset to which conversion from local encoding
222 222 (ui.encoding, ui.fallbackencoding) succeeds. If correct
223 223 conversion fails, the text in question is sent as is.
224 224 Defaults to empty (explicit) list.
225 225
226 226 Order of outgoing email charsets:
227 227
228 228 us-ascii always first, regardless of settings
229 229 email.charsets in order given by user
230 230 ui.fallbackencoding if not in email.charsets
231 231 ui.encoding if not in email.charsets
232 232 utf-8 always last, regardless of settings
233 233
234 234 Email example:
235 235
236 236 [email]
237 237 from = Joseph User <joe.user@example.com>
238 238 method = /usr/sbin/sendmail
239 239 # charsets for western europeans
240 240 # us-ascii, utf-8 omitted, as they are tried first and last
241 241 charsets = iso-8859-1, iso-8859-15, windows-1252
242 242
243 243 [[extensions]]
244 244 extensions::
245 245 Mercurial has an extension mechanism for adding new features. To
246 246 enable an extension, create an entry for it in this section.
247 247
248 248 If you know that the extension is already in Python's search path,
249 249 you can give the name of the module, followed by "=", with nothing
250 250 after the "=".
251 251
252 252 Otherwise, give a name that you choose, followed by "=", followed by
253 253 the path to the ".py" file (including the file name extension) that
254 254 defines the extension.
255 255
256 256 To explicitly disable an extension that is enabled in an hgrc of
257 257 broader scope, prepend its path with '!', as in
258 258 'hgext.foo = !/ext/path' or 'hgext.foo = !' when no path is supplied.
259 259
260 260 Example for ~/.hgrc:
261 261
262 262 [extensions]
263 263 # (the mq extension will get loaded from mercurial's path)
264 264 hgext.mq =
265 265 # (this extension will get loaded from the file specified)
266 266 myfeature = ~/.hgext/myfeature.py
267 267
268 268 [[format]]
269 269 format::
270 270
271 271 usestore;;
272 272 Enable or disable the "store" repository format which improves
273 273 compatibility with systems that fold case or otherwise mangle
274 274 filenames. Enabled by default. Disabling this option will allow
275 275 you to store longer filenames in some situations at the expense of
276 276 compatibility and ensures that the on-disk format of newly created
277 277 repositories will be compatible with Mercurial before version 0.9.4.
278 278
279 279 usefncache;;
280 280 Enable or disable the "fncache" repository format which enhances
281 281 the "store" repository format (which has to be enabled to use
282 282 fncache) to allow longer filenames and avoids using Windows reserved
283 283 names, e.g. "nul". Enabled by default. Disabling this option ensures
284 284 that the on-disk format of newly created repositories will be
285 285 compatible with Mercurial before version 1.1.
286 286
287 287 [[merge-patterns]]
288 288 merge-patterns::
289 289 This section specifies merge tools to associate with particular file
290 290 patterns. Tools matched here will take precedence over the default
291 291 merge tool. Patterns are globs by default, rooted at the repository root.
292 292
293 293 Example:
294 294
295 295 [merge-patterns]
296 296 **.c = kdiff3
297 297 **.jpg = myimgmerge
298 298
299 299 [[merge-tools]]
300 300 merge-tools::
301 301 This section configures external merge tools to use for file-level
302 302 merges.
303 303
304 304 Example ~/.hgrc:
305 305
306 306 [merge-tools]
307 307 # Override stock tool location
308 308 kdiff3.executable = ~/bin/kdiff3
309 309 # Specify command line
310 310 kdiff3.args = $base $local $other -o $output
311 311 # Give higher priority
312 312 kdiff3.priority = 1
313 313
314 314 # Define new tool
315 315 myHtmlTool.args = -m $local $other $base $output
316 316 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
317 317 myHtmlTool.priority = 1
318 318
319 319 Supported arguments:
320 320
321 321 priority;;
322 322 The priority in which to evaluate this tool.
323 323 Default: 0.
324 324 executable;;
325 325 Either just the name of the executable or its pathname.
326 326 Default: the tool name.
327 327 args;;
328 328 The arguments to pass to the tool executable. You can refer to the files
329 329 being merged as well as the output file through these variables: $base,
330 330 $local, $other, $output.
331 331 Default: $local $base $other
332 332 premerge;;
333 333 Attempt to run internal non-interactive 3-way merge tool before
334 334 launching external tool.
335 335 Default: True
336 336 binary;;
337 337 This tool can merge binary files. Defaults to False, unless tool
338 338 was selected by file pattern match.
339 339 symlink;;
340 340 This tool can merge symlinks. Defaults to False, even if tool was
341 341 selected by file pattern match.
342 342 checkconflicts;;
343 343 Check whether there are conflicts even though the tool reported
344 344 success.
345 345 Default: False
346 346 checkchanged;;
347 347 Check whether outputs were written even though the tool reported
348 348 success.
349 349 Default: False
350 350 fixeol;;
351 351 Attempt to fix up EOL changes caused by the merge tool.
352 352 Default: False
353 353 gui;;
354 354 This tool requires a graphical interface to run. Default: False
355 355 regkey;;
356 356 Windows registry key which describes install location of this tool.
357 357 Mercurial will search for this key first under HKEY_CURRENT_USER and
358 358 then under HKEY_LOCAL_MACHINE. Default: None
359 359 regname;;
360 360 Name of value to read from specified registry key. Defaults to the
361 361 unnamed (default) value.
362 362 regappend;;
363 363 String to append to the value read from the registry, typically the
364 364 executable name of the tool. Default: None
365 365
366 366 [[hooks]]
367 367 hooks::
368 368 Commands or Python functions that get automatically executed by
369 369 various actions such as starting or finishing a commit. Multiple
370 370 hooks can be run for the same action by appending a suffix to the
371 371 action. Overriding a site-wide hook can be done by changing its
372 372 value or setting it to an empty string.
373 373
374 374 Example .hg/hgrc:
375 375
376 376 [hooks]
377 377 # do not use the site-wide hook
378 378 incoming =
379 379 incoming.email = /my/email/hook
380 380 incoming.autobuild = /my/build/hook
381 381
382 382 Most hooks are run with environment variables set that give added
383 383 useful information. For each hook below, the environment variables
384 384 it is passed are listed with names of the form "$HG_foo".
385 385
386 386 changegroup;;
387 387 Run after a changegroup has been added via push, pull or
388 388 unbundle. ID of the first new changeset is in $HG_NODE. URL from
389 389 which changes came is in $HG_URL.
390 390 commit;;
391 391 Run after a changeset has been created in the local repository.
392 392 ID of the newly created changeset is in $HG_NODE. Parent
393 393 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
394 394 incoming;;
395 395 Run after a changeset has been pulled, pushed, or unbundled into
396 396 the local repository. The ID of the newly arrived changeset is in
397 397 $HG_NODE. URL that was source of changes came is in $HG_URL.
398 398 outgoing;;
399 399 Run after sending changes from local repository to another. ID of
400 400 first changeset sent is in $HG_NODE. Source of operation is in
401 401 $HG_SOURCE; see "preoutgoing" hook for description.
402 402 post-<command>;;
403 403 Run after successful invocations of the associated command. The
404 404 contents of the command line are passed as $HG_ARGS and the result
405 405 code in $HG_RESULT. Hook failure is ignored.
406 406 pre-<command>;;
407 407 Run before executing the associated command. The contents of the
408 408 command line are passed as $HG_ARGS. If the hook returns failure,
409 409 the command doesn't execute and Mercurial returns the failure code.
410 410 prechangegroup;;
411 411 Run before a changegroup is added via push, pull or unbundle.
412 412 Exit status 0 allows the changegroup to proceed. Non-zero status
413 413 will cause the push, pull or unbundle to fail. URL from which
414 414 changes will come is in $HG_URL.
415 415 precommit;;
416 416 Run before starting a local commit. Exit status 0 allows the
417 417 commit to proceed. Non-zero status will cause the commit to fail.
418 418 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
419 419 preoutgoing;;
420 420 Run before collecting changes to send from the local repository to
421 421 another. Non-zero status will cause failure. This lets you
422 422 prevent pull over http or ssh. Also prevents against local pull,
423 423 push (outbound) or bundle commands, but not effective, since you
424 424 can just copy files instead then. Source of operation is in
425 425 $HG_SOURCE. If "serve", operation is happening on behalf of
426 426 remote ssh or http repository. If "push", "pull" or "bundle",
427 427 operation is happening on behalf of repository on same system.
428 428 pretag;;
429 429 Run before creating a tag. Exit status 0 allows the tag to be
430 430 created. Non-zero status will cause the tag to fail. ID of
431 431 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
432 432 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
433 433 pretxnchangegroup;;
434 434 Run after a changegroup has been added via push, pull or unbundle,
435 435 but before the transaction has been committed. Changegroup is
436 436 visible to hook program. This lets you validate incoming changes
437 437 before accepting them. Passed the ID of the first new changeset
438 438 in $HG_NODE. Exit status 0 allows the transaction to commit.
439 439 Non-zero status will cause the transaction to be rolled back and
440 440 the push, pull or unbundle will fail. URL that was source of
441 441 changes is in $HG_URL.
442 442 pretxncommit;;
443 443 Run after a changeset has been created but the transaction not yet
444 444 committed. Changeset is visible to hook program. This lets you
445 445 validate commit message and changes. Exit status 0 allows the
446 446 commit to proceed. Non-zero status will cause the transaction to
447 447 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
448 448 IDs are in $HG_PARENT1 and $HG_PARENT2.
449 449 preupdate;;
450 450 Run before updating the working directory. Exit status 0 allows
451 451 the update to proceed. Non-zero status will prevent the update.
452 452 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
453 453 of second new parent is in $HG_PARENT2.
454 454 tag;;
455 455 Run after a tag is created. ID of tagged changeset is in
456 456 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
457 457 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
458 458 update;;
459 459 Run after updating the working directory. Changeset ID of first
460 460 new parent is in $HG_PARENT1. If merge, ID of second new parent
461 461 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
462 462 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
463 463
464 464 Note: it is generally better to use standard hooks rather than the
465 465 generic pre- and post- command hooks as they are guaranteed to be
466 466 called in the appropriate contexts for influencing transactions.
467 467 Also, hooks like "commit" will be called in all contexts that
468 468 generate a commit (eg. tag) and not just the commit command.
469 469
470 470 Note2: Environment variables with empty values may not be passed to
471 471 hooks on platforms like Windows. For instance, $HG_PARENT2 will
472 472 not be available under Windows for non-merge changesets while being
473 473 set to an empty value under Unix-like systems.
474 474
475 475 The syntax for Python hooks is as follows:
476 476
477 477 hookname = python:modulename.submodule.callable
478 478
479 479 Python hooks are run within the Mercurial process. Each hook is
480 480 called with at least three keyword arguments: a ui object (keyword
481 481 "ui"), a repository object (keyword "repo"), and a "hooktype"
482 482 keyword that tells what kind of hook is used. Arguments listed as
483 483 environment variables above are passed as keyword arguments, with no
484 484 "HG_" prefix, and names in lower case.
485 485
486 486 If a Python hook returns a "true" value or raises an exception, this
487 487 is treated as failure of the hook.
488 488
489 489 [[http_proxy]]
490 490 http_proxy::
491 491 Used to access web-based Mercurial repositories through a HTTP
492 492 proxy.
493 493 host;;
494 494 Host name and (optional) port of the proxy server, for example
495 495 "myproxy:8000".
496 496 no;;
497 497 Optional. Comma-separated list of host names that should bypass
498 498 the proxy.
499 499 passwd;;
500 500 Optional. Password to authenticate with at the proxy server.
501 501 user;;
502 502 Optional. User name to authenticate with at the proxy server.
503 503
504 504 [[smtp]]
505 505 smtp::
506 506 Configuration for extensions that need to send email messages.
507 507 host;;
508 508 Host name of mail server, e.g. "mail.example.com".
509 509 port;;
510 510 Optional. Port to connect to on mail server. Default: 25.
511 511 tls;;
512 512 Optional. Whether to connect to mail server using TLS. True or
513 513 False. Default: False.
514 514 username;;
515 515 Optional. User name to authenticate to SMTP server with.
516 516 If username is specified, password must also be specified.
517 517 Default: none.
518 518 password;;
519 519 Optional. Password to authenticate to SMTP server with.
520 520 If username is specified, password must also be specified.
521 521 Default: none.
522 522 local_hostname;;
523 523 Optional. It's the hostname that the sender can use to identify itself
524 524 to the MTA.
525 525
526 526 [[paths]]
527 527 paths::
528 528 Assigns symbolic names to repositories. The left side is the
529 529 symbolic name, and the right gives the directory or URL that is the
530 530 location of the repository. Default paths can be declared by
531 531 setting the following entries.
532 532 default;;
533 533 Directory or URL to use when pulling if no source is specified.
534 534 Default is set to repository from which the current repository
535 535 was cloned.
536 536 default-push;;
537 537 Optional. Directory or URL to use when pushing if no destination
538 538 is specified.
539 539
540 540 [[server]]
541 541 server::
542 542 Controls generic server settings.
543 543 uncompressed;;
544 544 Whether to allow clients to clone a repo using the uncompressed
545 545 streaming protocol. This transfers about 40% more data than a
546 546 regular clone, but uses less memory and CPU on both server and
547 547 client. Over a LAN (100Mbps or better) or a very fast WAN, an
548 548 uncompressed streaming clone is a lot faster (~10x) than a regular
549 549 clone. Over most WAN connections (anything slower than about
550 550 6Mbps), uncompressed streaming is slower, because of the extra
551 551 data transfer overhead. Default is False.
552 552
553 553 [[trusted]]
554 554 trusted::
555 555 For security reasons, Mercurial will not use the settings in
556 556 the .hg/hgrc file from a repository if it doesn't belong to a
557 557 trusted user or to a trusted group. The main exception is the
558 558 web interface, which automatically uses some safe settings, since
559 559 it's common to serve repositories from different users.
560 560
561 561 This section specifies what users and groups are trusted. The
562 562 current user is always trusted. To trust everybody, list a user
563 563 or a group with name "*".
564 564
565 565 users;;
566 566 Comma-separated list of trusted users.
567 567 groups;;
568 568 Comma-separated list of trusted groups.
569 569
570 570 [[ui]]
571 571 ui::
572 572 User interface controls.
573 573 archivemeta;;
574 574 Whether to include the .hg_archival.txt file containing metadata
575 575 (hashes for the repository base and for tip) in archives created by
576 576 the hg archive command or downloaded via hgweb.
577 577 Default is true.
578 578 askusername;;
579 579 Whether to prompt for a username when committing. If True, and
580 580 neither $HGUSER nor $EMAIL has been specified, then the user will
581 581 be prompted to enter a username. If no username is entered, the
582 582 default USER@HOST is used instead.
583 583 Default is False.
584 584 debug;;
585 585 Print debugging information. True or False. Default is False.
586 586 editor;;
587 587 The editor to use during a commit. Default is $EDITOR or "vi".
588 588 fallbackencoding;;
589 589 Encoding to try if it's not possible to decode the changelog using
590 590 UTF-8. Default is ISO-8859-1.
591 591 ignore;;
592 592 A file to read per-user ignore patterns from. This file should be in
593 593 the same format as a repository-wide .hgignore file. This option
594 594 supports hook syntax, so if you want to specify multiple ignore
595 595 files, you can do so by setting something like
596 596 "ignore.other = ~/.hgignore2". For details of the ignore file
597 597 format, see the hgignore(5) man page.
598 598 interactive;;
599 599 Allow to prompt the user. True or False. Default is True.
600 600 logtemplate;;
601 601 Template string for commands that print changesets.
602 602 merge;;
603 603 The conflict resolution program to use during a manual merge.
604 604 There are some internal tools available:
605 605
606 606 internal:local;;
607 607 keep the local version
608 608 internal:other;;
609 609 use the other version
610 610 internal:merge;;
611 611 use the internal non-interactive merge tool
612 612 internal:fail;;
613 613 fail to merge
614 614
615 615 See the merge-tools section for more information on configuring tools.
616 616
617 617 patch;;
618 618 command to use to apply patches. Look for 'gpatch' or 'patch' in PATH if
619 619 unset.
620 620 quiet;;
621 621 Reduce the amount of output printed. True or False. Default is False.
622 622 remotecmd;;
623 623 remote command to use for clone/push/pull operations. Default is 'hg'.
624 624 report_untrusted;;
625 625 Warn if a .hg/hgrc file is ignored due to not being owned by a
626 626 trusted user or group. True or False. Default is True.
627 627 slash;;
628 628 Display paths using a slash ("/") as the path separator. This only
629 629 makes a difference on systems where the default path separator is not
630 630 the slash character (e.g. Windows uses the backslash character ("\")).
631 631 Default is False.
632 632 ssh;;
633 633 command to use for SSH connections. Default is 'ssh'.
634 634 strict;;
635 635 Require exact command names, instead of allowing unambiguous
636 636 abbreviations. True or False. Default is False.
637 637 style;;
638 638 Name of style to use for command output.
639 639 timeout;;
640 640 The timeout used when a lock is held (in seconds), a negative value
641 641 means no timeout. Default is 600.
642 642 username;;
643 643 The committer of a changeset created when running "commit".
644 644 Typically a person's name and email address, e.g. "Fred Widget
645 645 <fred@example.com>". Default is $EMAIL or username@hostname.
646 646 If the username in hgrc is empty, it has to be specified manually or
647 647 in a different hgrc file (e.g. $HOME/.hgrc, if the admin set "username ="
648 648 in the system hgrc).
649 649 verbose;;
650 650 Increase the amount of output printed. True or False. Default is False.
651 651
652 652
653 653 [[web]]
654 654 web::
655 655 Web interface configuration.
656 656 accesslog;;
657 657 Where to output the access log. Default is stdout.
658 658 address;;
659 659 Interface address to bind to. Default is all.
660 660 allow_archive;;
661 661 List of archive format (bz2, gz, zip) allowed for downloading.
662 662 Default is empty.
663 663 allowbz2;;
664 664 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
665 665 Default is false.
666 666 allowgz;;
667 667 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
668 668 Default is false.
669 669 allowpull;;
670 670 Whether to allow pulling from the repository. Default is true.
671 671 allow_push;;
672 672 Whether to allow pushing to the repository. If empty or not set,
673 673 push is not allowed. If the special value "*", any remote user
674 674 can push, including unauthenticated users. Otherwise, the remote
675 675 user must have been authenticated, and the authenticated user name
676 676 must be present in this list (separated by whitespace or ",").
677 677 The contents of the allow_push list are examined after the
678 678 deny_push list.
679 allow_read;;
680 If the user has not already been denied repository access due to the
681 contents of deny_read, this list determines whether to grant repository
682 access to the user. If this list is not empty, and the user is
683 unauthenticated or not present in the list (separated by whitespace or ","),
684 then access is denied for the user. If the list is empty or not set, then
685 access is permitted to all users by default. Setting allow_read to the
686 special value "*" is equivalent to it not being set (i.e. access is
687 permitted to all users). The contents of the allow_read list are examined
688 after the deny_read list.
679 689 allowzip;;
680 690 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
681 691 Default is false. This feature creates temporary files.
682 692 baseurl;;
683 693 Base URL to use when publishing URLs in other locations, so
684 694 third-party tools like email notification hooks can construct URLs.
685 695 Example: "http://hgserver/repos/"
686 696 contact;;
687 697 Name or email address of the person in charge of the repository.
688 698 Defaults to ui.username or $EMAIL or "unknown" if unset or empty.
689 699 deny_push;;
690 700 Whether to deny pushing to the repository. If empty or not set,
691 701 push is not denied. If the special value "*", all remote users
692 702 are denied push. Otherwise, unauthenticated users are all denied,
693 703 and any authenticated user name present in this list (separated by
694 704 whitespace or ",") is also denied. The contents of the deny_push
695 705 list are examined before the allow_push list.
706 deny_read;;
707 Whether to deny reading/viewing of the repository. If this list is not
708 empty, unauthenticated users are all denied, and any authenticated user name
709 present in this list (separated by whitespace or ",") is also denied access
710 to the repository. If set to the special value "*", all remote users are
711 denied access (rarely needed ;). If deny_read is empty or not set, the
712 determination of repository access depends on the presence and content of
713 the allow_read list (see description). If both deny_read and allow_read are
714 empty or not set, then access is permitted to all users by default. If the
715 repository is being served via hgwebdir, denied users will not be able to
716 see it in the list of repositories. The contents of the deny_read list have
717 priority over (are examined before) the contents of the allow_read list.
696 718 description;;
697 719 Textual description of the repository's purpose or contents.
698 720 Default is "unknown".
699 721 encoding;;
700 722 Character encoding name.
701 723 Example: "UTF-8"
702 724 errorlog;;
703 725 Where to output the error log. Default is stderr.
704 726 hidden;;
705 727 Whether to hide the repository in the hgwebdir index. Default is false.
706 728 ipv6;;
707 729 Whether to use IPv6. Default is false.
708 730 name;;
709 731 Repository name to use in the web interface. Default is current
710 732 working directory.
711 733 maxchanges;;
712 734 Maximum number of changes to list on the changelog. Default is 10.
713 735 maxfiles;;
714 736 Maximum number of files to list per changeset. Default is 10.
715 737 port;;
716 738 Port to listen on. Default is 8000.
717 739 prefix;;
718 740 Prefix path to serve from. Default is '' (server root).
719 741 push_ssl;;
720 742 Whether to require that inbound pushes be transported over SSL to
721 743 prevent password sniffing. Default is true.
722 744 staticurl;;
723 745 Base URL to use for static files. If unset, static files (e.g.
724 746 the hgicon.png favicon) will be served by the CGI script itself.
725 747 Use this setting to serve them directly with the HTTP server.
726 748 Example: "http://hgserver/static/"
727 749 stripes;;
728 750 How many lines a "zebra stripe" should span in multiline output.
729 751 Default is 1; set to 0 to disable.
730 752 style;;
731 753 Which template map style to use.
732 754 templates;;
733 755 Where to find the HTML templates. Default is install path.
734 756
735 757
736 758 AUTHOR
737 759 ------
738 760 Bryan O'Sullivan <bos@serpentine.com>.
739 761
740 762 Mercurial was written by Matt Mackall <mpm@selenic.com>.
741 763
742 764 SEE ALSO
743 765 --------
744 766 hg(1), hgignore(5)
745 767
746 768 COPYING
747 769 -------
748 770 This manual page is copyright 2005 Bryan O'Sullivan.
749 771 Mercurial is copyright 2005-2007 Matt Mackall.
750 772 Free use of this software is granted under the terms of the GNU General
751 773 Public License (GPL).
@@ -1,308 +1,321 b''
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os, mimetypes
10 10 from mercurial.node import hex, nullid
11 11 from mercurial.repo import RepoError
12 12 from mercurial import ui, hg, util, hook
13 13 from mercurial import revlog, templater, templatefilters
14 14 from common import get_mtime, style_map, ErrorResponse
15 15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
16 16 from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED
17 17 from request import wsgirequest
18 18 import webcommands, protocol, webutil
19 19
20 20 perms = {
21 21 'changegroup': 'pull',
22 22 'changegroupsubset': 'pull',
23 23 'unbundle': 'push',
24 24 'stream_out': 'pull',
25 25 }
26 26
27 27 class hgweb(object):
28 28 def __init__(self, repo, name=None):
29 29 if isinstance(repo, str):
30 30 parentui = ui.ui(report_untrusted=False, interactive=False)
31 31 self.repo = hg.repository(parentui, repo)
32 32 else:
33 33 self.repo = repo
34 34
35 35 hook.redirect(True)
36 36 self.mtime = -1
37 37 self.reponame = name
38 38 self.archives = 'zip', 'gz', 'bz2'
39 39 self.stripecount = 1
40 40 # a repo owner may set web.templates in .hg/hgrc to get any file
41 41 # readable by the user running the CGI script
42 42 self.templatepath = self.config("web", "templates",
43 43 templater.templatepath(),
44 44 untrusted=False)
45 45
46 46 # The CGI scripts are often run by a user different from the repo owner.
47 47 # Trust the settings from the .hg/hgrc files by default.
48 48 def config(self, section, name, default=None, untrusted=True):
49 49 return self.repo.ui.config(section, name, default,
50 50 untrusted=untrusted)
51 51
52 52 def configbool(self, section, name, default=False, untrusted=True):
53 53 return self.repo.ui.configbool(section, name, default,
54 54 untrusted=untrusted)
55 55
56 56 def configlist(self, section, name, default=None, untrusted=True):
57 57 return self.repo.ui.configlist(section, name, default,
58 58 untrusted=untrusted)
59 59
60 60 def refresh(self):
61 61 mtime = get_mtime(self.repo.root)
62 62 if mtime != self.mtime:
63 63 self.mtime = mtime
64 64 self.repo = hg.repository(self.repo.ui, self.repo.root)
65 65 self.maxchanges = int(self.config("web", "maxchanges", 10))
66 66 self.stripecount = int(self.config("web", "stripes", 1))
67 67 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
68 68 self.maxfiles = int(self.config("web", "maxfiles", 10))
69 69 self.allowpull = self.configbool("web", "allowpull", True)
70 70 self.encoding = self.config("web", "encoding", util._encoding)
71 71
72 72 def run(self):
73 73 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
74 74 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
75 75 import mercurial.hgweb.wsgicgi as wsgicgi
76 76 wsgicgi.launch(self)
77 77
78 78 def __call__(self, env, respond):
79 79 req = wsgirequest(env, respond)
80 80 return self.run_wsgi(req)
81 81
82 82 def run_wsgi(self, req):
83 83
84 84 self.refresh()
85 85
86 86 # process this if it's a protocol request
87 87 # protocol bits don't need to create any URLs
88 88 # and the clients always use the old URL structure
89 89
90 90 cmd = req.form.get('cmd', [''])[0]
91 91 if cmd and cmd in protocol.__all__:
92 92 try:
93 93 if cmd in perms:
94 94 try:
95 95 self.check_perm(req, perms[cmd])
96 96 except ErrorResponse, inst:
97 97 if cmd == 'unbundle':
98 98 req.drain()
99 99 raise
100 100 method = getattr(protocol, cmd)
101 101 return method(self.repo, req)
102 102 except ErrorResponse, inst:
103 103 req.respond(inst.code, protocol.HGTYPE)
104 104 if not inst.message:
105 105 return []
106 106 return '0\n%s\n' % inst.message,
107 107
108 108 # work with CGI variables to create coherent structure
109 109 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
110 110
111 111 req.url = req.env['SCRIPT_NAME']
112 112 if not req.url.endswith('/'):
113 113 req.url += '/'
114 114 if 'REPO_NAME' in req.env:
115 115 req.url += req.env['REPO_NAME'] + '/'
116 116
117 117 if 'PATH_INFO' in req.env:
118 118 parts = req.env['PATH_INFO'].strip('/').split('/')
119 119 repo_parts = req.env.get('REPO_NAME', '').split('/')
120 120 if parts[:len(repo_parts)] == repo_parts:
121 121 parts = parts[len(repo_parts):]
122 122 query = '/'.join(parts)
123 123 else:
124 124 query = req.env['QUERY_STRING'].split('&', 1)[0]
125 125 query = query.split(';', 1)[0]
126 126
127 127 # translate user-visible url structure to internal structure
128 128
129 129 args = query.split('/', 2)
130 130 if 'cmd' not in req.form and args and args[0]:
131 131
132 132 cmd = args.pop(0)
133 133 style = cmd.rfind('-')
134 134 if style != -1:
135 135 req.form['style'] = [cmd[:style]]
136 136 cmd = cmd[style+1:]
137 137
138 138 # avoid accepting e.g. style parameter as command
139 139 if hasattr(webcommands, cmd):
140 140 req.form['cmd'] = [cmd]
141 141 else:
142 142 cmd = ''
143 143
144 144 if cmd == 'static':
145 145 req.form['file'] = ['/'.join(args)]
146 146 else:
147 147 if args and args[0]:
148 148 node = args.pop(0)
149 149 req.form['node'] = [node]
150 150 if args:
151 151 req.form['file'] = args
152 152
153 153 if cmd == 'archive':
154 154 fn = req.form['node'][0]
155 155 for type_, spec in self.archive_specs.iteritems():
156 156 ext = spec[2]
157 157 if fn.endswith(ext):
158 158 req.form['node'] = [fn[:-len(ext)]]
159 159 req.form['type'] = [type_]
160 160
161 161 # process the web interface request
162 162
163 163 try:
164
165 164 tmpl = self.templater(req)
166 165 ctype = tmpl('mimetype', encoding=self.encoding)
167 166 ctype = templater.stringify(ctype)
168 167
168 # check allow_read / deny_read config options
169 self.check_perm(req, None)
170
169 171 if cmd == '':
170 172 req.form['cmd'] = [tmpl.cache['default']]
171 173 cmd = req.form['cmd'][0]
172 174
173 175 if cmd not in webcommands.__all__:
174 176 msg = 'no such method: %s' % cmd
175 177 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
176 178 elif cmd == 'file' and 'raw' in req.form.get('style', []):
177 179 self.ctype = ctype
178 180 content = webcommands.rawfile(self, req, tmpl)
179 181 else:
180 182 content = getattr(webcommands, cmd)(self, req, tmpl)
181 183 req.respond(HTTP_OK, ctype)
182 184
183 185 return ''.join(content),
184 186
185 187 except revlog.LookupError, err:
186 188 req.respond(HTTP_NOT_FOUND, ctype)
187 189 msg = str(err)
188 190 if 'manifest' not in msg:
189 191 msg = 'revision not found: %s' % err.name
190 192 return ''.join(tmpl('error', error=msg)),
191 193 except (RepoError, revlog.RevlogError), inst:
192 194 req.respond(HTTP_SERVER_ERROR, ctype)
193 195 return ''.join(tmpl('error', error=str(inst))),
194 196 except ErrorResponse, inst:
195 197 req.respond(inst.code, ctype)
196 198 return ''.join(tmpl('error', error=inst.message)),
197 199
198 200 def templater(self, req):
199 201
200 202 # determine scheme, port and server name
201 203 # this is needed to create absolute urls
202 204
203 205 proto = req.env.get('wsgi.url_scheme')
204 206 if proto == 'https':
205 207 proto = 'https'
206 208 default_port = "443"
207 209 else:
208 210 proto = 'http'
209 211 default_port = "80"
210 212
211 213 port = req.env["SERVER_PORT"]
212 214 port = port != default_port and (":" + port) or ""
213 215 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
214 216 staticurl = self.config("web", "staticurl") or req.url + 'static/'
215 217 if not staticurl.endswith('/'):
216 218 staticurl += '/'
217 219
218 220 # some functions for the templater
219 221
220 222 def header(**map):
221 223 yield tmpl('header', encoding=self.encoding, **map)
222 224
223 225 def footer(**map):
224 226 yield tmpl("footer", **map)
225 227
226 228 def motd(**map):
227 229 yield self.config("web", "motd", "")
228 230
229 231 def sessionvars(**map):
230 232 fields = []
231 233 if 'style' in req.form:
232 234 style = req.form['style'][0]
233 235 if style != self.config('web', 'style', ''):
234 236 fields.append(('style', style))
235 237
236 238 separator = req.url[-1] == '?' and ';' or '?'
237 239 for name, value in fields:
238 240 yield dict(name=name, value=value, separator=separator)
239 241 separator = ';'
240 242
241 243 # figure out which style to use
242 244
243 245 style = self.config("web", "style", "")
244 246 if 'style' in req.form:
245 247 style = req.form['style'][0]
246 248 mapfile = style_map(self.templatepath, style)
247 249
248 250 if not self.reponame:
249 251 self.reponame = (self.config("web", "name")
250 252 or req.env.get('REPO_NAME')
251 253 or req.url.strip('/') or self.repo.root)
252 254
253 255 # create the templater
254 256
255 257 tmpl = templater.templater(mapfile, templatefilters.filters,
256 258 defaults={"url": req.url,
257 259 "staticurl": staticurl,
258 260 "urlbase": urlbase,
259 261 "repo": self.reponame,
260 262 "header": header,
261 263 "footer": footer,
262 264 "motd": motd,
263 265 "sessionvars": sessionvars
264 266 })
265 267 return tmpl
266 268
267 269 def archivelist(self, nodeid):
268 270 allowed = self.configlist("web", "allow_archive")
269 271 for i, spec in self.archive_specs.iteritems():
270 272 if i in allowed or self.configbool("web", "allow" + i):
271 273 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
272 274
273 275 archive_specs = {
274 276 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
275 277 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
276 278 'zip': ('application/zip', 'zip', '.zip', None),
277 279 }
278 280
279 281 def check_perm(self, req, op):
280 282 '''Check permission for operation based on request data (including
281 authentication info. Return true if op allowed, else false.'''
283 authentication info). Return if op allowed, else raise an ErrorResponse
284 exception.'''
285
286 user = req.env.get('REMOTE_USER')
287
288 deny_read = self.configlist('web', 'deny_read')
289 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
290 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
291
292 allow_read = self.configlist('web', 'allow_read')
293 result = (not allow_read) or (allow_read == ['*']) or (user in allow_read)
294 if not result:
295 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
282 296
283 297 if op == 'pull' and not self.allowpull:
284 298 raise ErrorResponse(HTTP_OK, '')
285 elif op == 'pull':
299 # op is None when checking allow/deny_read permissions for a web-browser request
300 elif op == 'pull' or op is None:
286 301 return
287 302
288 303 # enforce that you can only push using POST requests
289 304 if req.env['REQUEST_METHOD'] != 'POST':
290 305 msg = 'push requires POST request'
291 306 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
292 307
293 308 # require ssl by default for pushing, auth info cannot be sniffed
294 309 # and replayed
295 310 scheme = req.env.get('wsgi.url_scheme')
296 311 if self.configbool('web', 'push_ssl', True) and scheme != 'https':
297 312 raise ErrorResponse(HTTP_OK, 'ssl required')
298 313
299 user = req.env.get('REMOTE_USER')
300
301 314 deny = self.configlist('web', 'deny_push')
302 315 if deny and (not user or deny == ['*'] or user in deny):
303 316 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
304 317
305 318 allow = self.configlist('web', 'allow_push')
306 319 result = allow and (allow == ['*'] or user in allow)
307 320 if not result:
308 321 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
@@ -1,284 +1,309 b''
1 1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 import os
10 10 from mercurial.i18n import _
11 11 from mercurial.repo import RepoError
12 12 from mercurial import ui, hg, util, templater, templatefilters
13 13 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen,\
14 14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 15 from hgweb_mod import hgweb
16 16 from request import wsgirequest
17 17
18 18 # This is a stopgap
19 19 class hgwebdir(object):
20 20 def __init__(self, config, parentui=None):
21 21 def cleannames(items):
22 22 return [(util.pconvert(name).strip('/'), path)
23 23 for name, path in items]
24 24
25 25 self.parentui = parentui or ui.ui(report_untrusted=False,
26 26 interactive = False)
27 27 self.motd = None
28 28 self.style = None
29 29 self.stripecount = None
30 30 self.repos_sorted = ('name', False)
31 31 self._baseurl = None
32 32 if isinstance(config, (list, tuple)):
33 33 self.repos = cleannames(config)
34 34 self.repos_sorted = ('', False)
35 35 elif isinstance(config, dict):
36 36 self.repos = util.sort(cleannames(config.items()))
37 37 else:
38 38 if isinstance(config, util.configparser):
39 39 cp = config
40 40 else:
41 41 cp = util.configparser()
42 42 cp.read(config)
43 43 self.repos = []
44 44 if cp.has_section('web'):
45 45 if cp.has_option('web', 'motd'):
46 46 self.motd = cp.get('web', 'motd')
47 47 if cp.has_option('web', 'style'):
48 48 self.style = cp.get('web', 'style')
49 49 if cp.has_option('web', 'stripes'):
50 50 self.stripecount = int(cp.get('web', 'stripes'))
51 51 if cp.has_option('web', 'baseurl'):
52 52 self._baseurl = cp.get('web', 'baseurl')
53 53 if cp.has_section('paths'):
54 54 self.repos.extend(cleannames(cp.items('paths')))
55 55 if cp.has_section('collections'):
56 56 for prefix, root in cp.items('collections'):
57 57 for path in util.walkrepos(root, followsym=True):
58 58 repo = os.path.normpath(path)
59 59 name = repo
60 60 if name.startswith(prefix):
61 61 name = name[len(prefix):]
62 62 self.repos.append((name.lstrip(os.sep), repo))
63 63 self.repos.sort()
64 64
65 65 def run(self):
66 66 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
67 67 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
68 68 import mercurial.hgweb.wsgicgi as wsgicgi
69 69 wsgicgi.launch(self)
70 70
71 71 def __call__(self, env, respond):
72 72 req = wsgirequest(env, respond)
73 73 return self.run_wsgi(req)
74 74
75 def read_allowed(self, ui, req):
76 """Check allow_read and deny_read config options of a repo's ui object
77 to determine user permissions. By default, with neither option set (or
78 both empty), allow all users to read the repo. There are two ways a
79 user can be denied read access: (1) deny_read is not empty, and the
80 user is unauthenticated or deny_read contains user (or *), and (2)
81 allow_read is not empty and the user is not in allow_read. Return True
82 if user is allowed to read the repo, else return False."""
83
84 user = req.env.get('REMOTE_USER')
85
86 deny_read = ui.configlist('web', 'deny_read', default=None, untrusted=True)
87 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
88 return False
89
90 allow_read = ui.configlist('web', 'allow_read', default=None, untrusted=True)
91 # by default, allow reading if no allow_read option has been set
92 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
93 return True
94
95 return False
96
75 97 def run_wsgi(self, req):
76 98
77 99 try:
78 100 try:
79 101
80 102 virtual = req.env.get("PATH_INFO", "").strip('/')
81 103 tmpl = self.templater(req)
82 104 ctype = tmpl('mimetype', encoding=util._encoding)
83 105 ctype = templater.stringify(ctype)
84 106
85 107 # a static file
86 108 if virtual.startswith('static/') or 'static' in req.form:
87 109 if virtual.startswith('static/'):
88 110 fname = virtual[7:]
89 111 else:
90 112 fname = req.form['static'][0]
91 113 static = templater.templatepath('static')
92 114 return staticfile(static, fname, req)
93 115
94 116 # top-level index
95 117 elif not virtual:
96 118 req.respond(HTTP_OK, ctype)
97 119 return ''.join(self.makeindex(req, tmpl)),
98 120
99 121 # nested indexes and hgwebs
100 122
101 123 repos = dict(self.repos)
102 124 while virtual:
103 125 real = repos.get(virtual)
104 126 if real:
105 127 req.env['REPO_NAME'] = virtual
106 128 try:
107 129 repo = hg.repository(self.parentui, real)
108 130 return hgweb(repo).run_wsgi(req)
109 131 except IOError, inst:
110 132 msg = inst.strerror
111 133 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
112 134 except RepoError, inst:
113 135 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
114 136
115 137 # browse subdirectories
116 138 subdir = virtual + '/'
117 139 if [r for r in repos if r.startswith(subdir)]:
118 140 req.respond(HTTP_OK, ctype)
119 141 return ''.join(self.makeindex(req, tmpl, subdir)),
120 142
121 143 up = virtual.rfind('/')
122 144 if up < 0:
123 145 break
124 146 virtual = virtual[:up]
125 147
126 148 # prefixes not found
127 149 req.respond(HTTP_NOT_FOUND, ctype)
128 150 return ''.join(tmpl("notfound", repo=virtual)),
129 151
130 152 except ErrorResponse, err:
131 153 req.respond(err.code, ctype)
132 154 return ''.join(tmpl('error', error=err.message or '')),
133 155 finally:
134 156 tmpl = None
135 157
136 158 def makeindex(self, req, tmpl, subdir=""):
137 159
138 160 def archivelist(ui, nodeid, url):
139 161 allowed = ui.configlist("web", "allow_archive", untrusted=True)
140 162 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
141 163 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
142 164 untrusted=True):
143 165 yield {"type" : i[0], "extension": i[1],
144 166 "node": nodeid, "url": url}
145 167
146 168 def entries(sortcolumn="", descending=False, subdir="", **map):
147 169 def sessionvars(**map):
148 170 fields = []
149 171 if 'style' in req.form:
150 172 style = req.form['style'][0]
151 173 if style != get('web', 'style', ''):
152 174 fields.append(('style', style))
153 175
154 176 separator = url[-1] == '?' and ';' or '?'
155 177 for name, value in fields:
156 178 yield dict(name=name, value=value, separator=separator)
157 179 separator = ';'
158 180
159 181 rows = []
160 182 parity = paritygen(self.stripecount)
161 183 for name, path in self.repos:
162 184 if not name.startswith(subdir):
163 185 continue
164 186 name = name[len(subdir):]
165 187
166 188 u = ui.ui(parentui=self.parentui)
167 189 try:
168 190 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
169 191 except Exception, e:
170 192 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
171 193 continue
172 194 def get(section, name, default=None):
173 195 return u.config(section, name, default, untrusted=True)
174 196
175 197 if u.configbool("web", "hidden", untrusted=True):
176 198 continue
177 199
200 if not self.read_allowed(u, req):
201 continue
202
178 203 parts = [name]
179 204 if 'PATH_INFO' in req.env:
180 205 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
181 206 if req.env['SCRIPT_NAME']:
182 207 parts.insert(0, req.env['SCRIPT_NAME'])
183 208 url = ('/'.join(parts).replace("//", "/")) + '/'
184 209
185 210 # update time with local timezone
186 211 try:
187 212 d = (get_mtime(path), util.makedate()[1])
188 213 except OSError:
189 214 continue
190 215
191 216 contact = get_contact(get)
192 217 description = get("web", "description", "")
193 218 name = get("web", "name", name)
194 219 row = dict(contact=contact or "unknown",
195 220 contact_sort=contact.upper() or "unknown",
196 221 name=name,
197 222 name_sort=name,
198 223 url=url,
199 224 description=description or "unknown",
200 225 description_sort=description.upper() or "unknown",
201 226 lastchange=d,
202 227 lastchange_sort=d[1]-d[0],
203 228 sessionvars=sessionvars,
204 229 archives=archivelist(u, "tip", url))
205 230 if (not sortcolumn
206 231 or (sortcolumn, descending) == self.repos_sorted):
207 232 # fast path for unsorted output
208 233 row['parity'] = parity.next()
209 234 yield row
210 235 else:
211 236 rows.append((row["%s_sort" % sortcolumn], row))
212 237 if rows:
213 238 rows.sort()
214 239 if descending:
215 240 rows.reverse()
216 241 for key, row in rows:
217 242 row['parity'] = parity.next()
218 243 yield row
219 244
220 245 sortable = ["name", "description", "contact", "lastchange"]
221 246 sortcolumn, descending = self.repos_sorted
222 247 if 'sort' in req.form:
223 248 sortcolumn = req.form['sort'][0]
224 249 descending = sortcolumn.startswith('-')
225 250 if descending:
226 251 sortcolumn = sortcolumn[1:]
227 252 if sortcolumn not in sortable:
228 253 sortcolumn = ""
229 254
230 255 sort = [("sort_%s" % column,
231 256 "%s%s" % ((not descending and column == sortcolumn)
232 257 and "-" or "", column))
233 258 for column in sortable]
234 259
235 260 if self._baseurl is not None:
236 261 req.env['SCRIPT_NAME'] = self._baseurl
237 262
238 263 return tmpl("index", entries=entries, subdir=subdir,
239 264 sortcolumn=sortcolumn, descending=descending,
240 265 **dict(sort))
241 266
242 267 def templater(self, req):
243 268
244 269 def header(**map):
245 270 yield tmpl('header', encoding=util._encoding, **map)
246 271
247 272 def footer(**map):
248 273 yield tmpl("footer", **map)
249 274
250 275 def motd(**map):
251 276 if self.motd is not None:
252 277 yield self.motd
253 278 else:
254 279 yield config('web', 'motd', '')
255 280
256 281 def config(section, name, default=None, untrusted=True):
257 282 return self.parentui.config(section, name, default, untrusted)
258 283
259 284 if self._baseurl is not None:
260 285 req.env['SCRIPT_NAME'] = self._baseurl
261 286
262 287 url = req.env.get('SCRIPT_NAME', '')
263 288 if not url.endswith('/'):
264 289 url += '/'
265 290
266 291 staticurl = config('web', 'staticurl') or url + 'static/'
267 292 if not staticurl.endswith('/'):
268 293 staticurl += '/'
269 294
270 295 style = self.style
271 296 if style is None:
272 297 style = config('web', 'style', '')
273 298 if 'style' in req.form:
274 299 style = req.form['style'][0]
275 300 if self.stripecount is None:
276 301 self.stripecount = int(config('web', 'stripes', 1))
277 302 mapfile = style_map(templater.templatepath(), style)
278 303 tmpl = templater.templater(mapfile, templatefilters.filters,
279 304 defaults={"header": header,
280 305 "footer": footer,
281 306 "motd": motd,
282 307 "url": url,
283 308 "staticurl": staticurl})
284 309 return tmpl
General Comments 0
You need to be logged in to leave comments. Login now