#!/usr/bin/ruby

class Time
  def http
    utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
  end
end

class App

  SHAREDIR = "/usr/local/share/xmlvw"
  URLFIX = proc {|url| url.sub!(%r{^http://localhost:80/cgi-bin/}, 'http://toyoda-eizi.net/') }
  #URLFIX = nil

  def initialize
    @path = ENV['PATH_INFO']
    @myname = nil
    @cgi = nil
    @resp = nil
  end

  def myname
    return @myname if @myname
    host = ENV['SERVER_NAME'] || 'localhost'
    port = ENV['SERVER_PORT'] || '80'
    script = ENV['SCRIPT_NAME']
    @myname = "http://#{host}:#{port}#{script}"
    URLFIX.call(@myname) if URLFIX
    @myname
  end

  def showxsl xsl
    File.open(File.join(SHAREDIR, "#{xsl}.xsl"), 'r') { |fp|
      @resp = (<<RESP) + fp.read
Content-Type: text/xml\r
Date: #{Time.now.http}\r
Expires: #{(Time.now.utc + 86400 * 365).http}\r
\r
RESP
    }
  end

  def applyxsl xsl, url
    require 'rubygems'
    require 'xml'
    require 'libxslt'
    xml = nil
    if url.respond_to?(:read) then
      xml = url.read
      return if xml.empty?
      respxml = { 'date' => Time.now.utc.http }
      url = ''
    else
      require 'net/http'
      require 'uri'
      url += "?#{ENV['QUERY_STRING']}" unless ENV['QUERY_STRING'].to_s.empty?
      url = url.sub(%r{^http:/(?!/)}, 'http://')
      raise "only http is loadable - #{url}" unless /^http:/ === url
      respxml = Net::HTTP.get_response(URI.parse(url))
      n = 0
      while /^3/ === respxml.code
	p respxml['location'] if $DEBUG
	respxml = Net::HTTP.get_response(URI.parse(respxml['location']))
	n += 1
	raise "10 forwards" if n > 10
      end
      raise "resource #{url} not found" if /^4/ === respxml.code
      raise "error at #{url}" if /^5/ === respxml.code
      xml = respxml.body
    end
    doc = XML::Document.string(xml)
    piurl = url.gsub(/\?>/, '?%3E').gsub(/&/, '&amp;')
    docxsl = XML::Document.string(<<XSLT)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>

<xsl:template match="/">
  <xsl:processing-instruction name="xml-stylesheet"
  >href=&quot;#{[myname, xsl].join('/') }&quot; type=&quot;text/xsl&quot;</xsl:processing-instruction>
  <xsl:processing-instruction name="document-uri">#{piurl}</xsl:processing-instruction>
  <xsl:apply-templates />
</xsl:template>

<xsl:template match="*">
  <xsl:copy select=".">
  <xsl:apply-templates select="*|@*|text()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="@*|text()">
  <xsl:copy select="."/>
</xsl:template>

</xsl:stylesheet>
XSLT
    p piurl if $DEBUG
    xsl = LibXSLT::XSLT::Stylesheet.new(docxsl)
    resp = xsl.apply(doc)
    expires = (Time.now.utc + 86400 * 365).http
    @resp = (<<HTTP) + resp.to_s
Content-Type: application/xml\r
Last-modified: #{respxml['last-modified'] || respxml['date']}\r
Date: #{respxml['date']}\r
Expires: #{expires}\r
\r
HTTP
  end

  def form
    require 'cgi'
    @cgi = CGI.new
    xsl = @cgi['xsl']
    xsl = xsl.read if xsl.respond_to?(:read)
    return if xsl.empty?
    url = @cgi['url']
    url = url.read if url.respond_to?(:read)
    if !(url.empty?) then
      redirect(xsl, url)
      #applyxsl(xsl, url)
      return
    end
    if @cgi.has_key?('upld') then
      applyxsl(xsl, @cgi['upld'])
      return
    end
  end

  def redirect xsl, url
    @resp = <<RESP
Status: 302 Found\r
Location: #{myname}/#{xsl}/#{url}\r
\r
RESP
  end

  def showform(msg)
    require 'cgi'
    hmsg = CGI.escapeHTML(msg)
    options = nil
    Dir.open(SHAREDIR) {|dp|
      options = dp.collect.grep(%r{^\w+\.xsl$}).map{|f|
	CGI.escapeHTML(f.sub(/\.xsl$/, ''))
      }
    }
    @resp = <<RESP
Content-Type: text/html\r
\r
<html>
  <head><title>XML Viewer</title></head>
  <body>
    <p>#{hmsg}</p>
    <form action="#{myname}" method="POST" enctype="multipart/form-data">
    <h1>XML viewer</h1>
    <ol>
      <li>Choose XSLT: 
      <select name="xsl">
      #{options.map do |s| "<option value='"+s+"'>"+s+"</option>" end .join}
      </select>
      </li>
      <li>Specify XML:
      <ul>
      <li>by URL: <input type="text" name="url" size="128" /></li>
      <li>or your local file: <input type="file" name="upld" /></li>
      </ul>
      </li>
      <li>
      <input type="submit" value="apply" />
      </li>
    </form>
  </body>
</html>
RESP
  end

  def run
    begin
      case @path
      when %r{^/([^/]+)$} then showxsl($1)
      when %r{^/([^/]+)/(.*)} then applyxsl($1, $2)
      else form
      end
    rescue Exception => e
      showform("#{e.backtrace.reject do |s| /\.rb:\d+:in/ === s end.join(' < ')}: #{e.message}")
    end
    showform('') unless @resp
    print @resp
  end

end

App.new.run
