python: Ignore EPIPE in hredir and serve-ssi.
[ashd.git] / python / serve-ssi
CommitLineData
6d94a5f6
FT
1#!/usr/bin/python
2
3# This program is quite incomplete. I might complete it with more
4# features as I need them. It will probably never be entirely
5# compliant with Apache's version due to architectural differences.
6
7import sys, os, time
8
9def htmlquote(text):
10 ret = ""
11 for c in text:
12 if c == '&':
13 ret += "&"
14 elif c == '<':
15 ret += "&lt;"
16 elif c == '>':
17 ret += "&gt;"
18 elif c == '"':
19 ret += "&quot;"
20 else:
21 ret += c
22 return ret
23
24def simpleerror(out, code, title, msg):
25 html = """<?xml version="1.0" encoding="US-ASCII"?>
26<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
27<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
28<head>
29<title>%s</title>
30</head>
31<body>
32<h1>%s</h1>
33<p>%s</p>
34</body>
35</html>
36""" % (title, title, htmlquote(msg))
37 out.write("HTTP/1.1 %d %s\n" % (code, title))
38 out.write("Content-Type: text/html\n")
39 out.write("Content-Length: %d\n" % len(html))
40 out.write("\n")
41 out.write(html)
42
43ssivars = {}
44
45def parsecmd(line, p):
46 try:
47 while line[p].isspace(): p += 1
48 cmd = ""
49 while not line[p].isspace():
50 cmd += line[p]
51 p += 1
52 pars = {}
53 while True:
54 while line[p].isspace(): p += 1
55 if line[p:p + 3] == "-->":
56 return cmd, pars, p + 3
57 key = ""
58 while line[p].isalnum():
59 key += line[p]
60 p += 1
61 if key == "":
62 return None, {}, p
63 while line[p].isspace(): p += 1
64 if line[p] != '=':
65 continue
66 p += 1
67 while line[p].isspace(): p += 1
68 q = line[p]
69 if q != '"' and q != "'" and q != '`':
70 continue
71 val = ""
72 p += 1
73 while line[p] != q:
74 val += line[p]
75 p += 1
76 p += 1
77 pars[key] = val
78 except IndexError:
79 return None, {}, len(line)
80
81class ssifile(object):
82 def __init__(self, s, url, path):
83 self.s = s
84 self.url = url
85 self.path = path
86
87 def close(self):
88 self.s.close();
89
90 def initvars(self, vars):
91 now = time.time()
92 vars["DOCUMENT_NAME"] = os.path.basename(self.path)
93 vars["DATE_GMT"] = time.asctime(time.gmtime(now))
94 vars["DATE_LOCAL"] = time.asctime(time.localtime(now))
95 vars["LAST_MODIFIED"] = time.asctime(time.localtime(os.stat(self.path).st_mtime))
96
97 def includefile(self, path):
98 path = os.path.join(os.path.dirname(self.path), path)
99 try:
100 f = ssifile(open(path), url, path)
101 except Exception:
102 sys.stderr.write("serve-ssi: included file not found: %s\n" % path)
103 return
104 try:
105 f.process()
106 finally:
107 f.close
108
109 def docmd(self, cmd, pars):
110 if cmd == "include":
111 if "file" in pars:
112 self.includefile(pars["file"])
113 elif "virtual" in pars:
114 # XXX: For now, just include the file as-is. Change
115 # when necessary.
116 self.includefile(pars["virtual"])
117 elif cmd == "echo":
118 enc = htmlquote
119 if "encoding" in pars:
120 if pars["encoding"] == "entity":
121 enc = htmlquote
122 if "var" in pars:
123 if pars["var"] in ssivars:
124 sys.stdout.write(enc(ssivars[pars["var"]]))
125 else:
126 sys.stderr.write("serve-ssi: unknown SSI command: %s\n" % cmd)
127
128 def process(self):
129 for line in self.s:
130 p = 0
131 while True:
132 p2 = line.find("<!--#", p)
133 if p2 < 0:
134 sys.stdout.write(line[p:])
135 break
136 sys.stdout.write(line[p:p2])
137 cmd, pars, p = parsecmd(line, p2 + 5)
138 if cmd is not None:
139 self.docmd(cmd, pars)
140
141if len(sys.argv) < 4:
142 sys.stderr.write("usage: serve-ssi METHOD URL REST\n")
143 sys.exit(1)
144method, url, rest = sys.argv[1:]
145path = os.getenv("REQ_X_ASH_FILE")
146if path is None:
147 sys.stderr.write("serve-ssi: must be called with the X-Ash-File header\n")
148 sys.exit(1)
149if rest != "":
150 simpleerror(sys.stdout, 404, "Not Found", "The resource specified by the URL does not exist.")
151 sys.exit(0)
152
153try:
d4331238
FT
154 try:
155 f = ssifile(open(path), url, path)
156 except Exception:
157 simpleerror(sys.stdout, 500, "Server Error", "The server could not access its data.")
158 sys.exit(1)
159 try:
160 sys.stdout.write("HTTP/1.1 200 OK\n")
161 sys.stdout.write("Content-Type: text/html\n")
162 sys.stdout.write("\n")
163 f.initvars(ssivars)
164 f.process()
165 finally:
166 f.close()
167except IOError:
168 # This is for catching EPIPE, when the client has closed the
169 # connection. This shouldn't *really* be necessary since the
170 # process should terminate with SIGPIPE, but apparently Python
171 # ignores that.
6d94a5f6 172 sys.exit(1)