luci-base: ensure that file upload values have length
[project/luci.git] / modules / luci-base / luasrc / http.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io>
3 -- Licensed to the public under the Apache License 2.0.
4
5 local util  = require "luci.util"
6 local coroutine = require "coroutine"
7 local table = require "table"
8 local lhttp = require "lucihttp"
9 local nixio = require "nixio"
10 local ltn12 = require "luci.ltn12"
11
12 local table, ipairs, pairs, type, tostring, tonumber, error =
13         table, ipairs, pairs, type, tostring, tonumber, error
14
15 module "luci.http"
16
17 HTTP_MAX_CONTENT      = 1024*8          -- 8 kB maximum content size
18
19 context = util.threadlocal()
20
21 Request = util.class()
22 function Request.__init__(self, env, sourcein, sinkerr)
23         self.input = sourcein
24         self.error = sinkerr
25
26
27         -- File handler nil by default to let .content() work
28         self.filehandler = nil
29
30         -- HTTP-Message table
31         self.message = {
32                 env = env,
33                 headers = {},
34                 params = urldecode_params(env.QUERY_STRING or ""),
35         }
36
37         self.parsed_input = false
38 end
39
40 function Request.formvalue(self, name, noparse)
41         if not noparse and not self.parsed_input then
42                 self:_parse_input()
43         end
44
45         if name then
46                 return self.message.params[name]
47         else
48                 return self.message.params
49         end
50 end
51
52 function Request.formvaluetable(self, prefix)
53         local vals = {}
54         prefix = prefix and prefix .. "." or "."
55
56         if not self.parsed_input then
57                 self:_parse_input()
58         end
59
60         local void = self.message.params[nil]
61         for k, v in pairs(self.message.params) do
62                 if k:find(prefix, 1, true) == 1 then
63                         vals[k:sub(#prefix + 1)] = tostring(v)
64                 end
65         end
66
67         return vals
68 end
69
70 function Request.content(self)
71         if not self.parsed_input then
72                 self:_parse_input()
73         end
74
75         return self.message.content, self.message.content_length
76 end
77
78 function Request.getcookie(self, name)
79         return lhttp.header_attribute("cookie; " .. (self:getenv("HTTP_COOKIE") or ""), name)
80 end
81
82 function Request.getenv(self, name)
83         if name then
84                 return self.message.env[name]
85         else
86                 return self.message.env
87         end
88 end
89
90 function Request.setfilehandler(self, callback)
91         self.filehandler = callback
92
93         if not self.parsed_input then
94                 return
95         end
96
97         -- If input has already been parsed then uploads are stored as unlinked
98         -- temporary files pointed to by open file handles in the parameter
99         -- value table. Loop all params, and invoke the file callback for any
100         -- param with an open file handle.
101         local name, value
102         for name, value in pairs(self.message.params) do
103                 if type(value) == "table" then
104                         while value.fd do
105                                 local data = value.fd:read(1024)
106                                 local eof = (not data or data == "")
107
108                                 callback(value, data, eof)
109
110                                 if eof then
111                                         value.fd:close()
112                                         value.fd = nil
113                                 end
114                         end
115                 end
116         end
117 end
118
119 function Request._parse_input(self)
120         parse_message_body(
121                  self.input,
122                  self.message,
123                  self.filehandler
124         )
125         self.parsed_input = true
126 end
127
128 function close()
129         if not context.eoh then
130                 context.eoh = true
131                 coroutine.yield(3)
132         end
133
134         if not context.closed then
135                 context.closed = true
136                 coroutine.yield(5)
137         end
138 end
139
140 function content()
141         return context.request:content()
142 end
143
144 function formvalue(name, noparse)
145         return context.request:formvalue(name, noparse)
146 end
147
148 function formvaluetable(prefix)
149         return context.request:formvaluetable(prefix)
150 end
151
152 function getcookie(name)
153         return context.request:getcookie(name)
154 end
155
156 -- or the environment table itself.
157 function getenv(name)
158         return context.request:getenv(name)
159 end
160
161 function setfilehandler(callback)
162         return context.request:setfilehandler(callback)
163 end
164
165 function header(key, value)
166         if not context.headers then
167                 context.headers = {}
168         end
169         context.headers[key:lower()] = value
170         coroutine.yield(2, key, value)
171 end
172
173 function prepare_content(mime)
174         if not context.headers or not context.headers["content-type"] then
175                 if mime == "application/xhtml+xml" then
176                         if not getenv("HTTP_ACCEPT") or
177                           not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
178                                 mime = "text/html; charset=UTF-8"
179                         end
180                         header("Vary", "Accept")
181                 end
182                 header("Content-Type", mime)
183         end
184 end
185
186 function source()
187         return context.request.input
188 end
189
190 function status(code, message)
191         code = code or 200
192         message = message or "OK"
193         context.status = code
194         coroutine.yield(1, code, message)
195 end
196
197 -- This function is as a valid LTN12 sink.
198 -- If the content chunk is nil this function will automatically invoke close.
199 function write(content, src_err)
200         if not content then
201                 if src_err then
202                         error(src_err)
203                 else
204                         close()
205                 end
206                 return true
207         elseif #content == 0 then
208                 return true
209         else
210                 if not context.eoh then
211                         if not context.status then
212                                 status()
213                         end
214                         if not context.headers or not context.headers["content-type"] then
215                                 header("Content-Type", "text/html; charset=utf-8")
216                         end
217                         if not context.headers["cache-control"] then
218                                 header("Cache-Control", "no-cache")
219                                 header("Expires", "0")
220                         end
221                         if not context.headers["x-frame-options"] then
222                                 header("X-Frame-Options", "SAMEORIGIN")
223                         end
224                         if not context.headers["x-xss-protection"] then
225                                 header("X-XSS-Protection", "1; mode=block")
226                         end
227                         if not context.headers["x-content-type-options"] then
228                                 header("X-Content-Type-Options", "nosniff")
229                         end
230
231                         context.eoh = true
232                         coroutine.yield(3)
233                 end
234                 coroutine.yield(4, content)
235                 return true
236         end
237 end
238
239 function splice(fd, size)
240         coroutine.yield(6, fd, size)
241 end
242
243 function redirect(url)
244         if url == "" then url = "/" end
245         status(302, "Found")
246         header("Location", url)
247         close()
248 end
249
250 function build_querystring(q)
251         local s, n, k, v = {}, 1, nil, nil
252
253         for k, v in pairs(q) do
254                 s[n+0] = (n == 1) and "?" or "&"
255                 s[n+1] = util.urlencode(k)
256                 s[n+2] = "="
257                 s[n+3] = util.urlencode(v)
258                 n = n + 4
259         end
260
261         return table.concat(s, "")
262 end
263
264 urldecode = util.urldecode
265
266 urlencode = util.urlencode
267
268 function write_json(x)
269         util.serialize_json(x, write)
270 end
271
272 -- from given url or string. Returns a table with urldecoded values.
273 -- Simple parameters are stored as string values associated with the parameter
274 -- name within the table. Parameters with multiple values are stored as array
275 -- containing the corresponding values.
276 function urldecode_params(url, tbl)
277         local parser, name
278         local params = tbl or { }
279
280         parser = lhttp.urlencoded_parser(function (what, buffer, length)
281                 if what == parser.TUPLE then
282                         name, value = nil, nil
283                 elseif what == parser.NAME then
284                         name = lhttp.urldecode(buffer)
285                 elseif what == parser.VALUE and name then
286                         params[name] = lhttp.urldecode(buffer) or ""
287                 end
288
289                 return true
290         end)
291
292         if parser then
293                 parser:parse((url or ""):match("[^?]*$"))
294                 parser:parse(nil)
295         end
296
297         return params
298 end
299
300 -- separated by "&". Tables are encoded as parameters with multiple values by
301 -- repeating the parameter name with each value.
302 function urlencode_params(tbl)
303         local k, v
304         local n, enc = 1, {}
305         for k, v in pairs(tbl) do
306                 if type(v) == "table" then
307                         local i, v2
308                         for i, v2 in ipairs(v) do
309                                 if enc[1] then
310                                         enc[n] = "&"
311                                         n = n + 1
312                                 end
313
314                                 enc[n+0] = lhttp.urlencode(k)
315                                 enc[n+1] = "="
316                                 enc[n+2] = lhttp.urlencode(v2)
317                                 n = n + 3
318                         end
319                 else
320                         if enc[1] then
321                                 enc[n] = "&"
322                                 n = n + 1
323                         end
324
325                         enc[n+0] = lhttp.urlencode(k)
326                         enc[n+1] = "="
327                         enc[n+2] = lhttp.urlencode(v)
328                         n = n + 3
329                 end
330         end
331
332         return table.concat(enc, "")
333 end
334
335 -- Content-Type. Stores all extracted data associated with its parameter name
336 -- in the params table within the given message object. Multiple parameter
337 -- values are stored as tables, ordinary ones as strings.
338 -- If an optional file callback function is given then it is feeded with the
339 -- file contents chunk by chunk and only the extracted file name is stored
340 -- within the params table. The callback function will be called subsequently
341 -- with three arguments:
342 --  o Table containing decoded (name, file) and raw (headers) mime header data
343 --  o String value containing a chunk of the file data
344 --  o Boolean which indicates wheather the current chunk is the last one (eof)
345 function mimedecode_message_body(src, msg, file_cb)
346         local parser, header, field
347         local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
348
349         parser, err = lhttp.multipart_parser(msg.env.CONTENT_TYPE, function (what, buffer, length)
350                 if what == parser.PART_INIT then
351                         field = { }
352
353                 elseif what == parser.HEADER_NAME then
354                         header = buffer:lower()
355
356                 elseif what == parser.HEADER_VALUE and header then
357                         if header:lower() == "content-disposition" and
358                            lhttp.header_attribute(buffer, nil) == "form-data"
359                         then
360                                 field.name = lhttp.header_attribute(buffer, "name")
361                                 field.file = lhttp.header_attribute(buffer, "filename")
362                                 field[1] = field.file
363                         end
364
365                         if field.headers then
366                                 field.headers[header] = buffer
367                         else
368                                 field.headers = { [header] = buffer }
369                         end
370
371                 elseif what == parser.PART_BEGIN then
372                         return not field.file
373
374                 elseif what == parser.PART_DATA and field.name and length > 0 then
375                         if field.file then
376                                 if file_cb then
377                                         file_cb(field, buffer, false)
378                                         msg.params[field.name] = msg.params[field.name] or field
379                                 else
380                                         if not field.fd then
381                                                 field.fd = nixio.mkstemp(field.name)
382                                         end
383
384                                         if field.fd then
385                                                 field.fd:write(buffer)
386                                                 msg.params[field.name] = msg.params[field.name] or field
387                                         end
388                                 end
389                         else
390                                 field.value = buffer
391                         end
392
393                 elseif what == parser.PART_END and field.name then
394                         if field.file and msg.params[field.name] then
395                                 if file_cb then
396                                         file_cb(field, "", true)
397                                 elseif field.fd then
398                                         field.fd:seek(0, "set")
399                                 end
400                         else
401                                 local val = msg.params[field.name]
402
403                                 if type(val) == "table" then
404                                         val[#val+1] = field.value or ""
405                                 elseif val ~= nil then
406                                         msg.params[field.name] = { val, field.value or "" }
407                                 else
408                                         msg.params[field.name] = field.value or ""
409                                 end
410                         end
411
412                         field = nil
413
414                 elseif what == parser.ERROR then
415                         err = buffer
416                 end
417
418                 return true
419         end)
420
421         return ltn12.pump.all(src, function (chunk)
422                 len = len + (chunk and #chunk or 0)
423
424                 if maxlen and len > maxlen + 2 then
425                         return nil, "Message body size exceeds Content-Length"
426                 end
427
428                 if not parser or not parser:parse(chunk) then
429                         return nil, err
430                 end
431
432                 return true
433         end)
434 end
435
436 -- Content-Type. Stores all extracted data associated with its parameter name
437 -- in the params table within the given message object. Multiple parameter
438 -- values are stored as tables, ordinary ones as strings.
439 function urldecode_message_body(src, msg)
440         local err, name, value, parser
441         local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)
442
443         parser = lhttp.urlencoded_parser(function (what, buffer, length)
444                 if what == parser.TUPLE then
445                         name, value = nil, nil
446                 elseif what == parser.NAME then
447                         name = lhttp.urldecode(buffer, lhttp.DECODE_PLUS)
448                 elseif what == parser.VALUE and name then
449                         local val = msg.params[name]
450
451                         if type(val) == "table" then
452                                 val[#val+1] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
453                         elseif val ~= nil then
454                                 msg.params[name] = { val, lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" }
455                         else
456                                 msg.params[name] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or ""
457                         end
458                 elseif what == parser.ERROR then
459                         err = buffer
460                 end
461
462                 return true
463         end)
464
465         return ltn12.pump.all(src, function (chunk)
466                 len = len + (chunk and #chunk or 0)
467
468                 if maxlen and len > maxlen + 2 then
469                         return nil, "Message body size exceeds Content-Length"
470                 elseif len > HTTP_MAX_CONTENT then
471                         return nil, "Message body size exceeds maximum allowed length"
472                 end
473
474                 if not parser or not parser:parse(chunk) then
475                         return nil, err
476                 end
477
478                 return true
479         end)
480 end
481
482 -- This function will examine the Content-Type within the given message object
483 -- to select the appropriate content decoder.
484 -- Currently the application/x-www-urlencoded and application/form-data
485 -- mime types are supported. If the encountered content encoding can't be
486 -- handled then the whole message body will be stored unaltered as "content"
487 -- property within the given message object.
488 function parse_message_body(src, msg, filecb)
489         local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil)
490
491         -- Is it multipart/mime ?
492         if msg.env.REQUEST_METHOD == "POST" and
493            ctype == "multipart/form-data"
494         then
495                 return mimedecode_message_body(src, msg, filecb)
496
497         -- Is it application/x-www-form-urlencoded ?
498         elseif msg.env.REQUEST_METHOD == "POST" and
499                ctype == "application/x-www-form-urlencoded"
500         then
501                 return urldecode_message_body(src, msg)
502
503
504         -- Unhandled encoding
505         -- If a file callback is given then feed it chunk by chunk, else
506         -- store whole buffer in message.content
507         else
508
509                 local sink
510
511                 -- If we have a file callback then feed it
512                 if type(filecb) == "function" then
513                         local meta = {
514                                 name = "raw",
515                                 encoding = msg.env.CONTENT_TYPE
516                         }
517                         sink = function( chunk )
518                                 if chunk then
519                                         return filecb(meta, chunk, false)
520                                 else
521                                         return filecb(meta, nil, true)
522                                 end
523                         end
524                 -- ... else append to .content
525                 else
526                         msg.content = ""
527                         msg.content_length = 0
528
529                         sink = function( chunk )
530                                 if chunk then
531                                         if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
532                                                 msg.content        = msg.content        .. chunk
533                                                 msg.content_length = msg.content_length + #chunk
534                                                 return true
535                                         else
536                                                 return nil, "POST data exceeds maximum allowed length"
537                                         end
538                                 end
539                                 return true
540                         end
541                 end
542
543                 -- Pump data...
544                 while true do
545                         local ok, err = ltn12.pump.step( src, sink )
546
547                         if not ok and err then
548                                 return nil, err
549                         elseif not ok then -- eof
550                                 return true
551                         end
552                 end
553
554                 return true
555         end
556 end