-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathroundup.cgi
More file actions
executable file
·317 lines (273 loc) · 9.97 KB
/
Copy pathroundup.cgi
File metadata and controls
executable file
·317 lines (273 loc) · 9.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#!/usr/bin/env python
#
# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
# This module is free software, and you may redistribute it and/or modify
# under the same terms as Python, so long as this copyright message and
# disclaimer are retained in their original form.
#
# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
import os
import sys
import time
# If roundup is not on your normal path, add it here.
# sys.path.append('/home/username/develop/roundup_dev')
from roundup import version_check # noqa: F401
from roundup.anypy import http_
from roundup.anypy.html import html_escape
from roundup.anypy.strings import StringIO, s2b
from roundup.i18n import _
#
## Configuration
#
# Configuration can also be provided through the OS environment (or via
# the Apache "SetEnv" configuration directive). If the variables
# documented below are set, they _override_ any configuation defaults
# given in this file.
# TRACKER_HOMES is a list of trackers, in the form
# "NAME=DIR<sep>NAME2=DIR2<sep>...", where <sep> is the directory path
# separator (";" on Windows, ":" on Unix).
# Make sure the NAME part doesn't include any url-unsafe characters like
# spaces, as these confuse the cookie handling in browsers like IE.
# ROUNDUP_LOG is the name of the logfile; if it's empty or does not exist,
# logging is turned off (unless you changed the default below).
# DEBUG_TO_CLIENT specifies whether debugging goes to the HTTP server (via
# stderr) or to the web client (via cgitb).
DEBUG_TO_CLIENT = False
# This indicates where the Roundup tracker lives
TRACKER_HOMES = {
# 'example': '/path/to/example',
}
# print timing at bottom of web page
#TIMING = "INLINE"
# print timing inside web page comments
#TIMING = "COMMENT"
# disable timing
TIMING = ""
# Where to log debugging information to. Use an instance of DevNull if you
# don't want to log anywhere.
class DevNull:
def write(self, info):
pass
def close(self):
pass
def flush(self):
pass
#LOG = open('/var/log/roundup.cgi.log', 'a')
LOG = DevNull()
#
## end configuration
#
DEFAULT_ERROR_MESSAGE = http_.server.DEFAULT_ERROR_MESSAGE
#
# Set up the error handler
#
try:
import traceback
from roundup.cgi import cgitb
except ImportError:
print("Content-Type: text/plain\n")
print(_("Failed to import cgitb!\n\n"))
s = StringIO()
traceback.print_exc(None, s)
print(s.getvalue())
#
# Check environment for config items
#
def checkconfig():
global TRACKER_HOMES, LOG
# see if there's an environment var. ROUNDUP_INSTANCE_HOMES is the
# old name for it.
if 'ROUNDUP_INSTANCE_HOMES' in os.environ:
homes = os.environ.get('ROUNDUP_INSTANCE_HOMES')
else:
homes = os.environ.get('TRACKER_HOMES', '')
if homes:
TRACKER_HOMES = {}
for home in homes.split(os.pathsep):
try:
name, dir_ = home.split('=', 1)
except ValueError:
# ignore invalid definitions
continue
if name and dir_:
TRACKER_HOMES[name] = dir_
logname = os.environ.get('ROUNDUP_LOG', '')
if logname:
LOG = open(logname, 'a')
# ROUNDUP_DEBUG is checked directly in "roundup.cgi.client"
#
# Provide interface to CGI HTTP response handling
#
class RequestWrapper:
'''Used to make the CGI server look like a BaseHTTPRequestHandler
'''
def __init__(self, wfile):
self.rfile = sys.stdin
self.wfile = wfile
self.headers = {}
def write(self, data):
self.wfile.write(data)
def send_response(self, code):
self.write(s2b('Status: %s\r\n' % code))
def send_header(self, keyword, value):
self.write(s2b("%s: %s\r\n" % (keyword, value)))
def end_headers(self):
self.write(b"\r\n")
def start_response(self, headers, response):
self.send_response(response)
for key, value in headers:
self.send_header(key, value)
self.end_headers()
#
# Main CGI handler
#
def main(out, err):
import roundup.cgi.client
import roundup.instance
if TIMING:
os.environ["CGI_SHOW_TIMING"] = TIMING
request = RequestWrapper(out)
problem_header = roundup.cgi.client.are_header_values_safe(os.environ)
if problem_header:
error_body = """<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 400</p>
<p>Message: Invalid value: %s.</p>
<p>Error code explanation: 400 - The value for the header is unacceptable.</p>
</body>
</html>
""" % problem_header
request.send_response(400)
request.send_header('Content-Type', 'text/html')
request.send_header('Content-Length', '%s' % len(error_body))
request.end_headers()
out.write(s2b(error_body))
return
request.path = os.environ.get('PATH_INFO', '/')
if not request.path: # missing trailing /
request.path = "/"
path = request.path.split('/')
tracker = path[1]
os.environ['TRACKER_NAME'] = tracker
os.environ['PATH_INFO'] = '/'.join(path[2:])
if tracker in TRACKER_HOMES:
# redirect if we need a trailing '/'
if len(path) == 2:
request.send_response(301)
# redirect
if os.environ.get('HTTPS', '') == 'on':
protocol = 'https'
else:
protocol = 'http'
absolute_url = '%s://%s%s/' % (protocol, os.environ['HTTP_HOST'],
os.environ.get('REQUEST_URI', ''))
request.send_header('Location', absolute_url)
request.end_headers()
out.write(b'Moved Permanently')
else:
tracker_home = TRACKER_HOMES[tracker]
tracker = roundup.instance.open(tracker_home)
try:
if hasattr(tracker, 'Client'):
client = tracker.Client(tracker, request, os.environ)
else:
client = roundup.cgi.client.Client(tracker,
request, os.environ)
except ValueError as e:
code = 400
message = str(e)
explain = "The value for the header is unacceptable"
body = DEFAULT_ERROR_MESSAGE % locals()
request.send_response(code)
request.send_header('Content-Type', 'text/html')
request.send_header('Content-Length', '%s' % len(body))
request.end_headers()
out.write(s2b(body))
try:
client.main()
except roundup.cgi.client.Unauthorised:
body = b'Unauthorised'
request.send_response(403)
request.send_header('Content-Type', 'text/html')
request.send_header('Content-Length', '%s' % len(body))
request.end_headers()
out.write(body)
except roundup.cgi.client.NotFound:
body = s2b('Not found: %s' % html_escape(client.path))
request.send_response(404)
request.send_header('Content-Type', 'text/html')
request.send_header('Content-Length', '%s' % len(body))
request.end_headers()
out.write(body)
except Exception as e:
body = s2b('Error: %s' % html_escape(e))
request.send_response(400)
request.send_header('Content-Type', 'text/html')
request.send_header('Content-Length', '%s' % len(body))
request.end_headers()
out.write(body)
else:
from roundup.anypy import urllib_
request.send_response(200)
request.send_header('Content-Type', 'text/html')
request.end_headers()
w = request.write
w(s2b(_('<html><head><title>Roundup trackers index</title></head>\n')))
w(s2b(_('<body><h1>Roundup trackers index</h1><ol>\n')))
homes = sorted(TRACKER_HOMES.keys())
for tracker in homes:
w(s2b(_('<li><a href="%(tracker_url)s/index">%(tracker_name)s</a>\n') % {
'tracker_url': os.environ['SCRIPT_NAME'] + '/' +
urllib_.quote(tracker),
'tracker_name': html_escape(tracker)}))
w(s2b(_('</ol></body></html>')))
#
# Now do the actual CGI handling
#
out, err = sys.stdout, sys.stderr
try:
# force input/output to binary (important for file up/downloads)
if sys.platform == "win32":
import msvcrt
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
checkconfig()
sys.stdout = sys.stderr = LOG
if sys.version_info[0] > 2:
out_buf = out.buffer
else:
out_buf = out
main(out_buf, err)
except SystemExit:
pass
except Exception as e:
sys.stdout, sys.stderr = out, err
out.write('Content-Type: text/html\n\n')
if DEBUG_TO_CLIENT:
cgitb.handler()
else:
out.write(cgitb.breaker())
ts = time.ctime()
out.write('''<p>%s: An error occurred. Please check
the server log for more information.</p>''' % ts)
print('EXCEPTION AT', ts, file=sys.stderr)
traceback.print_exc(0, sys.stderr)
sys.stdout.flush()
sys.stdout, sys.stderr = out, err
LOG.close()
# vim: set filetype=python ts=4 sw=4 et si