##// END OF EJS Templates
hgweb: show correct error message for i18n environment...
hgweb: show correct error message for i18n environment If exception is error.LookupError and running in i18n environment, below condition is always true. Because msg is translated and dosen't contain 'manifest'. if util.safehasattr(err, 'name') and 'manifest' not in msg: This patch creates a new exception class and uses it instead of string match.

File last commit:

r18855:50c922c1 2.5.3 stable
r18855:50c922c1 2.5.3 stable
Show More
hgweb_mod.py
346 lines | 12.8 KiB | text/x-python | PythonLexer
Eric Hopper
Fixing up comment headers for split up code.
r2391 # hgweb/hgweb_mod.py - Web interface for a repository.
Eric Hopper
Final stage of the hgweb split up....
r2356 #
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
Eric Hopper
Final stage of the hgweb split up....
r2356 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Eric Hopper
Final stage of the hgweb split up....
r2356
Peter Arrenbrecht
cleanup: drop unused imports
r7873 import os
Pierre-Yves David
hgweb: add a `web.view` to control filtering...
r18522 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
Martin Geisler
hgweb: detect change based on changelog size too...
r13958 from common import get_stat, ErrorResponse, permhooks, caching
Augie Fackler
hgweb: don't send a body or illegal headers during 304 response...
r12739 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
Dirkjan Ochtman
Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
r5566 from request import wsgirequest
Dirkjan Ochtman
hgweb: separate out utility functions
r6392 import webcommands, protocol, webutil
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgweb: centralize permission checks for protocol commands...
r6779 perms = {
'changegroup': 'pull',
'changegroupsubset': 'pull',
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 'getbundle': 'pull',
Matt Mackall
pushkey: add http support...
r11370 'stream_out': 'pull',
'listkeys': 'pull',
Dirkjan Ochtman
hgweb: centralize permission checks for protocol commands...
r6779 'unbundle': 'push',
Matt Mackall
pushkey: add http support...
r11370 'pushkey': 'push',
Dirkjan Ochtman
hgweb: centralize permission checks for protocol commands...
r6779 }
Angel Ezquerra
hgwebdir: use web.prefix when creating url breadcrumbs (issue3790)...
r18515 def makebreadcrumb(url, prefix=''):
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 '''Return a 'URL breadcrumb' list
A 'URL breadcrumb' is a list of URL-name pairs,
corresponding to each of the path items on a URL.
This can be used to create path navigation entries.
'''
if url.endswith('/'):
url = url[:-1]
Angel Ezquerra
hgwebdir: use web.prefix when creating url breadcrumbs (issue3790)...
r18515 if prefix:
url = '/' + prefix + url
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 relpath = url
if relpath.startswith('/'):
relpath = relpath[1:]
breadcrumb = []
urlel = url
pathitems = [''] + relpath.split('/')
for pathel in reversed(pathitems):
if not pathel or not urlel:
break
breadcrumb.append({'url': urlel, 'name': pathel})
urlel = os.path.dirname(urlel)
return reversed(breadcrumb)
Eric Hopper
Final stage of the hgweb split up....
r2356 class hgweb(object):
Matt Mackall
hgweb: add baseui to hgweb entrypoint
r10994 def __init__(self, repo, name=None, baseui=None):
Christian Ebert
Use isinstance instead of type == type
r4874 if isinstance(repo, str):
Matt Mackall
hgweb: add baseui to hgweb entrypoint
r10994 if baseui:
u = baseui.copy()
else:
Matt Mackall
hgweb: another fix for the help termwidth bug
r12696 u = ui.ui()
Matt Mackall
hgweb: kill parentui references
r8191 self.repo = hg.repository(u, repo)
Eric Hopper
Final stage of the hgweb split up....
r2356 else:
self.repo = repo
Pierre-Yves David
hgweb: add a `web.view` to control filtering...
r18522 self.repo = self._getview(self.repo)
Matt Mackall
hgweb: always clear report_untrusted and interactive
r10995 self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
Matt Mackall
hgweb: use ui.nontty to disable all cooked I/O
r16754 self.repo.ui.setconfig('ui', 'nontty', 'true')
Matt Mackall
hook: redirect stdout to stderr for ssh and http servers
r5833 hook.redirect(True)
Eric Hopper
Final stage of the hgweb split up....
r2356 self.mtime = -1
Martin Geisler
hgweb: detect change based on changelog size too...
r13958 self.size = -1
Eric Hopper
Final stage of the hgweb split up....
r2356 self.reponame = name
self.archives = 'zip', 'gz', 'bz2'
Frank Kingswood
hgweb: Configurable zebra stripes...
r2666 self.stripecount = 1
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 # a repo owner may set web.templates in .hg/hgrc to get any file
# readable by the user running the CGI script
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966 self.templatepath = self.config('web', 'templates')
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555
# The CGI scripts are often run by a user different from the repo owner.
# Trust the settings from the .hg/hgrc files by default.
def config(self, section, name, default=None, untrusted=True):
return self.repo.ui.config(section, name, default,
untrusted=untrusted)
def configbool(self, section, name, default=False, untrusted=True):
return self.repo.ui.configbool(section, name, default,
untrusted=untrusted)
def configlist(self, section, name, default=None, untrusted=True):
return self.repo.ui.configlist(section, name, default,
untrusted=untrusted)
Eric Hopper
Final stage of the hgweb split up....
r2356
Pierre-Yves David
hgweb: add a `web.view` to control filtering...
r18522 def _getview(self, repo):
viewconfig = self.config('web', 'view', 'served')
if viewconfig == 'all':
return repo.unfiltered()
elif viewconfig in repoview.filtertable:
return repo.filtered(viewconfig)
else:
return repo.filtered('served')
Sune Foldager
ui: add environ property to access os.environ or wsgirequest.environ...
r9887 def refresh(self, request=None):
if request:
Henrik Stuart
hgweb: fix error in 38170eeed18c and introduce test for change
r9888 self.repo.ui.environ = request.env
Martin Geisler
hgweb: detect change based on changelog size too...
r13958 st = get_stat(self.repo.spath)
# compare changelog size in addition to mtime to catch
# rollbacks made less than a second ago
if st.st_mtime != self.mtime or st.st_size != self.size:
self.mtime = st.st_mtime
self.size = st.st_size
Eric Hopper
Final stage of the hgweb split up....
r2356 self.repo = hg.repository(self.repo.ui, self.repo.root)
Pierre-Yves David
hgweb: add a `web.view` to control filtering...
r18522 self.repo = self._getview(self.repo)
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 self.maxchanges = int(self.config("web", "maxchanges", 10))
self.stripecount = int(self.config("web", "stripes", 1))
Brodie Rao
cleanup: eradicate long lines
r16683 self.maxshortchanges = int(self.config("web", "maxshortchanges",
60))
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 self.maxfiles = int(self.config("web", "maxfiles", 10))
self.allowpull = self.configbool("web", "allowpull", True)
Matt Mackall
hgweb: web.encoding should override encoding.encoding (issue1183)
r8859 encoding.encoding = self.config("web", "encoding",
encoding.encoding)
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 def run(self):
if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
Martin Geisler
wrap string literals in error messages
r8663 raise RuntimeError("This function is only intended to be "
"called while running as a CGI script.")
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 import mercurial.hgweb.wsgicgi as wsgicgi
wsgicgi.launch(self)
def __call__(self, env, respond):
req = wsgirequest(env, respond)
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 return self.run_wsgi(req)
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
def run_wsgi(self, req):
Sune Foldager
ui: add environ property to access os.environ or wsgirequest.environ...
r9887 self.refresh(req)
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # work with CGI variables to create coherent structure
# use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 req.url = req.env['SCRIPT_NAME']
if not req.url.endswith('/'):
req.url += '/'
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'REPO_NAME' in req.env:
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 req.url += req.env['REPO_NAME'] + '/'
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: make hgwebdir work in the absence of PATH_INFO...
r6459 if 'PATH_INFO' in req.env:
parts = req.env['PATH_INFO'].strip('/').split('/')
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 repo_parts = req.env.get('REPO_NAME', '').split('/')
if parts[:len(repo_parts)] == repo_parts:
parts = parts[len(repo_parts):]
query = '/'.join(parts)
else:
query = req.env['QUERY_STRING'].split('&', 1)[0]
query = query.split(';', 1)[0]
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Benoit Boissinot
hgweb: deny cloning a subpath of a repo
r8860 # process this if it's a protocol request
# protocol bits don't need to create any URLs
# and the clients always use the old URL structure
cmd = req.form.get('cmd', [''])[0]
Matt Mackall
protocol: move hgweb protocol support back into protocol.py...
r11595 if protocol.iscmd(cmd):
Mads Kiilerich
hgweb: handle invalid requests with both form data and querystring...
r13445 try:
if query:
raise ErrorResponse(HTTP_NOT_FOUND)
if cmd in perms:
Matt Mackall
protocol: move hgweb protocol support back into protocol.py...
r11595 self.check_perm(req, perms[cmd])
Mads Kiilerich
hgweb: handle invalid requests with both form data and querystring...
r13445 return protocol.call(self.repo, req, cmd)
except ErrorResponse, inst:
Augie Fackler
hgweb_mod: respond right away if the client specified 100-continue support...
r13571 # A client that sends unbundle without 100-continue will
# break if we respond early.
if (cmd == 'unbundle' and
Augie Fackler
http2: send an extra header to signal a non-broken client...
r14991 (req.env.get('HTTP_EXPECT',
'').lower() != '100-continue') or
req.env.get('X-HgHttp2', '')):
Mads Kiilerich
hgweb: handle invalid requests with both form data and querystring...
r13445 req.drain()
Mads Kiilerich
hgweb: pass the actual response body to request.response, not just the length...
r18352 req.respond(inst, protocol.HGTYPE,
body='0\n%s\n' % inst.message)
return ''
Benoit Boissinot
hgweb: deny cloning a subpath of a repo
r8860
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # translate user-visible url structure to internal structure
args = query.split('/', 2)
if 'cmd' not in req.form and args and args[0]:
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
cmd = args.pop(0)
style = cmd.rfind('-')
if style != -1:
req.form['style'] = [cmd[:style]]
Matt Mackall
many, many trivial check-code fixups
r10282 cmd = cmd[style + 1:]
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 # avoid accepting e.g. style parameter as command
Augie Fackler
hgweb_mod: use safehasattr instead of hasattr
r14953 if util.safehasattr(webcommands, cmd):
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 req.form['cmd'] = [cmd]
Dirkjan Ochtman
hgweb: protocol requests are processed immediately...
r6777 else:
cmd = ''
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Brendan Cully
hgweb: handle subdirectories within static directory
r7287 if cmd == 'static':
req.form['file'] = ['/'.join(args)]
else:
if args and args[0]:
node = args.pop(0)
req.form['node'] = [node]
if args:
req.form['file'] = args
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: treat rev as raw-rev if user agent is hg
r9731 ua = req.env.get('HTTP_USER_AGENT', '')
if cmd == 'rev' and 'mercurial' in ua:
req.form['style'] = ['raw']
Brendan Cully
hgweb: handle subdirectories within static directory
r7287 if cmd == 'archive':
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 fn = req.form['node'][0]
for type_, spec in self.archive_specs.iteritems():
ext = spec[2]
if fn.endswith(ext):
req.form['node'] = [fn[:-len(ext)]]
req.form['type'] = [type_]
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 # process the web interface request
Dirkjan Ochtman
hgweb: split out templater definition
r5599
try:
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 tmpl = self.templater(req)
Matt Mackall
hgweb: web.encoding should override encoding.encoding (issue1183)
r8859 ctype = tmpl('mimetype', encoding=encoding.encoding)
Dirkjan Ochtman
Backed out changeset d2bb66a8a435 (temporary template compatibility)
r6391 ctype = templater.stringify(ctype)
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149
Mark Edgington
hgweb: allow static content when deny_read denies access
r7562 # check read permissions non-static content
if cmd != 'static':
self.check_perm(req, None)
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 if cmd == '':
req.form['cmd'] = [tmpl.cache['default']]
cmd = req.form['cmd'][0]
Dirkjan Ochtman
hgweb: fast path for sending raw files
r5890
Steven Stallion
hgweb: support disabling page cache...
r13966 if self.configbool('web', 'cache', True):
caching(self, req) # sets ETag header or raises NOT_MODIFIED
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 if cmd not in webcommands.__all__:
Dirkjan Ochtman
hgweb: better error messages
r6368 msg = 'no such method: %s' % cmd
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
elif cmd == 'file' and 'raw' in req.form.get('style', []):
self.ctype = ctype
content = webcommands.rawfile(self, req, tmpl)
else:
content = getattr(webcommands, cmd)(self, req, tmpl)
req.respond(HTTP_OK, ctype)
Dirkjan Ochtman
hgweb: fast path for sending raw files
r5890
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 return content
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600
Pierre-Yves David
hgweb: returns 404 for unknow revision instead of 500...
r18521 except (error.LookupError, error.RepoLookupError), err:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_NOT_FOUND, ctype)
Dirkjan Ochtman
hgweb: fix breakage in python < 2.5 introduced in 2c370f08c486
r6374 msg = str(err)
Takumi IINO
hgweb: show correct error message for i18n environment...
r18855 if (util.safehasattr(err, 'name') and
not isinstance(err, error.ManifestLookupError)):
Dirkjan Ochtman
hgweb: better error messages
r6368 msg = 'revision not found: %s' % err.name
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 return tmpl('error', error=msg)
Matt Mackall
error: move repo errors...
r7637 except (error.RepoError, error.RevlogError), inst:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_SERVER_ERROR, ctype)
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 return tmpl('error', error=str(inst))
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 except ErrorResponse, inst:
Dirkjan Ochtman
hgweb: pass ErrorResponses directly into req.respond()
r7740 req.respond(inst, ctype)
Augie Fackler
hgweb: don't send a body or illegal headers during 304 response...
r12739 if inst.code == HTTP_NOT_MODIFIED:
# Not allowed to return a body on a 304
return ['']
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 return tmpl('error', error=inst.message)
Dirkjan Ochtman
hgweb: split out templater definition
r5599
def templater(self, req):
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # determine scheme, port and server name
# this is needed to create absolute urls
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
proto = req.env.get('wsgi.url_scheme')
if proto == 'https':
proto = 'https'
default_port = "443"
else:
proto = 'http'
default_port = "80"
port = req.env["SERVER_PORT"]
port = port != default_port and (":" + port) or ""
urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
Steven Stallion
hgweb: support alternate logo url...
r13964 logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
Angel Ezquerra
hgweb: add a "web/logoimg" setting to customize the web logo image...
r14913 logoimg = self.config("web", "logoimg", "hglogo.png")
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 staticurl = self.config("web", "staticurl") or req.url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # some functions for the templater
def header(**map):
Matt Mackall
hgweb: web.encoding should override encoding.encoding (issue1183)
r8859 yield tmpl('header', encoding=encoding.encoding, **map)
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
def footer(**map):
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 yield tmpl("footer", **map)
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
def motd(**map):
yield self.config("web", "motd", "")
Dirkjan Ochtman
hgweb: split out templater definition
r5599 # figure out which style to use
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 vars = {}
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 styles = (
req.form.get('style', [None])[0],
self.config('web', 'style'),
'paper',
)
style, mapfile = templater.stylemap(styles, self.templatepath)
if style == styles[0]:
Dirkjan Ochtman
hgweb: fix up the less/more links on the graph page...
r7345 vars['style'] = style
start = req.url[-1] == '?' and '&' or '?'
sessionvars = webutil.sessionvars(vars, start)
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 if not self.reponame:
self.reponame = (self.config("web", "name")
or req.env.get('REPO_NAME')
or req.url.strip('/') or self.repo.root)
Dirkjan Ochtman
hgweb: split out templater definition
r5599 # create the templater
Dirkjan Ochtman
templater: provide the standard template filters by default
r8360 tmpl = templater.templater(mapfile,
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 defaults={"url": req.url,
Steven Stallion
hgweb: support alternate logo url...
r13964 "logourl": logourl,
Angel Ezquerra
hgweb: add a "web/logoimg" setting to customize the web logo image...
r14913 "logoimg": logoimg,
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 "staticurl": staticurl,
"urlbase": urlbase,
"repo": self.reponame,
"header": header,
"footer": footer,
"motd": motd,
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 "sessionvars": sessionvars,
"pathdef": makebreadcrumb(req.url),
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600 })
return tmpl
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Eric Hopper
Final stage of the hgweb split up....
r2356 def archivelist(self, nodeid):
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 allowed = self.configlist("web", "allow_archive")
Brendan Cully
hgweb: accept NewWebInterface URLs
r3260 for i, spec in self.archive_specs.iteritems():
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555 if i in allowed or self.configbool("web", "allow" + i):
Brendan Cully
hgweb: accept NewWebInterface URLs
r3260 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
Eric Hopper
Final stage of the hgweb split up....
r2356
archive_specs = {
Ry4an Brase
Correct Content-Type header values for archive downloads....
r12570 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None),
'gz': ('application/x-gzip', 'tgz', '.tar.gz', None),
Eric Hopper
Final stage of the hgweb split up....
r2356 'zip': ('application/zip', 'zip', '.zip', None),
}
Dirkjan Ochtman
hgweb: centralize permission checks for protocol commands...
r6779 def check_perm(self, req, op):
Sune Foldager
hgweb: add support for extension-provided permission hooks...
r9910 for hook in permhooks:
hook(self, req, op)