* Fix last commit
[project/luci.git] / libs / web / luasrc / http.lua
1 --[[
2 LuCI - HTTP-Interaction
3
4 Description:
5 HTTP-Header manipulator and form variable preprocessor
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at 
16
17         http://www.apache.org/licenses/LICENSE-2.0 
18
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24
25 ]]--
26
27 --- LuCI Web Framework high-level HTTP functions.
28 module("luci.http", package.seeall)
29 local ltn12 = require("luci.ltn12")
30 require("luci.http.protocol")
31 require("luci.util")
32
33 context = luci.util.threadlocal()
34
35 Request = luci.util.class()
36 function Request.__init__(self, env, sourcein, sinkerr)
37         self.input = sourcein
38         self.error = sinkerr
39
40
41         -- File handler
42         self.filehandler = function() end
43         
44         -- HTTP-Message table
45         self.message = {
46                 env = env,
47                 headers = {},
48                 params = luci.http.protocol.urldecode_params(env.QUERY_STRING or ""),
49         }
50         
51         self.parsed_input = false
52 end
53
54 function Request.formvalue(self, name, noparse)
55         if not noparse and not self.parsed_input then
56                 self:_parse_input()
57         end
58         
59         if name then
60                 return self.message.params[name]
61         else
62                 return self.message.params
63         end
64 end
65
66 function Request.formvaluetable(self, prefix)
67         local vals = {}
68         prefix = prefix and prefix .. "." or "."
69         
70         if not self.parsed_input then
71                 self:_parse_input()
72         end
73         
74         local void = self.message.params[nil]
75         for k, v in pairs(self.message.params) do
76                 if k:find(prefix, 1, true) == 1 then
77                         vals[k:sub(#prefix + 1)] = tostring(v)
78                 end
79         end
80         
81         return vals
82 end
83
84 function Request.getcookie(self, name)
85   local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";")
86   local p = ";" .. name .. "=(.-);"
87   local i, j, value = c:find(p)
88   return value and urldecode(value)
89 end
90
91 function Request.getenv(self, name)
92         if name then
93                 return self.message.env[name]
94         else
95                 return self.message.env
96         end
97 end
98
99 function Request.setfilehandler(self, callback)
100         self.filehandler = callback
101 end
102
103 function Request._parse_input(self)
104         luci.http.protocol.parse_message_body(
105                  self.input,
106                  self.message,
107                  self.filehandler
108         )
109         self.parsed_input = true
110 end
111
112 --- Close the HTTP-Connection.
113 function close()
114         if not context.eoh then
115                 context.eoh = true
116                 coroutine.yield(3)
117         end
118         
119         if not context.closed then
120                 context.closed = true
121                 coroutine.yield(5)
122         end
123 end
124
125 --- Get a certain HTTP input value or a table of all input values.
126 -- @param name          Name of the GET or POST variable to fetch
127 -- @param noparse       Don't parse POST data before getting the value
128 -- @return                      HTTP input value or table of all input value
129 function formvalue(name, noparse)
130         return context.request:formvalue(name, noparse)
131 end
132
133 --- Get a table of all HTTP input values with a certain prefix.
134 -- @param prefix        Prefix
135 -- @return                      Table of all HTTP input values with given prefix
136 function formvaluetable(prefix)
137         return context.request:formvaluetable(prefix)
138 end
139
140 --- Get the value of a certain HTTP-Cookie.
141 -- @param name          Cookie Name
142 -- @return                      String containing cookie data
143 function getcookie(name)
144         return context.request:getcookie(name)
145 end
146
147 --- Get the value of a certain HTTP environment variable 
148 -- or the environment table itself.
149 -- @param name          Environment variable
150 -- @return                      HTTP environment value or environment table
151 function getenv(name)
152         return context.request:getenv(name)
153 end
154
155 --- Set a handler function for incoming user file uploads.
156 -- @param callback      Handler function
157 function setfilehandler(callback)
158         return context.request:setfilehandler(callback)
159 end
160
161 --- Send a HTTP-Header.
162 -- @param key   Header key
163 -- @param value Header value
164 function header(key, value)
165         if not context.headers then
166                 context.headers = {}
167         end
168         context.headers[key:lower()] = value
169         coroutine.yield(2, key, value)
170 end
171
172 --- Set the mime type of following content data.
173 -- @param mime  Mimetype of following content
174 function prepare_content(mime)
175         header("Content-Type", mime)
176 end
177
178 --- Set the HTTP status code and status message.
179 -- @param code          Status code
180 -- @param message       Status message
181 function status(code, message)
182         code = code or 200
183         message = message or "OK"
184         context.status = code
185         coroutine.yield(1, code, message)
186 end
187
188 --- Send a chunk of content data to the client.
189 -- This function is as a valid LTN12 sink.
190 -- If the content chunk is nil this function will automatically invoke close.
191 -- @param content       Content chunk
192 -- @param src_err       Error object from source (optional)
193 -- @see close
194 function write(content, src_err)
195         if not content then
196                 if src_err then
197                         error(src_err)
198                 else
199                         close()
200                 end
201                 return true
202         elseif #content == 0 then
203                 return true
204         else
205                 if not context.eoh then
206                         if not context.status then
207                                 status()
208                         end
209                         if not context.headers or not context.headers["content-type"] then
210                                 header("Content-Type", "text/html; charset=utf-8")
211                         end
212                         if not context.headers["cache-control"] then
213                                 header("Cache-Control", "no-cache")
214                         end
215                         
216                         
217                         context.eoh = true
218                         coroutine.yield(3)
219                 end
220                 coroutine.yield(4, content)
221                 return true
222         end
223 end
224
225 --- Redirects the client to a new URL and closes the connection.
226 -- @param url   Target URL
227 function redirect(url)
228         status(302, "Found")
229         header("Location", url)
230         close()
231 end
232
233 --- Create a querystring out of a table of key - value pairs.
234 -- @param table         Query string source table
235 -- @return                      Encoded HTTP query string
236 function build_querystring(table)
237         local s="?"
238         
239         for k, v in pairs(table) do
240                 s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
241         end
242         
243         return s
244 end
245
246 --- Return the URL-decoded equivalent of a string.
247 -- @param str           URL-encoded string
248 -- @param no_plus       Don't decode + to " "
249 -- @return                      URL-decoded string
250 -- @see urlencode
251 urldecode = luci.http.protocol.urldecode
252
253 --- Return the URL-encoded equivalent of a string.
254 -- @param str           Source string
255 -- @return                      URL-encoded string
256 -- @see urldecode
257 urlencode = luci.http.protocol.urlencode