common.py
105 lines
| 3.5 KiB
| text/x-python
|
PythonLexer
Eric Hopper
|
r2391 | # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod | ||
Eric Hopper
|
r2356 | # | ||
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | ||||
Vadim Gelfer
|
r2859 | # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> | ||
Eric Hopper
|
r2356 | # | ||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
# GNU General Public License version 2, incorporated herein by reference. | ||||
Eric Hopper
|
r2356 | |||
Bryan O'Sullivan
|
r5561 | import errno, mimetypes, os | ||
Dirkjan Ochtman
|
r5993 | HTTP_OK = 200 | ||
HTTP_BAD_REQUEST = 400 | ||||
Dirkjan Ochtman
|
r6926 | HTTP_UNAUTHORIZED = 401 | ||
Rocco Rutte
|
r7029 | HTTP_FORBIDDEN = 403 | ||
Dirkjan Ochtman
|
r5993 | HTTP_NOT_FOUND = 404 | ||
Dirkjan Ochtman
|
r6926 | HTTP_METHOD_NOT_ALLOWED = 405 | ||
Dirkjan Ochtman
|
r5993 | HTTP_SERVER_ERROR = 500 | ||
Bryan O'Sullivan
|
r5561 | class ErrorResponse(Exception): | ||
Sune Foldager
|
r7741 | def __init__(self, code, message=None, headers=[]): | ||
Bryan O'Sullivan
|
r5561 | Exception.__init__(self) | ||
self.code = code | ||||
Sune Foldager
|
r7741 | self.headers = headers | ||
Dirkjan Ochtman
|
r6924 | if message is not None: | ||
Bryan O'Sullivan
|
r5563 | self.message = message | ||
Bryan O'Sullivan
|
r5561 | else: | ||
Bryan O'Sullivan
|
r5563 | self.message = _statusmessage(code) | ||
def _statusmessage(code): | ||||
from BaseHTTPServer import BaseHTTPRequestHandler | ||||
responses = BaseHTTPRequestHandler.responses | ||||
return responses.get(code, ('Error', 'Unknown error'))[0] | ||||
Thomas Arendsen Hein
|
r5760 | |||
Bryan O'Sullivan
|
r5563 | def statusmessage(code): | ||
return '%d %s' % (code, _statusmessage(code)) | ||||
Eric Hopper
|
r2356 | |||
def get_mtime(repo_path): | ||||
Benoit Boissinot
|
r3853 | store_path = os.path.join(repo_path, ".hg") | ||
if not os.path.isdir(os.path.join(store_path, "data")): | ||||
store_path = os.path.join(store_path, "store") | ||||
cl_path = os.path.join(store_path, "00changelog.i") | ||||
if os.path.exists(cl_path): | ||||
Eric Hopper
|
r2356 | return os.stat(cl_path).st_mtime | ||
else: | ||||
Benoit Boissinot
|
r3853 | return os.stat(store_path).st_mtime | ||
Eric Hopper
|
r2356 | |||
Eric Hopper
|
r2514 | def staticfile(directory, fname, req): | ||
Dirkjan Ochtman
|
r5930 | """return a file inside directory with guessed Content-Type header | ||
Eric Hopper
|
r2356 | |||
fname always uses '/' as directory separator and isn't allowed to | ||||
contain unusual path components. | ||||
Dirkjan Ochtman
|
r5930 | Content-Type is guessed using the mimetypes module. | ||
Eric Hopper
|
r2356 | Return an empty string if fname is illegal or file not found. | ||
""" | ||||
parts = fname.split('/') | ||||
for part in parts: | ||||
if (part in ('', os.curdir, os.pardir) or | ||||
os.sep in part or os.altsep is not None and os.altsep in part): | ||||
return "" | ||||
Brendan Cully
|
r7288 | fpath = os.path.join(*parts) | ||
if isinstance(directory, str): | ||||
directory = [directory] | ||||
for d in directory: | ||||
path = os.path.join(d, fpath) | ||||
if os.path.exists(path): | ||||
break | ||||
Eric Hopper
|
r2356 | try: | ||
os.stat(path) | ||||
ct = mimetypes.guess_type(path)[0] or "text/plain" | ||||
Dirkjan Ochtman
|
r5993 | req.respond(HTTP_OK, ct, length = os.path.getsize(path)) | ||
Wojciech Milkowski
|
r3244 | return file(path, 'rb').read() | ||
Bryan O'Sullivan
|
r5561 | except TypeError: | ||
timeless
|
r8761 | raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename') | ||
Bryan O'Sullivan
|
r5561 | except OSError, err: | ||
if err.errno == errno.ENOENT: | ||||
Dirkjan Ochtman
|
r5993 | raise ErrorResponse(HTTP_NOT_FOUND) | ||
Bryan O'Sullivan
|
r5561 | else: | ||
Dirkjan Ochtman
|
r5993 | raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror) | ||
Thomas Arendsen Hein
|
r3276 | |||
Thomas Arendsen Hein
|
r4462 | def paritygen(stripecount, offset=0): | ||
"""count parity of horizontal stripes for easier reading""" | ||||
if stripecount and offset: | ||||
# account for offset, e.g. due to building the list in reverse | ||||
count = (stripecount + offset) % stripecount | ||||
parity = (stripecount + offset) / stripecount & 1 | ||||
else: | ||||
count = 0 | ||||
parity = 0 | ||||
while True: | ||||
yield parity | ||||
count += 1 | ||||
if stripecount and count >= stripecount: | ||||
parity = 1 - parity | ||||
count = 0 | ||||
Thomas Arendsen Hein
|
r5779 | def get_contact(config): | ||
"""Return repo contact information or empty string. | ||||
web.contact is the primary source, but if that is not set, try | ||||
ui.username or $EMAIL as a fallback to display something useful. | ||||
""" | ||||
return (config("web", "contact") or | ||||
config("ui", "username") or | ||||
os.environ.get("EMAIL") or "") | ||||