rev |
line source |
jbe/bsw@0
|
1 #!/usr/bin/env lua
|
jbe/bsw@0
|
2
|
jbe/bsw@0
|
3 local assert = assert
|
jbe/bsw@0
|
4 local error = error
|
jbe/bsw@0
|
5 local getfenv = getfenv
|
jbe/bsw@0
|
6 local getmetatable = getmetatable
|
jbe/bsw@0
|
7 local ipairs = ipairs
|
jbe/bsw@0
|
8 local next = next
|
jbe/bsw@0
|
9 local pairs = pairs
|
jbe/bsw@0
|
10 local pcall = pcall
|
jbe/bsw@0
|
11 local print = print
|
jbe/bsw@0
|
12 local rawequal = rawequal
|
jbe/bsw@0
|
13 local rawget = rawget
|
jbe/bsw@0
|
14 local rawset = rawset
|
jbe/bsw@0
|
15 local select = select
|
jbe/bsw@0
|
16 local setfenv = setfenv
|
jbe/bsw@0
|
17 local setmetatable = setmetatable
|
jbe/bsw@0
|
18 local tonumber = tonumber
|
jbe/bsw@0
|
19 local tostring = tostring
|
jbe/bsw@0
|
20 local type = type
|
jbe/bsw@0
|
21 local unpack = unpack
|
jbe/bsw@0
|
22 local xpcall = xpcall
|
jbe/bsw@0
|
23
|
jbe/bsw@0
|
24 local io = io
|
jbe/bsw@0
|
25 local math = math
|
jbe/bsw@0
|
26 local os = os
|
jbe/bsw@0
|
27 local string = string
|
jbe/bsw@0
|
28 local table = table
|
jbe/bsw@0
|
29
|
jbe/bsw@0
|
30 module(...)
|
jbe/bsw@0
|
31
|
jbe/bsw@0
|
32 data_sent = false
|
jbe/bsw@0
|
33
|
jbe/bsw@0
|
34 function add_header(...)
|
jbe/bsw@0
|
35 if data_sent then
|
jbe/bsw@0
|
36 error("Can not add header after data has been sent.", 2)
|
jbe/bsw@0
|
37 end
|
jbe/bsw@0
|
38 io.stdout:write(...)
|
jbe/bsw@0
|
39 io.stdout:write("\r\n")
|
jbe/bsw@0
|
40 end
|
jbe/bsw@0
|
41
|
jbe/bsw@0
|
42 function send_data(...)
|
jbe/bsw@0
|
43 if not data_sent then
|
jbe/bsw@0
|
44 io.stdout:write("\r\n")
|
jbe/bsw@0
|
45 data_sent = true
|
jbe/bsw@0
|
46 end
|
jbe/bsw@0
|
47 io.stdout:write(...)
|
jbe/bsw@0
|
48 end
|
jbe/bsw@0
|
49
|
jbe/bsw@0
|
50 function set_status(status)
|
jbe/bsw@0
|
51 add_header("Status: ", status)
|
jbe/bsw@0
|
52 end
|
jbe/bsw@0
|
53
|
jbe/bsw@0
|
54 function redirect(location)
|
jbe/bsw@0
|
55 set_status("303 See Other")
|
jbe/bsw@0
|
56 add_header("Location: ", location)
|
jbe/bsw@0
|
57 end
|
jbe/bsw@0
|
58
|
jbe/bsw@0
|
59 function set_content_type(content_type)
|
jbe/bsw@0
|
60 add_header("Content-Type: ", content_type)
|
jbe/bsw@0
|
61 end
|
jbe/bsw@0
|
62
|
jbe/bsw@0
|
63 method = os.getenv("REQUEST_METHOD") or false
|
jbe/bsw@0
|
64 query = os.getenv("QUERY_STRING") or false
|
jbe/bsw@0
|
65 cookie_data = os.getenv("HTTP_COOKIE") or false
|
jbe/bsw@0
|
66 post_data = io.stdin:read("*a") or false
|
jbe/bsw@0
|
67 post_contenttype = os.getenv("CONTENT_TYPE") or false
|
jbe/bsw@0
|
68 params = {}
|
jbe/bsw@0
|
69 get_params = {}
|
jbe/bsw@0
|
70 cookies = {}
|
jbe/bsw@0
|
71 post_params = {}
|
jbe/bsw@0
|
72 post_filenames = {}
|
jbe/bsw@0
|
73 post_types = {}
|
jbe/bsw@0
|
74
|
jbe/bsw@0
|
75 local urldecode
|
jbe/bsw@0
|
76 do
|
jbe/bsw@0
|
77 local b0 = string.byte("0")
|
jbe/bsw@0
|
78 local b9 = string.byte("9")
|
jbe/bsw@0
|
79 local bA = string.byte("A")
|
jbe/bsw@0
|
80 local bF = string.byte("F")
|
jbe/bsw@0
|
81 local ba = string.byte("a")
|
jbe/bsw@0
|
82 local bf = string.byte("f")
|
jbe/bsw@0
|
83 function urldecode(str)
|
jbe/bsw@0
|
84 return (
|
jbe/bsw@0
|
85 string.gsub(
|
jbe/bsw@0
|
86 string.gsub(str, "%+", " "),
|
jbe/bsw@0
|
87 "%%([0-9A-Fa-f][0-9A-Fa-f])",
|
jbe/bsw@0
|
88 function(hex)
|
jbe/bsw@0
|
89 local n1, n2 = string.byte(hex, 1, 2)
|
jbe/bsw@0
|
90 if n1 >= b0 and n1 <= b9 then n1 = n1 - b0
|
jbe/bsw@0
|
91 elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10
|
jbe/bsw@0
|
92 elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10
|
jbe/bsw@0
|
93 else return end
|
jbe/bsw@0
|
94 if n2 >= b0 and n2 <= b9 then n2 = n2 - b0
|
jbe/bsw@0
|
95 elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10
|
jbe/bsw@0
|
96 elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10
|
jbe/bsw@0
|
97 else return end
|
jbe/bsw@0
|
98 return string.char(n1 * 16 + n2)
|
jbe/bsw@0
|
99 end
|
jbe/bsw@0
|
100 )
|
jbe/bsw@0
|
101 )
|
jbe/bsw@0
|
102 end
|
jbe/bsw@0
|
103 end
|
jbe/bsw@0
|
104
|
jbe/bsw@0
|
105 local function proc_param(tbl, key, value)
|
jbe/bsw@0
|
106 if string.find(key, "%[%]$") then
|
jbe/bsw@0
|
107 if tbl[key] then
|
jbe/bsw@0
|
108 table.insert(tbl[key], value)
|
jbe/bsw@0
|
109 else
|
jbe/bsw@0
|
110 local list = { value }
|
jbe/bsw@0
|
111 params[key] = list
|
jbe/bsw@0
|
112 tbl[key] = list
|
jbe/bsw@0
|
113 end
|
jbe/bsw@0
|
114 else
|
jbe/bsw@0
|
115 params[key] = value
|
jbe/bsw@0
|
116 tbl[key] = value
|
jbe/bsw@0
|
117 end
|
jbe/bsw@0
|
118 end
|
jbe/bsw@0
|
119
|
jbe/bsw@0
|
120 local function read_urlencoded_form(tbl, data)
|
jbe/bsw@0
|
121 for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do
|
jbe/bsw@0
|
122 proc_param(tbl, urldecode(rawkey), urldecode(rawvalue))
|
jbe/bsw@0
|
123 end
|
jbe/bsw@0
|
124 end
|
jbe/bsw@0
|
125
|
jbe/bsw@0
|
126 if query then
|
jbe/bsw@0
|
127 read_urlencoded_form(get_params, query)
|
jbe/bsw@0
|
128 end
|
jbe/bsw@0
|
129
|
jbe/bsw@0
|
130 if cookie_data then
|
jbe/bsw@0
|
131 for rawkey, rawvalue in string.gmatch(cookie_data, "([^=; ]*)=([^=; ]*)") do
|
jbe/bsw@0
|
132 cookies[urldecode(rawkey)] = urldecode(rawvalue)
|
jbe/bsw@0
|
133 end
|
jbe/bsw@0
|
134 end
|
jbe/bsw@0
|
135
|
jbe/bsw@0
|
136 if post_contenttype == "application/x-www-form-urlencoded" then
|
jbe/bsw@0
|
137 read_urlencoded_form(post_params, post_data)
|
jbe/bsw@0
|
138 elseif post_contenttype then
|
jbe/bsw@0
|
139 local boundary = string.match(
|
jbe/bsw@0
|
140 post_contenttype,
|
jbe/bsw@0
|
141 '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"'
|
jbe/bsw@0
|
142 ) or string.match(
|
jbe/bsw@0
|
143 post_contenttype,
|
jbe/bsw@0
|
144 '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)'
|
jbe/bsw@0
|
145 )
|
jbe/bsw@0
|
146 if boundary then
|
jbe/bsw@0
|
147 local parts = {}
|
jbe/bsw@0
|
148 do
|
jbe/bsw@0
|
149 local boundary = "\r\n--" .. boundary
|
jbe/bsw@0
|
150 local post_data = "\r\n" .. post_data
|
jbe/bsw@0
|
151 local pos1, pos2 = string.find(post_data, boundary, 1, true)
|
jbe/bsw@0
|
152 while true do
|
jbe/bsw@0
|
153 local ind = string.sub(post_data, pos2 + 1, pos2 + 2)
|
jbe/bsw@0
|
154 if ind == "\r\n" then
|
jbe/bsw@0
|
155 local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true)
|
jbe/bsw@0
|
156 if pos3 then
|
jbe/bsw@0
|
157 parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1)
|
jbe/bsw@0
|
158 else
|
jbe/bsw@0
|
159 error("Illegal POST data.")
|
jbe/bsw@0
|
160 end
|
jbe/bsw@0
|
161 pos1, pos2 = pos3, pos4
|
jbe/bsw@0
|
162 elseif ind == "--" then
|
jbe/bsw@0
|
163 break
|
jbe/bsw@0
|
164 else
|
jbe/bsw@0
|
165 error("Illegal POST data.")
|
jbe/bsw@0
|
166 end
|
jbe/bsw@0
|
167 end
|
jbe/bsw@0
|
168 end
|
jbe/bsw@0
|
169 for i, part in ipairs(parts) do
|
jbe/bsw@0
|
170 local pos = 1
|
jbe/bsw@0
|
171 local name, filename, contenttype
|
jbe/bsw@0
|
172 while true do
|
jbe/bsw@0
|
173 local header
|
jbe/bsw@0
|
174 do
|
jbe/bsw@0
|
175 local oldpos = pos
|
jbe/bsw@0
|
176 pos = string.find(part, "\r\n", oldpos, true)
|
jbe/bsw@0
|
177 if not pos then
|
jbe/bsw@0
|
178 error("Illegal POST data.")
|
jbe/bsw@0
|
179 end
|
jbe/bsw@0
|
180 if pos == oldpos then break end
|
jbe/bsw@0
|
181 header = string.sub(part, oldpos, pos - 1)
|
jbe/bsw@0
|
182 pos = pos + 2
|
jbe/bsw@0
|
183 end
|
jbe/bsw@0
|
184 if string.find(
|
jbe/bsw@0
|
185 string.lower(header),
|
jbe/bsw@0
|
186 "^content%-disposition:[ \t]*form%-data[ \t]*;"
|
jbe/bsw@0
|
187 ) then
|
jbe/bsw@0
|
188 -- TODO: handle all cases correctly
|
jbe/bsw@0
|
189 name = string.match(header, ';[ \t]*name="([^"]*)"') or
|
jbe/bsw@0
|
190 string.match(header, ';[ \t]*name=([^"; \t]+)')
|
jbe/bsw@0
|
191 filename = string.match(header, ';[ \t]*filename="([^"]*)"') or
|
jbe/bsw@0
|
192 string.match(header, ';[ \t]*filename=([^"; \t]+)')
|
jbe/bsw@0
|
193 else
|
jbe/bsw@0
|
194 local dummy, subpos = string.find(
|
jbe/bsw@0
|
195 string.lower(header),
|
jbe/bsw@0
|
196 "^content%-type:[ \t]*"
|
jbe/bsw@0
|
197 )
|
jbe/bsw@0
|
198 if subpos then
|
jbe/bsw@0
|
199 contenttype = string.sub(header, subpos + 1, #header)
|
jbe/bsw@0
|
200 end
|
jbe/bsw@0
|
201 end
|
jbe/bsw@0
|
202 end
|
jbe/bsw@0
|
203 local content = string.sub(part, pos + 2, #part)
|
jbe/bsw@0
|
204 if not name then
|
jbe/bsw@0
|
205 error("Illegal POST data.")
|
jbe/bsw@0
|
206 end
|
jbe/bsw@0
|
207 proc_param(post_params, name, content)
|
jbe/bsw@0
|
208 post_filenames[name] = filename
|
jbe/bsw@0
|
209 post_types[name] = contenttype
|
jbe/bsw@0
|
210 end
|
jbe/bsw@0
|
211 end
|
jbe/bsw@0
|
212 end
|
jbe/bsw@0
|
213
|
jbe/bsw@0
|
214 if post_data and #post_data > 262144 then
|
jbe/bsw@0
|
215 post_data = nil
|
jbe/bsw@0
|
216 collectgarbage("collect")
|
jbe/bsw@0
|
217 else
|
jbe/bsw@0
|
218 post_data = nil
|
jbe/bsw@0
|
219 end
|