##// END OF EJS Templates
hgweb: generate error message only if nothing is passed
Dirkjan Ochtman -
r6924:e8332c81 default
parent child Browse files
Show More
@@ -1,118 +1,118 b''
1 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
1 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
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
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import errno, mimetypes, os
9 import errno, mimetypes, os
10
10
11 HTTP_OK = 200
11 HTTP_OK = 200
12 HTTP_BAD_REQUEST = 400
12 HTTP_BAD_REQUEST = 400
13 HTTP_NOT_FOUND = 404
13 HTTP_NOT_FOUND = 404
14 HTTP_SERVER_ERROR = 500
14 HTTP_SERVER_ERROR = 500
15
15
16 class ErrorResponse(Exception):
16 class ErrorResponse(Exception):
17 def __init__(self, code, message=None):
17 def __init__(self, code, message=None):
18 Exception.__init__(self)
18 Exception.__init__(self)
19 self.code = code
19 self.code = code
20 if message:
20 if message is not None:
21 self.message = message
21 self.message = message
22 else:
22 else:
23 self.message = _statusmessage(code)
23 self.message = _statusmessage(code)
24
24
25 def _statusmessage(code):
25 def _statusmessage(code):
26 from BaseHTTPServer import BaseHTTPRequestHandler
26 from BaseHTTPServer import BaseHTTPRequestHandler
27 responses = BaseHTTPRequestHandler.responses
27 responses = BaseHTTPRequestHandler.responses
28 return responses.get(code, ('Error', 'Unknown error'))[0]
28 return responses.get(code, ('Error', 'Unknown error'))[0]
29
29
30 def statusmessage(code):
30 def statusmessage(code):
31 return '%d %s' % (code, _statusmessage(code))
31 return '%d %s' % (code, _statusmessage(code))
32
32
33 def get_mtime(repo_path):
33 def get_mtime(repo_path):
34 store_path = os.path.join(repo_path, ".hg")
34 store_path = os.path.join(repo_path, ".hg")
35 if not os.path.isdir(os.path.join(store_path, "data")):
35 if not os.path.isdir(os.path.join(store_path, "data")):
36 store_path = os.path.join(store_path, "store")
36 store_path = os.path.join(store_path, "store")
37 cl_path = os.path.join(store_path, "00changelog.i")
37 cl_path = os.path.join(store_path, "00changelog.i")
38 if os.path.exists(cl_path):
38 if os.path.exists(cl_path):
39 return os.stat(cl_path).st_mtime
39 return os.stat(cl_path).st_mtime
40 else:
40 else:
41 return os.stat(store_path).st_mtime
41 return os.stat(store_path).st_mtime
42
42
43 def staticfile(directory, fname, req):
43 def staticfile(directory, fname, req):
44 """return a file inside directory with guessed Content-Type header
44 """return a file inside directory with guessed Content-Type header
45
45
46 fname always uses '/' as directory separator and isn't allowed to
46 fname always uses '/' as directory separator and isn't allowed to
47 contain unusual path components.
47 contain unusual path components.
48 Content-Type is guessed using the mimetypes module.
48 Content-Type is guessed using the mimetypes module.
49 Return an empty string if fname is illegal or file not found.
49 Return an empty string if fname is illegal or file not found.
50
50
51 """
51 """
52 parts = fname.split('/')
52 parts = fname.split('/')
53 path = directory
53 path = directory
54 for part in parts:
54 for part in parts:
55 if (part in ('', os.curdir, os.pardir) or
55 if (part in ('', os.curdir, os.pardir) or
56 os.sep in part or os.altsep is not None and os.altsep in part):
56 os.sep in part or os.altsep is not None and os.altsep in part):
57 return ""
57 return ""
58 path = os.path.join(path, part)
58 path = os.path.join(path, part)
59 try:
59 try:
60 os.stat(path)
60 os.stat(path)
61 ct = mimetypes.guess_type(path)[0] or "text/plain"
61 ct = mimetypes.guess_type(path)[0] or "text/plain"
62 req.respond(HTTP_OK, ct, length = os.path.getsize(path))
62 req.respond(HTTP_OK, ct, length = os.path.getsize(path))
63 return file(path, 'rb').read()
63 return file(path, 'rb').read()
64 except TypeError:
64 except TypeError:
65 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal file name')
65 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal file name')
66 except OSError, err:
66 except OSError, err:
67 if err.errno == errno.ENOENT:
67 if err.errno == errno.ENOENT:
68 raise ErrorResponse(HTTP_NOT_FOUND)
68 raise ErrorResponse(HTTP_NOT_FOUND)
69 else:
69 else:
70 raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror)
70 raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror)
71
71
72 def style_map(templatepath, style):
72 def style_map(templatepath, style):
73 """Return path to mapfile for a given style.
73 """Return path to mapfile for a given style.
74
74
75 Searches mapfile in the following locations:
75 Searches mapfile in the following locations:
76 1. templatepath/style/map
76 1. templatepath/style/map
77 2. templatepath/map-style
77 2. templatepath/map-style
78 3. templatepath/map
78 3. templatepath/map
79 """
79 """
80 locations = style and [os.path.join(style, "map"), "map-"+style] or []
80 locations = style and [os.path.join(style, "map"), "map-"+style] or []
81 locations.append("map")
81 locations.append("map")
82 for location in locations:
82 for location in locations:
83 mapfile = os.path.join(templatepath, location)
83 mapfile = os.path.join(templatepath, location)
84 if os.path.isfile(mapfile):
84 if os.path.isfile(mapfile):
85 return mapfile
85 return mapfile
86 raise RuntimeError("No hgweb templates found in %r" % templatepath)
86 raise RuntimeError("No hgweb templates found in %r" % templatepath)
87
87
88 def paritygen(stripecount, offset=0):
88 def paritygen(stripecount, offset=0):
89 """count parity of horizontal stripes for easier reading"""
89 """count parity of horizontal stripes for easier reading"""
90 if stripecount and offset:
90 if stripecount and offset:
91 # account for offset, e.g. due to building the list in reverse
91 # account for offset, e.g. due to building the list in reverse
92 count = (stripecount + offset) % stripecount
92 count = (stripecount + offset) % stripecount
93 parity = (stripecount + offset) / stripecount & 1
93 parity = (stripecount + offset) / stripecount & 1
94 else:
94 else:
95 count = 0
95 count = 0
96 parity = 0
96 parity = 0
97 while True:
97 while True:
98 yield parity
98 yield parity
99 count += 1
99 count += 1
100 if stripecount and count >= stripecount:
100 if stripecount and count >= stripecount:
101 parity = 1 - parity
101 parity = 1 - parity
102 count = 0
102 count = 0
103
103
104 def countgen(start=0, step=1):
104 def countgen(start=0, step=1):
105 """count forever -- useful for line numbers"""
105 """count forever -- useful for line numbers"""
106 while True:
106 while True:
107 yield start
107 yield start
108 start += step
108 start += step
109
109
110 def get_contact(config):
110 def get_contact(config):
111 """Return repo contact information or empty string.
111 """Return repo contact information or empty string.
112
112
113 web.contact is the primary source, but if that is not set, try
113 web.contact is the primary source, but if that is not set, try
114 ui.username or $EMAIL as a fallback to display something useful.
114 ui.username or $EMAIL as a fallback to display something useful.
115 """
115 """
116 return (config("web", "contact") or
116 return (config("web", "contact") or
117 config("ui", "username") or
117 config("ui", "username") or
118 os.environ.get("EMAIL") or "")
118 os.environ.get("EMAIL") or "")
General Comments 0
You need to be logged in to leave comments. Login now