Added a simple python program for processing SSI.
authorFredrik Tolf <fredrik@dolda2000.com>
Mon, 30 Aug 2010 14:49:04 +0000 (16:49 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Mon, 30 Aug 2010 14:49:04 +0000 (16:49 +0200)
python/serve-ssi [new file with mode: 0755]

diff --git a/python/serve-ssi b/python/serve-ssi
new file mode 100755 (executable)
index 0000000..bbf7188
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+
+# This program is quite incomplete. I might complete it with more
+# features as I need them. It will probably never be entirely
+# compliant with Apache's version due to architectural differences.
+
+import sys, os, time
+
+def htmlquote(text):
+    ret = ""
+    for c in text:
+        if c == '&':
+            ret += "&amp;"
+        elif c == '<':
+            ret += "&lt;"
+        elif c == '>':
+            ret += "&gt;"
+        elif c == '"':
+            ret += "&quot;"
+        else:
+            ret += c
+    return ret
+
+def simpleerror(out, code, title, msg):
+    html = """<?xml version="1.0" encoding="US-ASCII"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
+<head>
+<title>%s</title>
+</head>
+<body>
+<h1>%s</h1>
+<p>%s</p>
+</body>
+</html>
+""" % (title, title, htmlquote(msg))
+    out.write("HTTP/1.1 %d %s\n" % (code, title))
+    out.write("Content-Type: text/html\n")
+    out.write("Content-Length: %d\n" % len(html))
+    out.write("\n")
+    out.write(html)
+
+ssivars = {}
+
+def parsecmd(line, p):
+    try:
+        while line[p].isspace(): p += 1
+        cmd = ""
+        while not line[p].isspace():
+            cmd += line[p]
+            p += 1
+        pars = {}
+        while True:
+            while line[p].isspace(): p += 1
+            if line[p:p + 3] == "-->":
+                return cmd, pars, p + 3
+            key = ""
+            while line[p].isalnum():
+                key += line[p]
+                p += 1
+            if key == "":
+                return None, {}, p
+            while line[p].isspace(): p += 1
+            if line[p] != '=':
+                continue
+            p += 1
+            while line[p].isspace(): p += 1
+            q = line[p]
+            if q != '"' and q != "'" and q != '`':
+                continue
+            val = ""
+            p += 1
+            while line[p] != q:
+                val += line[p]
+                p += 1
+            p += 1
+            pars[key] = val
+    except IndexError:
+        return None, {}, len(line)
+
+class ssifile(object):
+    def __init__(self, s, url, path):
+        self.s = s
+        self.url = url
+        self.path = path
+
+    def close(self):
+        self.s.close();
+
+    def initvars(self, vars):
+        now = time.time()
+        vars["DOCUMENT_NAME"] = os.path.basename(self.path)
+        vars["DATE_GMT"] = time.asctime(time.gmtime(now))
+        vars["DATE_LOCAL"] = time.asctime(time.localtime(now))
+        vars["LAST_MODIFIED"] = time.asctime(time.localtime(os.stat(self.path).st_mtime))
+
+    def includefile(self, path):
+        path = os.path.join(os.path.dirname(self.path), path)
+        try:
+            f = ssifile(open(path), url, path)
+        except Exception:
+            sys.stderr.write("serve-ssi: included file not found: %s\n" % path)
+            return
+        try:
+            f.process()
+        finally:
+            f.close
+
+    def docmd(self, cmd, pars):
+        if cmd == "include":
+            if "file" in pars:
+                self.includefile(pars["file"])
+            elif "virtual" in pars:
+                # XXX: For now, just include the file as-is. Change
+                # when necessary.
+                self.includefile(pars["virtual"])
+        elif cmd == "echo":
+            enc = htmlquote
+            if "encoding" in pars:
+                if pars["encoding"] == "entity":
+                    enc = htmlquote
+            if "var" in pars:
+                if pars["var"] in ssivars:
+                    sys.stdout.write(enc(ssivars[pars["var"]]))
+        else:
+            sys.stderr.write("serve-ssi: unknown SSI command: %s\n" % cmd)
+
+    def process(self):
+        for line in self.s:
+            p = 0
+            while True:
+                p2 = line.find("<!--#", p)
+                if p2 < 0:
+                    sys.stdout.write(line[p:])
+                    break
+                sys.stdout.write(line[p:p2])
+                cmd, pars, p = parsecmd(line, p2 + 5)
+                if cmd is not None:
+                    self.docmd(cmd, pars)
+
+if len(sys.argv) < 4:
+    sys.stderr.write("usage: serve-ssi METHOD URL REST\n")
+    sys.exit(1)
+method, url, rest = sys.argv[1:]
+path = os.getenv("REQ_X_ASH_FILE")
+if path is None:
+    sys.stderr.write("serve-ssi: must be called with the X-Ash-File header\n")
+    sys.exit(1)
+if rest != "":
+    simpleerror(sys.stdout, 404, "Not Found", "The resource specified by the URL does not exist.")
+    sys.exit(0)
+
+try:
+    f = ssifile(open(path), url, path)
+except Exception:
+    simpleerror(sys.stdout, 500, "Server Error", "The server could not access its data.")
+    sys.exit(1)
+try:
+    sys.stdout.write("HTTP/1.1 200 OK\n")
+    sys.stdout.write("Content-Type: text/html\n")
+    sys.stdout.write("\n")
+    f.initvars(ssivars)
+    f.process()
+finally:
+    f.close()
+