#!/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:
    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()
except IOError:
    # This is for catching EPIPE, when the client has closed the
    # connection. This shouldn't *really* be necessary since the
    # process should terminate with SIGPIPE, but apparently Python
    # ignores that.
    sys.exit(1)
