* luci/libs/http: remove left over field initialisation
[project/luci.git] / libs / http / luasrc / http / protocol.lua
1 --[[
2
3 HTTP protocol implementation for LuCI
4 (c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10         http://www.apache.org/licenses/LICENSE-2.0
11
12 $Id$
13
14 ]]--
15
16 module("luci.http.protocol", package.seeall)
17
18 local ltn12 = require("luci.ltn12")
19
20 HTTP_MAX_CONTENT      = 1024*4          -- 4 kB maximum content size
21 HTTP_URLENC_MAXKEYLEN = 1024            -- maximum allowd size of urlencoded parameter names
22
23
24 -- Decode an urlencoded string.
25 -- Returns the decoded value.
26 function urldecode( str, no_plus )
27
28         local function __chrdec( hex )
29                 return string.char( tonumber( hex, 16 ) )
30         end
31
32         if type(str) == "string" then
33                 if not no_plus then
34                         str = str:gsub( "+", " " )
35                 end
36
37                 str = str:gsub( "%%([a-fA-F0-9][a-fA-F0-9])", __chrdec )
38         end
39
40         return str
41 end
42
43
44 -- Extract and split urlencoded data pairs, separated bei either "&" or ";" from given url.
45 -- Returns a table value with urldecoded values.
46 function urldecode_params( url, tbl )
47
48         local params = tbl or { }
49
50         if url:find("?") then
51                 url = url:gsub( "^.+%?([^?]+)", "%1" )
52         end
53
54         for pair in url:gmatch( "[^&;]+" ) do
55
56                 -- find key and value
57                 local key = urldecode( pair:match("^([^=]+)")     )
58                 local val = urldecode( pair:match("^[^=]+=(.+)$") )
59
60                 -- store
61                 if type(key) == "string" and key:len() > 0 then
62                         if type(val) ~= "string" then val = "" end
63
64                         if not params[key] then
65                                 params[key] = val
66                         elseif type(params[key]) ~= "table" then
67                                 params[key] = { params[key], val }
68                         else
69                                 table.insert( params[key], val )
70                         end
71                 end
72         end
73
74         return params
75 end
76
77
78 -- Encode given string in urlencoded format.
79 -- Returns the encoded string.
80 function urlencode( str )
81
82         local function __chrenc( chr )
83                 return string.format(
84                         "%%%02x", string.byte( chr )
85                 )
86         end
87
88         if type(str) == "string" then
89                 str = str:gsub(
90                         "([^a-zA-Z0-9$_%-%.%+!*'(),])",
91                         __chrenc
92                 )
93         end
94
95         return str
96 end
97
98
99 -- Encode given table to urlencoded string.
100 -- Returns the encoded string.
101 function urlencode_params( tbl )
102         local enc = ""
103
104         for k, v in pairs(tbl) do
105                 enc = enc .. ( enc and "&" or "" ) ..
106                         urlencode(k) .. "="  ..
107                         urlencode(v)
108         end
109
110         return enc
111 end
112
113
114 -- Parameter helper
115 local function __initval( tbl, key )
116         local multival = ( key:sub( #key - 1, #key ) == "[]" )
117
118         if multival then
119                 if type(tbl[key]) == "table" then
120                         table.insert( tbl[key], "" )
121                 else
122                         tbl[key] = { "" }
123                 end
124         else
125                 tbl[key] = ""
126         end
127
128         return multival
129 end
130
131 local function __appendval( tbl, key, multival, chunk )
132         if multival then
133                 tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
134         else
135                 tbl[key] = tbl[key] .. chunk
136         end
137 end
138
139 local function __finishval( tbl, key, multival, handler )
140         if handler then
141                 if multival then
142                         tbl[key][#tbl[key]] = handler( tbl[key][#tbl[key]] )
143                 else
144                         tbl[key] = handler( tbl[key] )
145                 end
146         end
147 end
148
149
150 -- Table of our process states
151 local process_states = { }
152
153 -- Extract "magic", the first line of a http message.
154 -- Extracts the message type ("get", "post" or "response"), the requested uri
155 -- or the status code if the line descripes a http response.
156 process_states['magic'] = function( msg, chunk, err )
157
158         if chunk ~= nil then
159                 -- ignore empty lines before request
160                 if #chunk == 0 then
161                         return true, nil
162                 end
163
164                 -- Is it a request?
165                 local method, uri, http_ver = chunk:match("^([A-Z]+) ([^ ]+) HTTP/([01]%.[019])$")
166
167                 -- Yup, it is
168                 if method then
169
170                         msg.type           = "request"
171                         msg.request_method = method:lower()
172                         msg.request_uri    = uri
173                         msg.http_version   = tonumber( http_ver )
174                         msg.headers        = { }
175
176                         -- We're done, next state is header parsing
177                         return true, function( chunk )
178                                 return process_states['headers']( msg, chunk )
179                         end
180
181                 -- Is it a response?
182                 else
183
184                         local http_ver, code, message = chunk:match("^HTTP/([01]%.[019]) ([0-9]+) ([^\r\n]+)$")
185
186                         -- Is a response
187                         if code then
188
189                                 msg.type           = "response"
190                                 msg.status_code    = code
191                                 msg.status_message = message
192                                 msg.http_version   = tonumber( http_ver )
193                                 msg.headers        = { }
194
195                                 -- We're done, next state is header parsing
196                                 return true, function( chunk )
197                                         return process_states['headers']( msg, chunk )
198                                 end
199                         end
200                 end
201         end
202
203         -- Can't handle it
204         return nil, "Invalid HTTP message magic"
205 end
206
207
208 -- Extract headers from given string.
209 process_states['headers'] = function( msg, chunk )
210
211         if chunk ~= nil then
212
213                 -- Look for a valid header format
214                 local hdr, val = chunk:match( "^([A-Z][A-Za-z0-9%-_]+): +(.+)$" )
215
216                 if type(hdr) == "string" and hdr:len() > 0 and
217                    type(val) == "string" and val:len() > 0
218                 then
219                         msg.headers[hdr] = val
220
221                         -- Valid header line, proceed
222                         return true, nil
223
224                 elseif #chunk == 0 then
225                         -- Empty line, we won't accept data anymore
226                         return false, nil
227                 else
228                         -- Junk data
229                         return nil, "Invalid HTTP header received"
230                 end
231         else
232                 return nil, "Unexpected EOF"
233         end
234 end
235
236
237 -- Find first MIME boundary
238 process_states['mime-init'] = function( msg, chunk, filecb )
239
240         if chunk ~= nil then
241                 if #chunk >= #msg.mime_boundary + 2 then
242                         local boundary = chunk:sub( 1, #msg.mime_boundary + 4 )
243
244                         if boundary == "--" .. msg.mime_boundary .. "\r\n" then
245
246                                 -- Store remaining data in buffer
247                                 msg._mimebuffer = chunk:sub( #msg.mime_boundary + 5, #chunk )
248
249                                 -- Switch to header processing state
250                                 return true, function( chunk )
251                                         return process_states['mime-headers']( msg, chunk, filecb )
252                                 end
253                         else
254                                 return nil, "Invalid MIME boundary"
255                         end
256                 else
257                         return true
258                 end
259         else
260                 return nil, "Unexpected EOF"
261         end
262 end
263
264
265 -- Read MIME part headers
266 process_states['mime-headers'] = function( msg, chunk, filecb )
267
268         if chunk ~= nil then
269
270                 -- Combine look-behind buffer with current chunk
271                 chunk = msg._mimebuffer .. chunk
272
273                 if not msg._mimeheaders then
274                         msg._mimeheaders = { }
275                 end
276
277                 local function __storehdr( k, v )
278                         msg._mimeheaders[k] = v
279                         return ""
280                 end
281
282                 -- Read all header lines
283                 local ok, count = 1, 0
284                 while ok > 0 do
285                         chunk, ok = chunk:gsub( "^([A-Z][A-Za-z0-9%-_]+): +([^\r\n]+)\r\n", __storehdr )
286                         count = count + ok
287                 end
288
289                 -- Headers processed, check for empty line
290                 chunk, ok = chunk:gsub( "^\r\n", "" )
291
292                 -- Store remaining buffer contents
293                 msg._mimebuffer = chunk
294
295                 -- End of headers
296                 if ok > 0 then
297
298                         -- When no Content-Type header is given assume text/plain
299                         if not msg._mimeheaders['Content-Type'] then
300                                 msg._mimeheaders['Content-Type'] = 'text/plain'
301                         end
302
303                         -- Check Content-Disposition
304                         if msg._mimeheaders['Content-Disposition'] then
305                                 -- Check for "form-data" token
306                                 if msg._mimeheaders['Content-Disposition']:match("^form%-data; ") then
307                                         -- Check for field name, filename
308                                         local field = msg._mimeheaders['Content-Disposition']:match('name="(.-)"')
309                                         local file  = msg._mimeheaders['Content-Disposition']:match('filename="(.+)"$')
310
311                                         -- Is a file field and we have a callback
312                                         if file and filecb then
313                                                 msg.params[field] = file
314                                                 msg._mimecallback = function(chunk,eof)
315                                                         filecb( {
316                                                                 name    = field;
317                                                                 file    = file;
318                                                                 headers = msg._mimeheaders
319                                                         }, chunk, eof )
320                                                 end
321
322                                         -- Treat as form field
323                                         else
324                                                 local mv = __initval( msg.params, field )
325                                                 msg._mimecallback = function(chunk,eof)
326                                                         __appendval( msg.params, field, mv, chunk )
327                                                 end
328                                         end
329
330                                         -- Header was valid, continue with mime-data
331                                         return true, function( chunk )
332                                                 return process_states['mime-data']( msg, chunk, filecb )
333                                         end
334                                 else
335                                         -- Unknown Content-Disposition, abort
336                                         return nil, "Unexpected Content-Disposition MIME section header"
337                                 end
338                         else
339                                 -- Content-Disposition is required, abort without
340                                 return nil, "Missing Content-Disposition MIME section header"
341                         end
342
343                 -- We parsed no headers yet and buffer is almost empty
344                 elseif count > 0 or #chunk < 128 then
345                         -- Keep feeding me with chunks
346                         return true, nil
347                 end
348
349                 -- Buffer looks like garbage
350                 return nil, "Malformed MIME section header"
351         else
352                 return nil, "Unexpected EOF"
353         end
354 end
355
356
357 -- Read MIME part data
358 process_states['mime-data'] = function( msg, chunk, filecb )
359
360         if chunk ~= nil then
361
362                 -- Combine look-behind buffer with current chunk
363                 local buffer = msg._mimebuffer .. chunk
364
365                 -- Look for MIME boundary
366                 local spos, epos = buffer:find( "\r\n--" .. msg.mime_boundary .. "\r\n", 1, true )
367
368                 if spos then
369                         -- Content data
370                         msg._mimecallback( buffer:sub( 1, spos - 1 ), true )
371
372                         -- Store remainder
373                         msg._mimebuffer = buffer:sub( epos + 1, #buffer )
374
375                         -- Next state is mime-header processing
376                         return true, function( chunk )
377                                 return process_states['mime-headers']( msg, chunk, filecb )
378                         end
379                 else
380                         -- Look for EOF?
381                         local spos, epos = buffer:find( "\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true )
382
383                         if spos then
384                                 -- Content data
385                                 msg._mimecallback( buffer:sub( 1, spos - 1 ), true )
386
387                                 -- We processed the final MIME boundary, cleanup
388                                 msg._mimebuffer   = nil
389                                 msg._mimeheaders  = nil
390                                 msg._mimecallback = nil
391
392                                 -- We won't accept data anymore
393                                 return false
394                         else
395                                 -- We're somewhere within a data section and our buffer is full
396                                 if #buffer > #chunk then
397                                         -- Flush buffered data
398                                         msg._mimecallback( buffer:sub( 1, #buffer - #chunk ), false )
399
400                                         -- Store new data
401                                         msg._mimebuffer = buffer:sub( #buffer - #chunk + 1, #buffer )
402
403                                 -- Buffer is not full yet, append new data
404                                 else
405                                         msg._mimebuffer = buffer
406                                 end
407
408                                 -- Keep feeding me
409                                 return true
410                         end
411                 end
412         else
413                 return nil, "Unexpected EOF"
414         end
415 end
416
417
418 -- Init urldecoding stream
419 process_states['urldecode-init'] = function( msg, chunk, filecb )
420
421         if chunk ~= nil then
422
423                 -- Check for Content-Length
424                 if msg.env.CONTENT_LENGTH then
425                         msg.content_length = tonumber(msg.env.CONTENT_LENGTH)
426
427                         if msg.content_length <= HTTP_MAX_CONTENT then
428                                 -- Initialize buffer
429                                 msg._urldecbuffer = chunk
430                                 msg._urldeclength = 0
431
432                                 -- Switch to urldecode-key state
433                                 return true, function(chunk)
434                                         return process_states['urldecode-key']( msg, chunk, filecb )
435                                 end
436                         else
437                                 return nil, "Request exceeds maximum allowed size"
438                         end
439                 else
440                         return nil, "Missing Content-Length header"
441                 end
442         else
443                 return nil, "Unexpected EOF"
444         end
445 end
446
447
448 -- Process urldecoding stream, read and validate parameter key
449 process_states['urldecode-key'] = function( msg, chunk, filecb )
450         if chunk ~= nil then
451
452                 -- Prevent oversized requests
453                 if msg._urldeclength >= msg.content_length then
454                         return nil, "Request exceeds maximum allowed size"
455                 end
456
457                 -- Combine look-behind buffer with current chunk
458                 local buffer = msg._urldecbuffer .. chunk
459                 local spos, epos = buffer:find("=")
460
461                 -- Found param
462                 if spos then
463
464                         -- Check that key doesn't exceed maximum allowed key length
465                         if ( spos - 1 ) <= HTTP_URLENC_MAXKEYLEN then
466                                 local key = urldecode( buffer:sub( 1, spos - 1 ) )
467
468                                 -- Prepare buffers
469                                 msg._urldeclength   = msg._urldeclength + epos
470                                 msg._urldecbuffer   = buffer:sub( epos + 1, #buffer )
471
472                                 -- Use file callback or store values inside msg.params
473                                 if filecb then
474                                         msg._urldeccallback = function( chunk, eof )
475                                                 filecb( field, chunk, eof )
476                                         end
477                                 else
478                                         local mv = __initval( msg.params, key )
479                                         msg._urldeccallback = function( chunk, eof )
480                                                 __appendval( msg.params, key, mv, chunk )
481
482                                                 -- FIXME: Use a filter
483                                                 if eof then
484                                                         __finishval( msg.params, key, mv, urldecode )
485                                                 end
486                                         end
487                                 end
488
489                                 -- Proceed with urldecode-value state
490                                 return true, function( chunk )
491                                         return process_states['urldecode-value']( msg, chunk, filecb )
492                                 end
493                         else
494                                 return nil, "POST parameter exceeds maximum allowed length"
495                         end
496                 else
497                         return nil, "POST data exceeds maximum allowed length"
498                 end
499         else
500                 return nil, "Unexpected EOF"
501         end
502 end
503
504
505 -- Process urldecoding stream, read parameter value
506 process_states['urldecode-value'] = function( msg, chunk, filecb )
507
508         if chunk ~= nil then
509
510                 -- Combine look-behind buffer with current chunk
511                 local buffer = msg._urldecbuffer .. chunk
512
513                 -- Check for EOF
514                 if #buffer == 0 then
515                         -- Compare processed length
516                         if msg._urldeclength == msg.content_length then
517                                 -- Cleanup
518                                 msg._urldeclength   = nil
519                                 msg._urldecbuffer   = nil
520                                 msg._urldeccallback = nil
521
522                                 -- We won't accept data anymore
523                                 return false
524                         else
525                                 return nil, "Content-Length mismatch"
526                         end
527                 end
528
529                 -- Check for end of value
530                 local spos, epos = buffer:find("[&;]")
531                 if spos then
532
533                         -- Flush buffer, send eof
534                         msg._urldeccallback( buffer:sub( 1, spos - 1 ), true )
535                         msg._urldecbuffer = buffer:sub( epos + 1, #buffer )
536                         msg._urldeclength = msg._urldeclength + epos
537
538                         -- Back to urldecode-key state
539                         return true, function( chunk )
540                                 return process_states['urldecode-key']( msg, chunk, filecb )
541                         end
542                 else
543                         -- We're somewhere within a data section and our buffer is full
544                         if #buffer > #chunk then
545                                 -- Flush buffered data
546                                 msg._urldeccallback( buffer:sub( 1, #buffer - #chunk ), false )
547
548                                 -- Store new data
549                                 msg._urldeclength = msg._urldeclength + #buffer - #chunk
550                                 msg._urldecbuffer = buffer:sub( #buffer - #chunk + 1, #buffer )
551
552                         -- Buffer is not full yet, append new data
553                         else
554                                 msg._urldecbuffer = buffer
555                         end
556
557                         -- Keep feeding me
558                         return true
559                 end
560         else
561                 -- Send EOF
562                 msg._urldeccallback( "", true )
563                 return false
564         end
565 end
566
567
568 -- Creates a header source from a given socket
569 function header_source( sock )
570         return ltn12.source.simplify( function()
571
572                 local chunk, err, part = sock:receive("*l")
573
574                 -- Line too long
575                 if chunk == nil then
576                         if err ~= "timeout" then
577                                 return nil, part
578                                         and "Line exceeds maximum allowed length"
579                                         or  "Unexpected EOF"
580                         else
581                                 return nil, err
582                         end
583
584                 -- Line ok
585                 elseif chunk ~= nil then
586
587                         -- Strip trailing CR
588                         chunk = chunk:gsub("\r$","")
589
590                         return chunk, nil
591                 end
592         end )
593 end
594
595
596 -- Decode MIME encoded data.
597 function mimedecode_message_body( source, msg, filecb )
598
599         -- Find mime boundary
600         if msg and msg.env.CONTENT_TYPE then
601
602                 local bound = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)")
603
604                 if bound then
605                         msg.mime_boundary = bound
606                 else
607                         return nil, "No MIME boundary found or invalid content type given"
608                 end
609         end
610
611         -- Create an initial LTN12 sink
612         -- The whole MIME parsing process is implemented as fancy sink, sinks replace themself
613         -- depending on current processing state (init, header, data). Return the initial state.
614         local sink = ltn12.sink.simplify(
615                 function( chunk )
616                         return process_states['mime-init']( msg, chunk, filecb )
617                 end
618         )
619
620         -- Create a throttling LTN12 source
621         -- Frequent state switching in the mime parsing process leads to unwanted buffer aggregation.
622         -- This source checks wheather there's still data in our internal read buffer and returns an
623         -- empty string if there's already enough data in the processing queue. If the internal buffer
624         -- runs empty we're calling the original source to get the next chunk of data.
625         local tsrc = function()
626
627                 -- XXX: we schould propably keep the maximum buffer size in sync with
628                 --      the blocksize of our original source... but doesn't really matter
629                 if msg._mimebuffer ~= null and #msg._mimebuffer > 256 then
630                         return ""
631                 else
632                         return source()
633                 end
634         end
635
636         -- Pump input data...
637         while true do
638                 -- get data
639                 local ok, err = ltn12.pump.step( tsrc, sink )
640
641                 -- error
642                 if not ok and err then
643                         return nil, err
644
645                 -- eof
646                 elseif not ok then
647                         return true
648                 end
649         end
650 end
651
652
653 -- Decode urlencoded data.
654 function urldecode_message_body( source, msg )
655
656         -- Create an initial LTN12 sink
657         -- Return the initial state.
658         local sink = ltn12.sink.simplify(
659                 function( chunk )
660                         return process_states['urldecode-init']( msg, chunk )
661                 end
662         )
663
664         -- Create a throttling LTN12 source
665         -- See explaination in mimedecode_message_body().
666         local tsrc = function()
667                 if msg._urldecbuffer ~= null and #msg._urldecbuffer > 0 then
668                         return ""
669                 else
670                         return source()
671                 end
672         end
673
674         -- Pump input data...
675         while true do
676                 -- get data
677                 local ok, err = ltn12.pump.step( tsrc, sink )
678
679                 -- step
680                 if not ok and err then
681                         return nil, err
682
683                 -- eof
684                 elseif not ok then
685                         return true
686                 end
687         end
688 end
689
690
691 -- Parse a http message header
692 function parse_message_header( source )
693
694         local ok   = true
695         local msg  = { }
696
697         local sink = ltn12.sink.simplify(
698                 function( chunk )
699                         return process_states['magic']( msg, chunk )
700                 end
701         )
702
703         -- Pump input data...
704         while ok do
705
706                 -- get data
707                 ok, err = ltn12.pump.step( source, sink )
708
709                 -- error
710                 if not ok and err then
711                         return nil, err
712
713                 -- eof
714                 elseif not ok then
715
716                         -- Process get parameters
717                         if ( msg.request_method == "get" or msg.request_method == "post" ) and
718                            msg.request_uri:match("?")
719                         then
720                                 msg.params = urldecode_params( msg.request_uri )
721                         else
722                                 msg.params = { }
723                         end
724
725                         -- Populate common environment variables
726                         msg.env = {
727                                 CONTENT_LENGTH    = msg.headers['Content-Length'];
728                                 CONTENT_TYPE      = msg.headers['Content-Type'];
729                                 REQUEST_METHOD    = msg.request_method:upper();
730                                 REQUEST_URI       = msg.request_uri;
731                                 SCRIPT_NAME       = msg.request_uri:gsub("?.+$","");
732                                 SCRIPT_FILENAME   = "";         -- XXX implement me
733                                 SERVER_PROTOCOL   = "HTTP/" .. string.format("%.1f", msg.http_version)
734                         }
735
736                         -- Populate HTTP_* environment variables
737                         for i, hdr in ipairs( {
738                                 'Accept',
739                                 'Accept-Charset',
740                                 'Accept-Encoding',
741                                 'Accept-Language',
742                                 'Connection',
743                                 'Cookie',
744                                 'Host',
745                                 'Referer',
746                                 'User-Agent',
747                         } ) do
748                                 local var = 'HTTP_' .. hdr:upper():gsub("%-","_")
749                                 local val = msg.headers[hdr]
750
751                                 msg.env[var] = val
752                         end
753                 end
754         end
755
756         return msg
757 end
758
759
760 -- Parse a http message body
761 function parse_message_body( source, msg, filecb )
762         -- Is it multipart/mime ?
763         if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
764            msg.env.CONTENT_TYPE:match("^multipart/form%-data")
765         then
766
767                 return mimedecode_message_body( source, msg, filecb )
768
769         -- Is it application/x-www-form-urlencoded ?
770         elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
771                msg.env.CONTENT_TYPE == "application/x-www-form-urlencoded"
772         then
773                 return urldecode_message_body( source, msg, filecb )
774
775
776         -- Unhandled encoding
777         -- If a file callback is given then feed it chunk by chunk, else
778         -- store whole buffer in message.content
779         else
780
781                 local sink
782
783                 -- If we have a file callback then feed it
784                 if type(filecb) == "function" then
785                         sink = filecb
786
787                 -- ... else append to .content
788                 else
789                         msg.content = ""
790                         msg.content_length = 0
791
792                         sink = function( chunk )
793                                 if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then
794
795                                         msg.content        = msg.content        .. chunk
796                                         msg.content_length = msg.content_length + #chunk
797
798                                         return true
799                                 else
800                                         return nil, "POST data exceeds maximum allowed length"
801                                 end
802                         end
803                 end
804
805                 -- Pump data...
806                 while true do
807                         local ok, err = ltn12.pump.step( source, sink )
808
809                         if not ok and err then
810                                 return nil, err
811                         elseif not err then
812                                 return true
813                         end
814                 end
815         end
816 end
817
818 -- Status codes
819 statusmsg = {
820         [200] = "OK",
821         [301] = "Moved Permanently",
822         [304] = "Not Modified",
823         [400] = "Bad Request",
824         [403] = "Forbidden",
825         [404] = "Not Found",
826         [405] = "Method Not Allowed",
827         [411] = "Length Required",
828         [412] = "Precondition Failed",
829         [500] = "Internal Server Error",
830         [503] = "Server Unavailable",
831 }