* lib/sapi.py

- Filled in ModPythonServer and AspServer header() methods to make use of
      the new http 'status' parameter. Also, changed CgiServer's header()
      method to ignore the status parameter under IIS to prevent the server
      from discarding the ViewCVS output and instead sending a static error
      page.
    - ModPythonFile and AspFile classes have been eliminated and replaced
      with a more generic File class. All server classes have new write()
      and flush() methods.
    - Common code from AspServer and ModPythonServer has been moved into
      a base class called ThreadedServer. AspProxy is renamed to
      ThreadedServerProxy.
    - All server classes now inherit from a new base class called Server
      which contains the small amount of code common to all of them.
    - added fix_iis_path_info() function, renamed IIS_FixURL() to
      fix_iis_url()
    - renamed getFile() methods to file()

* lib/viewcvs.py
    - Eliminated global server variable. Changed some scattered
      server.escape() calls into cgi.escape() calls. Got around other uses
      of the variable by adding a server member to the Request class.
    - Deleted gstein's strongly worded comment about the quality of the sapi
      hack :)
    - Page-global 'g_name_printed' is now passed as a normal parameter
      called 'name_printed' to the augment_entry() function
    - Got rid of some confusing string manipulation in human_readable_diff()
    - added 'server' parameter to viewcvs.main() to avoid relying on the
      sapi.server global variable

* cgi/viewcvs.cgi, windows/viewcvs.py, windows/viewcvs.asp
    - Added 'server' parameter to viewcvs.main() calls

* standalone.py:
    - Changed StandaloneServer.header() method to accept http status code
    - Added 'server' parameter to viewcvs.main() call

* lib/query.py
    - added 'server' parameter to query.main() to avoid relying on the
      sapi.server global variable
    - got rid of global 'server' variable and page-global 'viewcvs_link'
      variable, instead those values are passed as function parameters

* cgi/query.cgi, windows/query.py, windows/query.asp
    - Added 'server' parameter to query.main() calls

* lib/debug.py
    - Changed PrintStackTrace(), PrintException(), and DumpChildren() not to
      rely on global sapi.server variable and to use new server.write()
      method

* lib/popen.py
    - changed server.getFile() calls to server.file()


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@642 8cb11bc2-c004-0410-86c3-e597b4017df7
remotes/tags/1.0.0-rc1
rey4 2003-03-12 23:08:09 +00:00
parent c74fa8eb35
commit 459ed3fea3
12 changed files with 373 additions and 387 deletions

View File

@ -50,5 +50,4 @@ else:
import sapi
import query
sapi.CgiServer()
query.main('viewcvs.cgi')
query.main(sapi.CgiServer(), 'viewcvs.cgi')

View File

@ -55,5 +55,4 @@ else:
import sapi
import viewcvs
sapi.CgiServer()
viewcvs.main()
viewcvs.main(sapi.CgiServer())

View File

@ -70,25 +70,19 @@ class ViewCVSException:
ViewcvsException = ViewCVSException
def PrintStackTrace(text=''):
import traceback, string, sapi
def PrintStackTrace(server, text=''):
import traceback, string
out = sys.stdout
server = sapi.server.self()
server.write("<hr><p><font color=red>%s</font></p>\n<p><pre>" % text)
server.write(server.escape(string.join(traceback.format_stack(), '')))
server.write("</pre></p>")
server.flush()
out.write("<hr><p><font color=red>%s</font></p>\n<p><pre>" % text)
out.write(server.escape(string.join(traceback.format_stack(), '')))
out.write("</pre></p>")
out.flush()
def PrintException():
def PrintException(server):
# capture the exception before doing anything else
exc_type, exc, exc_tb = sys.exc_info()
try:
import traceback, string, sapi
out = sys.stdout
server = sapi.server.self()
import traceback, string
if issubclass(exc_type, ViewCVSException):
server.header(status=exc.status)
@ -97,12 +91,12 @@ def PrintException():
server.header()
desc = ''
out.write("<h3>An Exception Has Occurred</h3>\n")
server.write("<h3>An Exception Has Occurred</h3>\n")
# put message in a prominent position (rather than at the end of the
# stack trace)
if desc:
out.write(desc)
server.write(desc)
stacktrace = string.join(traceback.format_exception(exc_type,
exc,
@ -116,9 +110,9 @@ def PrintException():
# This is all based on 'exc_tb', and we're now done with it. Toss it.
del exc_tb
out.write("<h4>Python Traceback</h4>\n<p><pre>")
out.write(server.escape(stacktrace))
out.write("</pre></p>\n")
server.write("<h4>Python Traceback</h4>\n<p><pre>")
server.write(server.escape(stacktrace))
server.write("</pre></p>\n")
if SHOW_CHILD_PROCESSES:
@ -136,11 +130,8 @@ if SHOW_CHILD_PROCESSES:
else:
sapi.server.pageGlobals['processes'].append(self)
def DumpChildren():
import sapi, os
out = sys.stdout
server = sapi.server.self()
def DumpChildren(server):
import os
if not server.pageGlobals.has_key('processes'):
return
@ -151,54 +142,54 @@ if SHOW_CHILD_PROCESSES:
for k in server.pageGlobals['processes']:
i = i + 1
out.write("<table border=1>\n")
out.write("<tr><td colspan=2>Child Process%i</td></tr>" % i)
out.write("<tr>\n <td valign=top>Command Line</td> <td><pre>")
out.write(server.escape(k.command))
out.write("</pre></td>\n</tr>\n")
out.write("<tr>\n <td valign=top>Standard In:</td> <td>")
server.write("<table border=1>\n")
server.write("<tr><td colspan=2>Child Process%i</td></tr>" % i)
server.write("<tr>\n <td valign=top>Command Line</td> <td><pre>")
server.write(server.escape(k.command))
server.write("</pre></td>\n</tr>\n")
server.write("<tr>\n <td valign=top>Standard In:</td> <td>")
if k.debugIn is lastOut and not lastOut is None:
out.write("<i>Output from process %i</i>" % (i - 1))
server.write("<i>Output from process %i</i>" % (i - 1))
elif k.debugIn:
out.write("<pre>")
out.write(server.escape(k.debugIn.getvalue()))
out.write("</pre>")
server.write("<pre>")
server.write(server.escape(k.debugIn.getvalue()))
server.write("</pre>")
out.write("</td>\n</tr>\n")
server.write("</td>\n</tr>\n")
if k.debugOut is k.debugErr:
out.write("<tr>\n <td valign=top>Standard Out & Error:</td> <td><pre>")
server.write("<tr>\n <td valign=top>Standard Out & Error:</td> <td><pre>")
if k.debugOut:
out.write(server.escape(k.debugOut.getvalue()))
out.write("</pre></td>\n</tr>\n")
server.write(server.escape(k.debugOut.getvalue()))
server.write("</pre></td>\n</tr>\n")
else:
out.write("<tr>\n <td valign=top>Standard Out:</td> <td><pre>")
server.write("<tr>\n <td valign=top>Standard Out:</td> <td><pre>")
if k.debugOut:
out.write(server.escape(k.debugOut.getvalue()))
out.write("</pre></td>\n</tr>\n")
out.write("<tr>\n <td valign=top>Standard Error:</td> <td><pre>")
server.write(server.escape(k.debugOut.getvalue()))
server.write("</pre></td>\n</tr>\n")
server.write("<tr>\n <td valign=top>Standard Error:</td> <td><pre>")
if k.debugErr:
out.write(server.escape(k.debugErr.getvalue()))
out.write("</pre></td>\n</tr>\n")
server.write(server.escape(k.debugErr.getvalue()))
server.write("</pre></td>\n</tr>\n")
out.write("</table>\n")
out.flush()
server.write("</table>\n")
server.flush()
lastOut = k.debugOut
out.write("<table border=1>\n")
out.write("<tr><td colspan=2>Environment Variables</td></tr>")
server.write("<table border=1>\n")
server.write("<tr><td colspan=2>Environment Variables</td></tr>")
for k, v in os.environ.items():
out.write("<tr>\n <td valign=top><pre>")
out.write(server.escape(k))
out.write("</pre></td>\n <td valign=top><pre>")
out.write(server.escape(v))
out.write("</pre></td>\n</tr>")
out.write("</table>")
server.write("<tr>\n <td valign=top><pre>")
server.write(server.escape(k))
server.write("</pre></td>\n <td valign=top><pre>")
server.write(server.escape(v))
server.write("</pre></td>\n</tr>")
server.write("</table>")
else:
def DumpChildren():
def DumpChildren(server):
pass

View File

@ -162,7 +162,7 @@ def pipe_cmds(cmds):
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
nextStdIn, hStdOut = win32popen.MakeSpyPipe(None, 1, (dbgOut, sapi.server.getFile()), ehandle)
nextStdIn, hStdOut = win32popen.MakeSpyPipe(None, 1, (dbgOut, sapi.server.file()), ehandle)
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
command = win32popen.CommandLine(cmd[0], cmd[1:])
@ -194,7 +194,7 @@ def pipe_cmds(cmds):
ehandle = None
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
x, hStdOut = win32popen.MakeSpyPipe(None, 1, (sapi.server.getFile(),), ehandle)
x, hStdOut = win32popen.MakeSpyPipe(None, 1, (sapi.server.file(),), ehandle)
command = win32popen.CommandLine(cmd[0], cmd[1:])
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, None)

View File

@ -36,7 +36,6 @@ CONF_PATHNAME = None
import os
import sys
import string
import sapi
import time
import cvsdb
@ -278,7 +277,7 @@ def form_to_cvsdb_query(form_data):
return query
def build_commit(desc, files, cvsroots):
def build_commit(server, desc, files, cvsroots, viewcvs_link):
ob = _item(num_files=len(files), files=[])
if desc:
@ -311,7 +310,7 @@ def build_commit(desc, files, cvsroots):
if cvsroot_name:
flink = '<a href="%s/%s?root=%s">%s</a>' % (
server.pageGlobals['viewcvs_link'], file, cvsroot_name,
viewcvs_link, file, cvsroot_name,
file_full_path)
else:
flink = file_full_path
@ -327,7 +326,7 @@ def build_commit(desc, files, cvsroots):
return ob
def run_query(form_data):
def run_query(server, form_data, viewcvs_link):
query = form_to_cvsdb_query(form_data)
db = cvsdb.ConnectDatabaseReadOnly()
db.RunQuery(query)
@ -352,13 +351,13 @@ def run_query(form_data):
files.append(commit)
continue
commits.append(build_commit(current_desc, files, cvsroots))
commits.append(build_commit(server, current_desc, files, cvsroots, viewcvs_link))
files = [ commit ]
current_desc = desc
## add the last file group to the commit list
commits.append(build_commit(current_desc, files, cvsroots))
commits.append(build_commit(server, current_desc, files, cvsroots, viewcvs_link))
return commits
@ -367,19 +366,15 @@ def handle_config():
global cfg
cfg = viewcvs.cfg
def main(viewcvs_link):
global server
def main(server, viewcvs_link):
try:
server = sapi.server
handle_config()
server.pageGlobals['viewcvs_link'] = viewcvs_link
form = server.FieldStorage()
form_data = FormData(form)
if form_data.valid:
commits = run_query(form_data)
commits = run_query(server, form_data, viewcvs_link)
query = None
else:
commits = [ ]

View File

@ -1,245 +1,54 @@
# generic server api - currently supports normal cgi, and active server pages
#
# Russ Yanofsky -- rey4@columbia.edu
# generic server api - currently supports normal cgi, mod_python, and
# active server pages
import types
import string
import os
import sys
import re
# global server object. It will be either a CgiServer or an AspServer,
# depending on the environment
# global server object. It will be either a CgiServer or a proxy to
# an AspServer or ModPythonServer object.
server = None
class CgiServer:
class Server:
def __init__(self):
self.inheritableOut = 1
self.header_sent = 0
self.pageGlobals = {}
if os.environ.get('SERVER_SOFTWARE', '')[:13] == 'Microsoft-IIS':
self.iis = 1
else:
self.iis = 0
if sys.platform == "win32":
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
global server
server = self
global cgi
import cgi
def header(self, content_type='text/html', status='200 OK'):
if not self.header_sent:
self.header_sent = 1
sys.stdout.write('Status: %s\r\nContent-Type: %s\r\n\r\n'
% (status, content_type))
def redirect(self, url):
print 'Status: 301 Moved'
if self.iis: url = IIS_FixURL(url)
sys.stdout.write('Location: ' + url + '\r\n\r\n')
print 'This document is located <a href="%s">here</a>.' % url
sys.exit(0)
def params(self):
return cgi.parse()
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None):
# If the viewcvs cgi's are in the /viewcvs/ folder on the web server and a
# request looks like
#
# /viewcvs/viewcvs.cgi/myproject/?someoption
#
# The CGI environment variables will look like this:
#
# SCRIPT_NAME = /viewcvs/viewcvs.cgi
# PATH_INFO = /viewcvs/viewcvs.cgi/myproject/
#
# I'm not sure how these variables are supposed to work on a unix server,
# but this next statement was needed in order to get existing script
# to work properly on windows.
if self.iis and name == 'PATH_INFO':
return os.environ.get('PATH_INFO', '')[len(os.environ.get('SCRIPT_NAME', '')):]
else:
return os.environ.get(name, value)
def FieldStorage(fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(fp, headers, outerboundary, environ,
keep_blank_values, strict_parsing)
def self(self):
return self
def getFile(self):
return sys.stdout
def close():
def close(self):
pass
def IIS_FixURL(url):
"""When a CGI application under IIS outputs a "Location" header with a url
beginning with a forward slash, IIS tries to optimise the redirect by not
returning any output from the original CGI script at all and instead just
returning the new page in its place. Because of this, the browser does
not know it is getting a different page than it requested. As a result,
The address bar that appears in the browser window shows the wrong location
and if the new page is in a different folder than the old one, any relative
links on it will be broken.
This function can be used to circumvent the IIS "optimization" of local
redirects. If it is passed a location that begins with a forward slash it
will return a URL constructed with the information in CGI environment.
If it is passed a URL or any location that doens't begin with a forward slash
it will return just argument unaltered.
"""
if url[0] == '/':
if os.environ['HTTPS'] == 'on':
dport = "443"
prefix = "https://"
else:
dport = "80"
prefix = "http://"
prefix = prefix + os.environ['HTTP_HOST']
if os.environ['SERVER_PORT'] != dport:
prefix = prefix + ":" + os.environ['SERVER_PORT']
return prefix + url
return url
class ThreadedServer(Server):
def __init__(self):
Server.__init__(self)
class AspServer:
def __init__(self, Server, Request, Response, Application):
self.inheritableOut = 0
self.header_sent = 0
self.server = Server
self.request = Request
self.response = Response
self.application = Application
self.pageGlobals = {}
global server
if not isinstance(server, AspProxy):
server = AspProxy()
if not isinstance(sys.stdout, AspFile):
sys.stdout = AspFile(server)
if not isinstance(server, ThreadedServerProxy):
server = ThreadedServerProxy()
if not isinstance(sys.stdout, File):
sys.stdout = File(server)
server.registerThread(self)
def escape(self, s, quote = None):
return self.server.HTMLEncode(str(s))
def params(self):
p = {}
for i in self.request.Form:
p[str(i)] = map(str, self.request.Form[i])
for i in self.request.QueryString:
p[str(i)] = map(str, self.request.QueryString[i])
return p
def header(self, content_type='text/html', status='200 OK'):
### what to do with the status?
# In normal circumstances setting self.response.ContentType
# after headers have already been sent simply results in
# an AttributeError exception, but sometimes it leads to
# a fatal ASP error. For this reason I'm keeping
# the self.header_sent member, and only checking for the
# exception as a secondary measure
if not self.header_sent:
try:
self.header_sent = 1
self.response.ContentType = content_type
except AttributeError:
pass
def redirect(self, url):
self.response.Redirect(url)
sys.exit()
def getenv(self, name, value = None):
if name == 'PATH_INFO':
p = self.request.ServerVariables('PATH_INFO')()
s = self.request.ServerVariables('SCRIPT_NAME')()
return str(p[len(s):])
else:
r = self.request.ServerVariables(name)()
if type(r) == types.UnicodeType:
return str(r)
else:
return value
def FieldStorage(self, fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
# Code based on a very helpful usenet post by "Max M" (maxm@mxm.dk)
# Subject "Re: Help! IIS and Python"
# http://groups.google.com/groups?selm=3C7C0AB6.2090307%40mxm.dk
from StringIO import StringIO
from cgi import FieldStorage
environ = {}
for i in self.request.ServerVariables:
environ[str(i)] = str(self.request.ServerVariables(i)())
# this would be bad for uploaded files, could use a lot of memory
binaryContent, size = self.request.BinaryRead(int(environ['CONTENT_LENGTH']))
fp = StringIO(str(binaryContent))
fs = FieldStorage(fp, None, "", environ, keep_blank_values, strict_parsing)
fp.close()
return fs
def getFile(self):
return AspFile(self)
def file(self):
return File(self)
def close(self):
server.unregisterThread()
class AspFile:
def __init__(self, server):
self.closed = 0
self.mode = 'w'
self.name = "<AspFile file>"
self.softspace = 0
self.server = server
def flush(self):
self.server.response.Flush()
def write(self, s):
t = type(s)
if t is types.StringType:
s = buffer(s)
elif not t is types.BufferType:
s = buffer(str(s))
self.server.response.BinaryWrite(s)
def writelines(self, list):
for str in list:
self.server.response.BinaryWrite(str)
def truncate(self, size):
pass
def close(self):
pass
class AspProxy:
"""In a multithreaded server environment, AspProxy stores the different server
objects being used to display pages and transparently forwards access to them
based on the current thread id."""
class ThreadedServerProxy:
"""In a multithreaded server environment, ThreadedServerProxy stores the
different server objects being used to display pages and transparently
forwards access to them based on the current thread id."""
def __init__(self):
self.__dict__['servers'] = { }
@ -266,36 +75,194 @@ class AspProxy:
def __delattr__(self, key):
delattr(self.self(), key)
class ModPythonServer:
def __init__(self, request):
self.inheritableOut = 0
self.request = request
self.pageGlobals = {}
class File:
def __init__(self, server):
self.closed = 0
self.mode = 'w'
self.name = "<AspFile file>"
self.softspace = 0
self.server = server
def write(self, s):
self.server.write(s)
def writelines(self, list):
for s in list:
self.server.write(s)
def flush(self):
self.server.flush()
def truncate(self, size):
pass
def close(self):
pass
class CgiServer(Server):
def __init__(self, inheritableOut = 1):
Server.__init__(self)
self.headerSent = 0
self.inheritableOut = inheritableOut
self.iis = os.environ.get('SERVER_SOFTWARE', '')[:13] == 'Microsoft-IIS'
if sys.platform == "win32" and inheritableOut:
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
global server
if not isinstance(server, AspProxy):
server = AspProxy()
if not isinstance(sys.stdout, ModPythonFile):
sys.stdout = ModPythonFile(server)
server.registerThread(self)
server = self
global cgi
import cgi
def header(self, content_type='text/html', status='200 OK'):
if not self.headerSent:
self.headerSent = 1
# The only way ViewCVS pages and error messages will be visible
# under IIS is if a 200 error code is returned. Otherwise it
# will send the static error page corresponding to the code number
if self.iis:
status = '200 OK'
sys.stdout.write('Status: %s\r\nContent-Type: %s\r\n\r\n'
% (status, content_type))
def redirect(self, url):
print 'Status: 301 Moved'
if self.iis: url = fix_iis_url(self, url)
sys.stdout.write('Location: ' + url + '\r\n\r\n')
print 'This document is located <a href="%s">here</a>.' % url
sys.exit(0)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None):
ret = os.environ.get(name, value)
if self.iis and name == 'PATH_INFO' and ret:
ret = fix_iis_path_info(self, ret)
return ret
def params(self):
import mod_python.util
if self.request.args is None:
return {}
else:
return mod_python.util.parse_qs(self.request.args)
return cgi.parse()
def FieldStorage(fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(fp, headers, outerboundary, environ,
keep_blank_values, strict_parsing)
def write(self, s):
sys.stdout.write(s)
def flush(self):
sys.stdout.flush()
def file(self):
return sys.stdout
class AspServer(ThreadedServer):
def __init__(self, Server, Request, Response, Application):
ThreadedServer.__init__(self)
self.headerSent = 0
self.server = Server
self.request = Request
self.response = Response
self.application = Application
def header(self, content_type='text/html', status='200 OK'):
# Normally, setting self.response.ContentType after headers have already
# been sent simply results in an AttributeError exception, but sometimes
# it leads to a fatal ASP error. For this reason I'm keeping the
# self.headerSent member and only checking for the exception as a
# secondary measure
if not self.headerSent:
try:
self.headerSent = 1
self.response.ContentType = content_type
self.response.Status = status
except AttributeError:
pass
def redirect(self, url):
self.response.Redirect(url)
sys.exit()
def escape(self, s, quote = None):
return self.server.HTMLEncode(str(s))
def getenv(self, name, value = None):
ret = self.request.ServerVariables(name)()
if not type(ret) is types.UnicodeType:
return value
ret = str(ret)
if name == 'PATH_INFO':
ret = fix_iis_path_info(self, ret)
return ret
def params(self):
p = {}
for i in self.request.Form:
p[str(i)] = map(str, self.request.Form[i])
for i in self.request.QueryString:
p[str(i)] = map(str, self.request.QueryString[i])
return p
def FieldStorage(self, fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
# Code based on a very helpful usenet post by "Max M" (maxm@mxm.dk)
# Subject "Re: Help! IIS and Python"
# http://groups.google.com/groups?selm=3C7C0AB6.2090307%40mxm.dk
from StringIO import StringIO
from cgi import FieldStorage
environ = {}
for i in self.request.ServerVariables:
environ[str(i)] = str(self.request.ServerVariables(i)())
# this would be bad for uploaded files, could use a lot of memory
binaryContent, size = self.request.BinaryRead(int(environ['CONTENT_LENGTH']))
fp = StringIO(str(binaryContent))
fs = FieldStorage(fp, None, "", environ, keep_blank_values, strict_parsing)
fp.close()
return fs
def write(self, s):
t = type(s)
if t is types.StringType:
s = buffer(s)
elif not t is types.BufferType:
s = buffer(str(s))
self.response.BinaryWrite(s)
def flush(self):
self.response.Flush()
_re_status = re.compile("\\d+")
class ModPythonServer(ThreadedServer):
def __init__(self, request):
ThreadedServer.__init__(self)
self.request = request
global cgi
import cgi
def header(self, content_type='text/html', status='200 OK'):
### what to do with the status?
self.request.content_type = content_type
m = _re_status.match(status)
if not m is None:
self.request.status = int(m.group())
def redirect(self, url):
import mod_python.apache
@ -305,43 +272,81 @@ class ModPythonServer:
% (url, url))
sys.exit()
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value = None):
try:
return self.request.subprocess_env[name]
except KeyError:
return value
def params(self):
import mod_python.util
if self.request.args is None:
return {}
else:
return mod_python.util.parse_qs(self.request.args)
def FieldStorage(self, fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
import mod_python.util
return mod_python.util.FieldStorage(self.request, keep_blank_values, strict_parsing)
def getFile(self):
return ModPythonFile(self)
def close(self, code = 0):
server.unregisterThread()
class ModPythonFile:
def __init__(self, server):
import StringIO
self.closed = 0
self.mode = 'w'
self.name = "<ModPythonFile file>"
self.softspace = 0
self.server = server
def write(self, s):
self.request.write(s)
def flush(self):
pass
def write(self, s):
self.server.request.write(s)
def writelines(self, ls):
raise "not implemented"
def fix_iis_url(server, url):
"""When a CGI application under IIS outputs a "Location" header with a url
beginning with a forward slash, IIS tries to optimise the redirect by not
returning any output from the original CGI script at all and instead just
returning the new page in its place. Because of this, the browser does
not know it is getting a different page than it requested. As a result,
The address bar that appears in the browser window shows the wrong location
and if the new page is in a different folder than the old one, any relative
links on it will be broken.
def truncate(self, size):
pass
This function can be used to circumvent the IIS "optimization" of local
redirects. If it is passed a location that begins with a forward slash it
will return a URL constructed with the information in CGI environment.
If it is passed a URL or any location that doens't begin with a forward slash
it will return just argument unaltered.
"""
if url[0] == '/':
if server.getenv('HTTPS') == 'on':
dport = "443"
prefix = "https://"
else:
dport = "80"
prefix = "http://"
prefix = prefix + server.getenv('HTTP_HOST')
if server.getenv('SERVER_PORT') != dport:
prefix = prefix + ":" + server.getenv('SERVER_PORT')
return prefix + url
return url
def close(self):
pass
def fix_iis_path_info(server, path_info):
"""Fix the PATH_INFO value in IIS"""
# If the viewcvs cgi's are in the /viewcvs/ folder on the web server and a
# request looks like
#
# /viewcvs/viewcvs.cgi/myproject/?someoption
#
# The CGI environment variables on IIS will look like this:
#
# SCRIPT_NAME = /viewcvs/viewcvs.cgi
# PATH_INFO = /viewcvs/viewcvs.cgi/myproject/
#
# Whereas on Apache they look like:
#
# SCRIPT_NAME = /viewcvs/viewcvs.cgi
# PATH_INFO = /myproject/
#
# This function converts the IIS PATH_INFO into the nonredundant form
# expected by ViewCVS
return path_info[len(server.getenv('SCRIPT_NAME', '')):]

View File

@ -48,6 +48,7 @@ debug.t_start('imports')
import sys
import os
import sapi
import cgi
import string
import urllib
import mimetypes
@ -113,11 +114,13 @@ else:
class Request:
def __init__(self):
def __init__(self, server):
# global needed because "import vclib.svn" causes the
# interpreter to make vclib a local variable
global vclib
self.server = server
where = server.getenv('PATH_INFO', '')
# clean it up. this removes duplicate '/' characters and any that may
@ -299,14 +302,14 @@ def _validate_param(name, value):
validator = _legal_params[name]
except KeyError:
raise debug.ViewcvsException(
'An illegal parameter name ("%s") was passed.' % server.escape(name))
'An illegal parameter name ("%s") was passed.' % cgi.escape(name))
# is the validator a regex?
if hasattr(validator, 'match'):
if not validator.match(value):
raise debug.ViewcvsException(
'An illegal value ("%s") was passed as a parameter.' %
server.escape(value))
cgi.escape(value))
return
# the validator must be a function
@ -318,7 +321,7 @@ def _validate_root(value):
raise debug.ViewcvsException(
'The root "%s" is unknown. If you believe the value is '
'correct, then please double-check your configuration.'
% server.escape(value), "404 Repository not found")
% cgi.escape(value), "404 Repository not found")
def _validate_regex(value):
# hmm. there isn't anything that we can do here.
@ -465,7 +468,7 @@ def is_text(mime_type):
_re_rewrite_url = re.compile('((http|ftp)(://[-a-zA-Z0-9%.~:_/]+)([?&]([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*(#([-a-zA-Z0-9%.~:_]+)?)?)')
_re_rewrite_email = re.compile('([-a-zA-Z0-9_.]+@([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})')
def htmlify(html):
html = server.escape(html)
html = cgi.escape(html)
html = re.sub(_re_rewrite_url, r'<a href="\1">\1</a>', html)
html = re.sub(_re_rewrite_email, r'<a href="mailto:\1">\1</a>', html)
return html
@ -595,7 +598,7 @@ def markup_stream_python(fp):
else:
### it doesn't escape stuff quite right, nor does it munge URLs and
### mailtos as well as we do.
html = server.escape(fp.read())
html = cgi.escape(fp.read())
pp = py2html.PrettyPrint(PyFontify.fontify, "rawhtml", "color")
html = pp.fontify(html)
html = re.sub(_re_rewrite_url, r'<a href="\1">\1</a>', html)
@ -835,7 +838,7 @@ def markup_stream(request, fp, revision, mime_type):
else:
data['tag'] = query_dict.get('only_with_tag')
server.header()
request.server.header()
generate_page(request, cfg.templates.markup, data)
if mime_type[:6] == 'image/':
@ -1010,7 +1013,7 @@ def prepare_hidden_values(request, var_list, vars_to_omit_list):
value = query_dict.get(varname, '')
if value != '' and value != default_settings.get(varname):
hidden_values.append('<input type="hidden" name="%s" value="%s" />' %
(varname, server.escape(value)))
(varname, request.server.escape(value)))
return string.join(hidden_values, '')
def sort_file_data(file_data, sortdir, sortby, fileinfo, roottype):
@ -1388,7 +1391,7 @@ def view_directory_cvs(request):
data['dir_pagestart'] = int(query_dict.get('dir_pagestart',0))
data['rows'] = paging(data, 'rows', data['dir_pagestart'], 'name')
server.header()
request.server.header()
generate_page(request, cfg.templates.directory, data)
def view_directory_svn(request):
@ -1515,7 +1518,7 @@ def view_directory_svn(request):
# the number actually displayed
data['files_shown'] = num_displayed
server.header()
request.server.header()
generate_page(request, cfg.templates.directory, data)
def paging(data, key, pagestart, local_name):
@ -1695,11 +1698,10 @@ def read_log(full_name, which_rev=None, view_tag=None, logsort='cvs'):
_re_is_vendor_branch = re.compile(r'^1\.1\.1\.\d+$')
def augment_entry(entry, request, file_url, rev_map, rev2tag, branch_points,
rev_order, extended):
rev_order, extended, name_printed):
"Augment the entry with additional, computed data from the log output."
query_dict = request.query_dict
g_name_printed = server.pageGlobals['g_name_printed']
rev = entry.rev
idx = string.rfind(rev, '.')
@ -1732,9 +1734,9 @@ def augment_entry(entry, request, file_url, rev_map, rev2tag, branch_points,
if extended:
entry.tag_names = rev2tag.get(rev, [ ])
if rev2tag.has_key(branch) and not g_name_printed.has_key(branch):
if rev2tag.has_key(branch) and not name_printed.has_key(branch):
entry.branch_names = rev2tag.get(branch)
g_name_printed[branch] = 1
name_printed[branch] = 1
else:
entry.branch_names = [ ]
@ -1907,7 +1909,7 @@ def view_log_svn(request):
data['log_pagestart'] = int(query_dict.get('log_pagestart',0))
data['entries'] = paging(data, 'entries', data['log_pagestart'], 'rev')
server.header()
request.server.header()
generate_page(request, cfg.templates.log, data)
def view_log_cvs(request):
@ -2000,11 +2002,11 @@ def view_log_cvs(request):
else:
data['head_href'] = download_url(request, file_url, 'HEAD', None)
server.pageGlobals['g_name_printed'] = { } ### gawd, what a hack...
name_printed = { }
for entry in show_revs:
# augment the entry with (extended=1) info.
augment_entry(entry, request, file_url, rev_map, rev2tag, branch_points,
rev_order, 1)
rev_order, 1, name_printed)
tagitems = taginfo.items()
tagitems.sort()
@ -2061,7 +2063,7 @@ def view_log_cvs(request):
data['log_pagestart'] = int(query_dict.get('log_pagestart',0))
data['entries'] = paging(data, 'entries', data['log_pagestart'], 'rev')
server.header()
request.server.header()
generate_page(request, cfg.templates.log, data)
### suck up other warnings in _re_co_warning?
@ -2167,7 +2169,7 @@ def view_checkout(request):
# use the "real" MIME type
markup_stream(request, fp, revision, request.mime_type)
else:
server.header(mime_type)
request.server.header(mime_type)
copy_stream(fp)
def view_annotate(request):
@ -2184,7 +2186,7 @@ def view_annotate(request):
'kv' : request.kv,
})
server.header()
request.server.header()
generate_page(request, cfg.templates.annotate, data)
### be nice to hook this into the template...
@ -2198,7 +2200,7 @@ def view_annotate(request):
def cvsgraph_image(cfg, request):
"output the image rendered by cvsgraph"
# this function is derived from cgi/cvsgraphmkimg.cgi
server.header('image/png')
request.server.header('image/png')
fp = popen.popen(os.path.normpath(os.path.join(cfg.options.cvsgraph_path,'cvsgraph')),
("-c", cfg.options.cvsgraph_conf,
"-r", request.repos.rootpath,
@ -2239,7 +2241,7 @@ def view_cvsgraph(cfg, request):
'kv' : request.kv,
})
server.header()
request.server.header()
generate_page(request, cfg.templates.graph, data)
def search_files(request, search_re):
@ -2334,13 +2336,13 @@ def view_doc(request):
raise debug.ViewcvsException('help file "%s" not available\n(%s)'
% (help_page, str(v)), '404 Not Found')
if help_page[-3:] == 'png':
server.header('image/png')
request.server.header('image/png')
elif help_page[-3:] == 'jpg':
server.header('image/jpeg')
request.server.header('image/jpeg')
elif help_page[-3:] == 'gif':
server.header('image/gif')
request.server.header('image/gif')
else: # assume HTML:
server.header()
request.server.header()
copy_stream(fp)
fp.close()
@ -2349,7 +2351,7 @@ _re_extract_rev = re.compile(r'^[-+]+ [^\t]+\t([^\t]+)\t((\d+\.)*\d+)$')
_re_extract_info = re.compile(r'@@ \-([0-9]+).*\+([0-9]+).*@@(.*)')
def human_readable_diff(request, fp, rev1, rev2, sym1, sym2):
# do this now, in case we need to print an error
server.header()
request.server.header()
query_dict = request.query_dict
@ -2373,12 +2375,12 @@ def human_readable_diff(request, fp, rev1, rev2, sym1, sym2):
if line[:4] == '--- ':
match = _re_extract_rev.match(line)
if match:
date1 = ', ' + match.group(1)
date1 = match.group(1)
log_rev1 = match.group(2)
elif line[:4] == '+++ ':
match = _re_extract_rev.match(line)
if match:
date2 = ', ' + match.group(1)
date2 = match.group(1)
log_rev2 = match.group(2)
break
@ -2421,11 +2423,11 @@ def human_readable_diff(request, fp, rev1, rev2, sym1, sym2):
# Convert to local time if option is set, otherwise remains UTC
if (cfg.options.use_localtime):
def time_format(date):
date = compat.cvs_strptime(date[-19:])
date = compat.cvs_strptime(date)
date = compat.timegm(date)
localtime = time.localtime(date)
date = time.strftime('%Y/%m/%d %H:%M:%S', localtime)
return ', ' + date + ' ' + time.tzname[localtime[8]]
return date + ' ' + time.tzname[localtime[8]]
date1 = time_format(date1)
date2 = time_format(date2)
else:
@ -2442,8 +2444,8 @@ def human_readable_diff(request, fp, rev1, rev2, sym1, sym2):
'rev2' : rev2,
'tag1' : sym1,
'tag2' : sym2,
'date1' : date1,
'date2' : date2,
'date1' : ', ' + date1,
'date2' : ', ' + date2,
'changes' : rcs_diff,
'diff_format' : query_dict['diff_format'],
'hidden_values' : hidden_values,
@ -2706,7 +2708,7 @@ def view_diff(request):
human_readable_diff(request, fp, rev1, rev2, sym1, sym2)
return
server.header('text/plain')
request.server.header('text/plain')
rootpath = request.repos.rootpath
if unified:
@ -2856,7 +2858,7 @@ def download_tarball(request):
### look for GZIP binary
server.header('application/octet-stream')
request.server.header('application/octet-stream')
sys.stdout.flush()
fp = popen.pipe_cmds([('gzip', '-c', '-n')])
generate_tarball(fp, request, tar_top, rep_top, [], tag)
@ -2919,7 +2921,7 @@ def run_viewcvs(server):
handle_config()
# build a Request object, which contains info about the HTTP request
request = Request()
request = Request(server)
# most of the startup is done now.
debug.t_end('startup')
@ -3027,30 +3029,21 @@ def run_viewcvs(server):
% request.url, '404 Not Found')
def main():
### this is a bad hack. various functions expect a runtime global
### named 'server' which corresponds to how we generate output.
### bleck. the right answer is to make this part of the Request object
### and ensure that every function is passed the Request instance.
### this would also allow us to toss the AspProxy and its per-thread
### nonsense.
global server
server = sapi.server
def main(server):
try:
debug.t_start('main')
try:
run_viewcvs(sapi.server)
run_viewcvs(server)
except SystemExit, e:
return
except:
debug.PrintException()
debug.PrintException(server)
html_footer(None)
finally:
debug.t_end('main')
debug.dump()
debug.DumpChildren()
debug.DumpChildren(server)
class _item:

View File

@ -71,16 +71,23 @@ class Options:
class StandaloneServer(sapi.CgiServer):
def __init__(self, handler):
sapi.CgiServer.__init__(self)
sapi.CgiServer.__init__(self, inheritableOut = sys.platform != "win32")
self.handler = handler
self.sent = 0
self.inheritableOut = sys.platform != "win32"
def header(self, content_type='text/html'):
if not self.sent:
def header(self, content_type='text/html', status = '200 OK'):
if not self.headerSent:
self.headerSent = 1
p = string.find(status, ' ')
if p < 0:
statusCode = status
statusText = ''
else:
statusCode = status[:p]
statusText = status[p+1:]
self.handler.send_response(statusCode, statusText)
self.handler.send_header("Content-type", content_type)
self.handler.end_headers()
self.sent = 1
def serve(host, port, callback=None):
"""start a HTTP server on the given port. call 'callback' when the
@ -93,7 +100,6 @@ def serve(host, port, callback=None):
if not self.path or self.path == "/":
self.redirect()
elif self.is_viewcvs():
StandaloneServer(self)
try:
self.run_viewcvs()
except IOError:
@ -203,8 +209,6 @@ If this doesn't work, please click on the link above.
# XXX Other HTTP_* headers
decoded_query = string.replace(query, '+', ' ')
self.send_response(200)
# Preserve state, because we execute script in current process:
save_argv = sys.argv
save_stdin = sys.stdin
@ -222,7 +226,7 @@ If this doesn't work, please click on the link above.
os.close(1)
assert os.dup(self.wfile.fileno()) == 1
sys.stdin = self.rfile
viewcvs.main()
viewcvs.main(StandaloneServer(self))
finally:
sys.argv = save_argv
sys.stdin = save_stdin

View File

@ -55,7 +55,7 @@ import query
s = sapi.AspServer(Server, Request, Response, Application)
try:
query.main('viewcvs.asp')
query.main(s, 'viewcvs.asp')
finally:
s.close()

View File

@ -3,6 +3,6 @@ import query
s = sapi.ModPythonServer(Request)
try:
query.main('viewcvs.py')
query.main(s, 'viewcvs.py')
finally:
s.close()

View File

@ -60,7 +60,7 @@ import viewcvs
s = sapi.AspServer(Server, Request, Response, Application)
try:
viewcvs.main()
viewcvs.main(s)
finally:
s.close()

View File

@ -3,6 +3,6 @@ import viewcvs
s = sapi.ModPythonServer(Request)
try:
viewcvs.main()
viewcvs.main(s)
finally:
s.close()