cpsa-2.0.2: src/cpsacgi.py
#! /usr/bin/python
"""Warning: this CGI script is for deployment in friendly
environments only!"""
import cgi
import sys
import subprocess
import cgitb; cgitb.enable()
# The command used to start cpsa limits memory usage
cpsa_command = ['./cpsa', '+RTS', '-M512m', '-RTS']
# Flag for using ps to determine if a copy of cpsa is already running
use_in_use_check = False
def main():
shapes_cmd = './cpsashapes'
graph_cmd = './cpsagraph -c'
annotations_cmd = './cpsaannotations'
xml_cmd = './cpsagraph -x'
shapes_graph_cmd = shapes_cmd + ' | ' + graph_cmd
shapes_xml_cmd = shapes_cmd + ' | ' + xml_cmd
shapes_annotations_cmd = shapes_cmd + ' | ' + annotations_cmd
shapes_annotations_xml_cmd = shapes_annotations_cmd + ' | ' + xml_cmd
form = cgi.FieldStorage()
if form.has_key('xml'):
resp = xml(xml_cmd, form['xml'], None)
elif form.has_key('shapes xml'):
resp = xml(shapes_xml_cmd, form['shapes xml'], None)
elif form.has_key('shapes annotations xml'):
resp = xml(shapes_annotations_xml_cmd,
form['shapes annotations xml'], None)
elif form.has_key('graph'):
resp = graph(graph_cmd, form['graph'], None)
elif form.has_key('shapes graph'):
resp = graph(shapes_graph_cmd, form['shapes graph'], None)
elif form.has_key('cpsa'):
resp = cpsa(None, form['cpsa'])
elif form.has_key('cpsa shapes'):
resp = cpsa(shapes_cmd, form['cpsa shapes'])
elif form.has_key('cpsa shapes annotations'):
resp = cpsa(shapes_annotations_cmd,
form['cpsa shapes annotations'])
elif form.has_key('cpsa xml'):
resp = cpsa_xml(xml_cmd, form['cpsa xml'])
elif form.has_key('cpsa shapes xml'):
resp = cpsa_xml(shapes_xml_cmd, form['cpsa shapes xml'])
elif form.has_key('cpsa shapes annotations xml'):
resp = cpsa_xml(shapes_annotations_xml_cmd,
form['cpsa shapes annotations xml'])
elif form.has_key('cpsa graph'):
resp = cpsa_graph(graph_cmd, form['cpsa graph'])
elif form.has_key('cpsa shapes graph'):
resp = cpsa_graph(shapes_graph_cmd, form['cpsa shapes graph'])
else:
resp = 'Content-Type: text/plain\n\nBad form input\n'
sys.stdout.write(resp)
def graph(command, data, source):
if not source and not data.file:
return 'Content-Type: text/plain\n\nBad form input--no file\n'
p = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if source:
p.stdin.write(source)
else:
p.stdin.write(data.value)
p.stdin.close()
out = bounded_read(p.stdout)
err = p.stderr.read()
if p.wait():
resp = 'Content-Type: text/plain\n\n' + err
elif not out:
resp = 'Content-Type: text/plain\n\nSize limit exceeded'
else:
resp = 'Content-Type: image/svg+xml; charset=UTF-8\n'
if source and data.filename: # Add content disposition
name = data.filename
i = name.rfind('.')
if i > 0:
name = name[0:i]
fmt = 'Content-Disposition: attachment; filename=%s.svg\n\n'
resp += fmt % name
else:
resp += '\n'
resp += out
return resp
def xml(command, data, source):
if not source and not data.file:
return 'Content-Type: text/plain\n\nBad form input--no file\n'
p = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if source:
p.stdin.write(source)
else:
p.stdin.write(data.value)
p.stdin.close()
out = bounded_read(p.stdout)
err = p.stderr.read()
if p.wait():
resp = 'Content-Type: text/plain\n\n' + err
elif not out:
resp = 'Content-Type: text/plain\n\nSize limit exceeded'
else:
resp = 'Content-Type: text/xml; charset=UTF-8\n\n' + out
return resp
def cpsa(command, data):
global cpsa_command
resp = 'Content-Type: text/plain; charset=US-ASCII\n\n'
if not data.file:
resp += 'Bad form input--no file\n'
return resp
busy = in_use()
if busy:
return busy
p = subprocess.Popen(cpsa_command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(data.value)
p.stdin.close()
out = bounded_read(p.stdout)
err = p.stderr.read()
if p.wait():
return resp + err
elif not out:
return resp + 'Size limit exceeded'
elif not command:
return resp + out
p = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(out)
p.stdin.close()
out = bounded_read(p.stdout)
err = p.stderr.read()
if p.wait():
return resp + err
elif not out:
return resp + 'Size limit exceeded'
else:
return resp + out
def cpsa_graph(command, data):
global cpsa_command
if not data.file:
resp = 'Content-Type: text/plain; charset=US-ASCII\n\n'
resp += 'Bad form input--no file\n'
return resp
busy = in_use()
if busy:
return busy
p = subprocess.Popen(cpsa_command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(data.value)
p.stdin.close()
out = bounded_read(p.stdout)
err = p.stderr.read()
if p.wait():
resp = 'Content-Type: text/plain; charset=US-ASCII\n\n' + err
return resp
if not out:
resp = 'Content-Type: text/plain\n\nSize limit exceeded'
return resp
resp = 'Content-Type: multipart/mixed; boundary="boundary"'
resp += '\n\n--boundary\n'
resp += graph(command, data, out)
resp += '\n--boundary\n'
resp += 'Content-Type: text/plain; charset=US-ASCII\n\n'
resp += out
resp += '\n--boundary--\n'
return resp
def cpsa_xml(command, data):
global cpsa_command
if not data.file:
resp = 'Content-Type: text/plain; charset=US-ASCII\n\n'
resp += 'Bad form input--no file\n'
return resp
busy = in_use()
if busy:
return busy
p = subprocess.Popen(cpsa_command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(data.value)
p.stdin.close()
out = bounded_read(p.stdout)
err = p.stderr.read()
if p.wait():
resp = 'Content-Type: text/plain; charset=US-ASCII\n\n' + err
return resp
if not out:
resp = 'Content-Type: text/plain\n\nSize limit exceeded'
return resp
return xml(command, data, out)
# Use ps to ensure at most one copy of cpsa is running.
def in_use():
global use_in_use_check
if not use_in_use_check:
return False
p = subprocess.Popen(['ps', '-ux'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.close()
out = p.stdout.read()
err = p.stderr.read()
if p.wait():
resp = 'Content-Type: text/plain; charset=US-ASCII\n\n' + err
return resp
if out.find("cpsa") >= 0:
return 'Content-Type: text/plain\n\nServer busy'
return False
READ_BOUND = 2 << 20
# Return the empty string when the input is too big.
def bounded_read(f):
buf = f.read(READ_BOUND)
if f.read(1):
return ""
return buf
main()