1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
### Copyright 2004, Xavier Neys (neysx@gentoo.org)
# #
# # This file is part of gorg.
# #
# # gorg is free software; you can redistribute it and/or modify
# # it under the terms of the GNU General Public License as published by
# # the Free Software Foundation; either version 2 of the License, or
# # (at your option) any later version.
# #
# # gorg is distributed in the hope that it will be useful,
# # but WITHOUT ANY WARRANTY; without even the implied warranty of
# # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# # GNU General Public License for more details.
# #
# # You should have received a copy of the GNU General Public License
# # along with Foobar; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Process CGI request, either from cgi or fcgi
require "gorg/base"
module Gorg
def do_Filter(tmout=30, params=nil)
# Read STDIN, transform, spit result out
timeout(tmout) {
# Give it a few seconds to read it all, then timeout
xml = STDIN.read
err, body, filelist = xproc(xml, params, false, true)
if err["xmlErrLevel"] > 0 then
STDERR.puts("#{err.collect{|e|e.join(':')}.join("\n")}")
elsif (body||"").length < 1 then
# Some transforms can yield empty content
STDERR.puts("Empty body")
else
STDOUT.puts(body)
end
}
rescue Timeout::Error, StandardError =>ex
# Just spew it out
STDERR.puts(ex)
end
def do_CGI(cgi)
header = Hash.new
if cgi.path_info.nil? || cgi.env_table["REQUEST_URI"].index("/#{File.basename($0)}/")
# Sorry, I'm not supposed to be called directly, e.g. /cgi-bin/gorg.cgi/bullshit_from_smartass_skriptbaby
raise Gorg::Status::Forbidden
elsif cgi.request_method == "OPTIONS"
cgi.out('Allow'=>'GET,HEAD'){""}
elsif cgi.request_method == "HEAD" or cgi.request_method == "GET"
# lighttp is b0rked despite what they say :(
# PATH_INFO == "" and PATH_TRANSLATED == nil
if cgi.path_info.length > 0 then
# Apache, or any web browser that works
path_info = cgi.path_info
else
# lighttp, use SCRIPT_NAME instead
path_info = cgi.env_table['SCRIPT_NAME']
end
query = Hash.new
cgi.params.each do |p, v|
# fcgi 0.9 always provides an Array even with one
# argument. Gorg only handles one argument, as far as I can
# tell, so we take the first value in that case.
value = if v.class == Array
v.first
else
v
end
query[p] = value.to_s
end
# Get DOCUMENT_ROOT from environment
$Config["root"] = cgi.env_table['DOCUMENT_ROOT']
xml_file = cgi.path_translated||(cgi.env_table['DOCUMENT_ROOT']+cgi.env_table['SCRIPT_NAME'])
if not FileTest.file?(xml_file)
# Should have been checked by apache, check anyway
raise Gorg::Status::NotFound
else
# Process request
# Parse If-None-Match and If-Modified-Since request header fields if any
inm=ims=nil
begin
inm = split_header_etags(cgi.env_table['HTTP_IF_NONE_MATCH']) if cgi.env_table['HTTP_IF_NONE_MATCH']
ims = Time.parse(cgi.env_table['HTTP_IF_MODIFIED_SINCE']) if cgi.env_table['HTTP_IF_MODIFIED_SINCE']
ims = nil if ims > Time.now # Dates later than current must be ignored
rescue
# Just ignore ill-formated data
nil
end
if $Config['passthru'] && query["passthru"] && query["passthru"] != "0" then
# passthru allowed by config and requested by visitor, return file as text/plain
debug("Passthru granted for #{path_info}")
mstat = File.stat(xml_file)
raise Gorg::Status::NotModified.new(mstat) if notModified?(mstat, inm, ims)
body = IO.read(xml_file)
header['type'] = 'text/plain'
# If client accepts gzip encoding and we support it, return gzipped file
if $Config["zipLevel"] > 0 and ( cgi.accept_encoding =~ /gzip(\s*;\s*q=([0-9\.]+))?/ and ($2||"1") != "0" ) then
body = gzip(body, $Config["zipLevel"])
header['Content-Encoding'] = "gzip"
header['Vary'] = "Accept-Encoding"
end
else
# Get cookies and add them to the parameters
if $Config["acceptCookies"] then
# Add cookies to our params
query.merge!(cookies_to_params(cgi.cookies))
end
if $Config["httphost"] then
# Add HTTP_HOST to stylesheet params
query["httphost"] = if $Config["httphost"][0] == '*' then
cgi.host||""
elsif $Config["httphost"].include?('*') then
$Config["httphost"][0]
elsif $Config["httphost"].include?(cgi.host) then
$Config["httphost"][0]
else
cgi.host||""
end
end
xml_query = query.dup # xml_query==params passed to the XSL, query=>metadata in cache
if $Config["linkParam"] then
xml_query[$Config["linkParam"]] = path_info
end
bodyZ = nil # Compressed version
body, mstat, extrameta = Cache.hit(path_info, query, inm, ims)
if body.nil? then
# Cache miss, process file and cache result
err, body, filelist, extrameta = xproc(xml_file, xml_query, true)
if err["xmlErrLevel"] > 0 then
raise "#{err.collect{|e|e.join(':')}.join('<br/>')}"
elsif (body||"").length < 1 then
# Some transforms can yield empty content (handbook?part=9&chap=99)
# Consider this a 404
raise Gorg::Status::NotFound
else
# Cache the output if all was OK
mstat, bodyZ = Cache.store(body, path_info, query, filelist, extrameta)
debug("Cached #{path_info}, mstat=#{mstat.inspect}")
# Check inm & ims again as they might match if another web node had
# previously delivered the same data
if notModified?(mstat, inm, ims) and extrameta.join !~ /set-cookie/i
raise Gorg::Status::NotModified.new(mstat)
end
end
else
if $Config["zipLevel"] > 0 then
bodyZ = body
body = nil
end
end
# If client accepts gzip encoding and we support it, return gzipped file
if bodyZ and $Config["zipLevel"] > 0 and ( cgi.accept_encoding =~ /gzip(\s*;\s*q=([0-9\.]+))?/ and ($2||"1") != "0" ) then
body = bodyZ
header['Content-Encoding'] = "gzip"
header['Vary'] = "Accept-Encoding"
else
unless body then
# We need to unzip bodyZ into body, i.e. we cached zipped data but client does not support gzip
body = gunzip(bodyZ)
end
end
# Add cookies to http header
cookies = makeCookies(extrameta)
if cookies then
header['cookie'] = cookies
end
# Add Content-Type to header
ct = contentType(extrameta)
if ct then
# Turn application/xhtml+xml into text/html if browser does not accept it
if cgi.accept !~ /application\/xhtml\+xml/ and ct =~ /application\/xhtml\+xml(.*)$/ then
header['type'] = "text/html#{$1}"
else
header['type'] = ct
end
else
header['type'] = 'text/plain'
end
end
# Add ETag & Last-Modified http headers
# NB: it's simply mstat(file.xml) when passthru=1
if mstat then
header['ETag'] = makeETag(mstat)
header['Last-Modified'] = mstat.mtime.httpdate
end
end
cgi.out(header){body}
else # Not a HEAD or GET
raise Gorg::Status::NotAllowed
end
rescue => ex
if ex.respond_to?(:errCode) then
# One of ours (Gorg::Status::HTTPStatus)
cgi.out(ex.header){ex.html}
else
# Some ruby exceptions occurred, make it a 500
syserr = Gorg::Status::SysError.new
cgi.out('Status'=>syserr.errSts){syserr.html(ex)}
error("do_CGI() failed: #{$!}")
end
end
end
|