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