diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-01-19 05:41:30 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-01-19 05:41:30 +0000 |
commit | 5b8d7dec541a618b4ca7f2205470a28cde2e3e25 (patch) | |
tree | 209ad0c80c9a938068401fc5b8fa51942972418f /src | |
parent | 6ac82d50eecdf9bf55f4234ed3a5449afd7a2992 (diff) | |
download | luasocket-5b8d7dec541a618b4ca7f2205470a28cde2e3e25.tar.gz luasocket-5b8d7dec541a618b4ca7f2205470a28cde2e3e25.tar.bz2 luasocket-5b8d7dec541a618b4ca7f2205470a28cde2e3e25.zip |
Updated some of the callbacks in callback.lua.
Update get.lua to use the new callbacks.
The old "code" module is now the "mime" module.
Updated all modules that depended on it.
Updated url.lua to use the new namespace scheme, and moved the
escape and unescape functions that used to be in the code.lua module
to it, since these are specific to urls.
Updated the callback entries in the manual.
Diffstat (limited to 'src')
-rw-r--r-- | src/http.lua | 25 | ||||
-rw-r--r-- | src/inet.c | 2 | ||||
-rw-r--r-- | src/luasocket.c | 8 | ||||
-rw-r--r-- | src/mime.c | 614 | ||||
-rw-r--r-- | src/mime.h | 17 | ||||
-rw-r--r-- | src/mime.lua | 104 | ||||
-rw-r--r-- | src/tcp.c | 2 | ||||
-rw-r--r-- | src/url.lua | 179 |
8 files changed, 864 insertions, 87 deletions
diff --git a/src/http.lua b/src/http.lua index fb13d99..72bde0a 100644 --- a/src/http.lua +++ b/src/http.lua | |||
@@ -421,7 +421,7 @@ end | |||
421 | ----------------------------------------------------------------------------- | 421 | ----------------------------------------------------------------------------- |
422 | local function authorize(reqt, parsed, respt) | 422 | local function authorize(reqt, parsed, respt) |
423 | reqt.headers["authorization"] = "Basic " .. | 423 | reqt.headers["authorization"] = "Basic " .. |
424 | (socket.code.b64(parsed.user .. ":" .. parsed.password)) | 424 | (socket.mime.b64(parsed.user .. ":" .. parsed.password)) |
425 | local autht = { | 425 | local autht = { |
426 | nredirects = reqt.nredirects, | 426 | nredirects = reqt.nredirects, |
427 | method = reqt.method, | 427 | method = reqt.method, |
@@ -429,8 +429,8 @@ local function authorize(reqt, parsed, respt) | |||
429 | body_cb = reqt.body_cb, | 429 | body_cb = reqt.body_cb, |
430 | headers = reqt.headers, | 430 | headers = reqt.headers, |
431 | timeout = reqt.timeout, | 431 | timeout = reqt.timeout, |
432 | host = reqt.host, | 432 | proxyhost = reqt.proxyhost, |
433 | port = reqt.port | 433 | proxyport = reqt.proxyport |
434 | } | 434 | } |
435 | return request_cb(autht, respt) | 435 | return request_cb(autht, respt) |
436 | end | 436 | end |
@@ -471,8 +471,8 @@ local function redirect(reqt, respt) | |||
471 | body_cb = reqt.body_cb, | 471 | body_cb = reqt.body_cb, |
472 | headers = reqt.headers, | 472 | headers = reqt.headers, |
473 | timeout = reqt.timeout, | 473 | timeout = reqt.timeout, |
474 | host = reqt.host, | 474 | proxyhost = reqt.proxyhost, |
475 | port = reqt.port | 475 | proxyport = reqt.proxyport |
476 | } | 476 | } |
477 | respt = request_cb(redirt, respt) | 477 | respt = request_cb(redirt, respt) |
478 | -- we pass the location header as a clue we tried to redirect | 478 | -- we pass the location header as a clue we tried to redirect |
@@ -482,8 +482,8 @@ end | |||
482 | 482 | ||
483 | ----------------------------------------------------------------------------- | 483 | ----------------------------------------------------------------------------- |
484 | -- Computes the request URI from the parsed request URL | 484 | -- Computes the request URI from the parsed request URL |
485 | -- If host and port are given in the request table, we use he | 485 | -- If we are using a proxy, we use the absoluteURI format. |
486 | -- absoluteURI format. Otherwise, we use the abs_path format. | 486 | -- Otherwise, we use the abs_path format. |
487 | -- Input | 487 | -- Input |
488 | -- parsed: parsed URL | 488 | -- parsed: parsed URL |
489 | -- Returns | 489 | -- Returns |
@@ -491,7 +491,7 @@ end | |||
491 | ----------------------------------------------------------------------------- | 491 | ----------------------------------------------------------------------------- |
492 | local function request_uri(reqt, parsed) | 492 | local function request_uri(reqt, parsed) |
493 | local url | 493 | local url |
494 | if not reqt.host and not reqt.port then | 494 | if not reqt.proxyhost and not reqt.proxyport then |
495 | url = { | 495 | url = { |
496 | path = parsed.path, | 496 | path = parsed.path, |
497 | params = parsed.params, | 497 | params = parsed.params, |
@@ -543,6 +543,7 @@ end | |||
543 | -- error: error message, or nil if successfull | 543 | -- error: error message, or nil if successfull |
544 | ----------------------------------------------------------------------------- | 544 | ----------------------------------------------------------------------------- |
545 | function request_cb(reqt, respt) | 545 | function request_cb(reqt, respt) |
546 | local sock, ret | ||
546 | local parsed = socket.url.parse(reqt.url, { | 547 | local parsed = socket.url.parse(reqt.url, { |
547 | host = "", | 548 | host = "", |
548 | port = PORT, | 549 | port = PORT, |
@@ -561,14 +562,14 @@ function request_cb(reqt, respt) | |||
561 | -- fill default headers | 562 | -- fill default headers |
562 | reqt.headers = fill_headers(reqt.headers, parsed) | 563 | reqt.headers = fill_headers(reqt.headers, parsed) |
563 | -- try to connect to server | 564 | -- try to connect to server |
564 | local sock | ||
565 | sock, respt.error = socket.tcp() | 565 | sock, respt.error = socket.tcp() |
566 | if not sock then return respt end | 566 | if not sock then return respt end |
567 | -- set connection timeout so that we do not hang forever | 567 | -- set connection timeout so that we do not hang forever |
568 | sock:settimeout(reqt.timeout or TIMEOUT) | 568 | sock:settimeout(reqt.timeout or TIMEOUT) |
569 | local ret | 569 | ret, respt.error = sock:connect( |
570 | ret, respt.error = sock:connect(reqt.host or parsed.host, | 570 | reqt.proxyhost or PROXYHOST or parsed.host, |
571 | reqt.port or parsed.port) | 571 | reqt.proxyport or PROXYPORT or parsed.port |
572 | ) | ||
572 | if not ret then | 573 | if not ret then |
573 | sock:close() | 574 | sock:close() |
574 | return respt | 575 | return respt |
@@ -234,7 +234,7 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port, | |||
234 | return sock_bindstrerror(); | 234 | return sock_bindstrerror(); |
235 | } else { | 235 | } else { |
236 | sock_setnonblocking(ps); | 236 | sock_setnonblocking(ps); |
237 | if (backlog > 0) sock_listen(ps, backlog); | 237 | if (backlog >= 0) sock_listen(ps, backlog); |
238 | return NULL; | 238 | return NULL; |
239 | } | 239 | } |
240 | } | 240 | } |
diff --git a/src/luasocket.c b/src/luasocket.c index 8b30f4d..578d65c 100644 --- a/src/luasocket.c +++ b/src/luasocket.c | |||
@@ -33,7 +33,7 @@ | |||
33 | #include "tcp.h" | 33 | #include "tcp.h" |
34 | #include "udp.h" | 34 | #include "udp.h" |
35 | #include "select.h" | 35 | #include "select.h" |
36 | #include "code.h" | 36 | #include "mime.h" |
37 | 37 | ||
38 | /*=========================================================================*\ | 38 | /*=========================================================================*\ |
39 | * Exported functions | 39 | * Exported functions |
@@ -52,13 +52,13 @@ LUASOCKET_API int luaopen_socket(lua_State *L) | |||
52 | tcp_open(L); | 52 | tcp_open(L); |
53 | udp_open(L); | 53 | udp_open(L); |
54 | select_open(L); | 54 | select_open(L); |
55 | code_open(L); | 55 | mime_open(L); |
56 | #ifdef LUASOCKET_COMPILED | 56 | #ifdef LUASOCKET_COMPILED |
57 | #include "auxiliar.lch" | 57 | #include "auxiliar.lch" |
58 | #include "concat.lch" | 58 | #include "concat.lch" |
59 | #include "url.lch" | 59 | #include "url.lch" |
60 | #include "callback.lch" | 60 | #include "callback.lch" |
61 | #include "code.lch" | 61 | #include "mime.lch" |
62 | #include "smtp.lch" | 62 | #include "smtp.lch" |
63 | #include "ftp.lch" | 63 | #include "ftp.lch" |
64 | #include "http.lch" | 64 | #include "http.lch" |
@@ -67,7 +67,7 @@ LUASOCKET_API int luaopen_socket(lua_State *L) | |||
67 | lua_dofile(L, "concat.lua"); | 67 | lua_dofile(L, "concat.lua"); |
68 | lua_dofile(L, "url.lua"); | 68 | lua_dofile(L, "url.lua"); |
69 | lua_dofile(L, "callback.lua"); | 69 | lua_dofile(L, "callback.lua"); |
70 | lua_dofile(L, "code.lua"); | 70 | lua_dofile(L, "mime.lua"); |
71 | lua_dofile(L, "smtp.lua"); | 71 | lua_dofile(L, "smtp.lua"); |
72 | lua_dofile(L, "ftp.lua"); | 72 | lua_dofile(L, "ftp.lua"); |
73 | lua_dofile(L, "http.lua"); | 73 | lua_dofile(L, "http.lua"); |
diff --git a/src/mime.c b/src/mime.c new file mode 100644 index 0000000..6807af5 --- /dev/null +++ b/src/mime.c | |||
@@ -0,0 +1,614 @@ | |||
1 | /*=========================================================================*\ | ||
2 | * Encoding support functions | ||
3 | * LuaSocket toolkit | ||
4 | * | ||
5 | * RCS ID: $Id$ | ||
6 | \*=========================================================================*/ | ||
7 | #include <string.h> | ||
8 | |||
9 | #include <lua.h> | ||
10 | #include <lauxlib.h> | ||
11 | |||
12 | #include "luasocket.h" | ||
13 | #include "mime.h" | ||
14 | |||
15 | /*=========================================================================*\ | ||
16 | * Don't want to trust escape character constants | ||
17 | \*=========================================================================*/ | ||
18 | #define CR 0x0D | ||
19 | #define LF 0x0A | ||
20 | #define HT 0x09 | ||
21 | #define SP 0x20 | ||
22 | |||
23 | typedef unsigned char UC; | ||
24 | static const UC CRLF[2] = {CR, LF}; | ||
25 | static const UC EQCRLF[3] = {'=', CR, LF}; | ||
26 | |||
27 | /*=========================================================================*\ | ||
28 | * Internal function prototypes. | ||
29 | \*=========================================================================*/ | ||
30 | static int mime_global_fmt(lua_State *L); | ||
31 | static int mime_global_b64(lua_State *L); | ||
32 | static int mime_global_unb64(lua_State *L); | ||
33 | static int mime_global_qp(lua_State *L); | ||
34 | static int mime_global_unqp(lua_State *L); | ||
35 | static int mime_global_qpfmt(lua_State *L); | ||
36 | static int mime_global_eol(lua_State *L); | ||
37 | |||
38 | static void b64fill(UC *b64unbase); | ||
39 | static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer); | ||
40 | static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer); | ||
41 | static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer); | ||
42 | |||
43 | static void qpfill(UC *qpclass, UC *qpunbase); | ||
44 | static void qpquote(UC c, luaL_Buffer *buffer); | ||
45 | static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer); | ||
46 | static size_t qpencode(UC c, UC *input, size_t size, | ||
47 | const UC *marker, luaL_Buffer *buffer); | ||
48 | |||
49 | /* code support functions */ | ||
50 | static luaL_reg func[] = { | ||
51 | { "eol", mime_global_eol }, | ||
52 | { "qp", mime_global_qp }, | ||
53 | { "unqp", mime_global_unqp }, | ||
54 | { "qpfmt", mime_global_qpfmt }, | ||
55 | { "b64", mime_global_b64 }, | ||
56 | { "unb64", mime_global_unb64 }, | ||
57 | { "fmt", mime_global_fmt }, | ||
58 | { NULL, NULL } | ||
59 | }; | ||
60 | |||
61 | /*-------------------------------------------------------------------------*\ | ||
62 | * Quoted-printable globals | ||
63 | \*-------------------------------------------------------------------------*/ | ||
64 | static UC qpclass[256]; | ||
65 | static UC qpbase[] = "0123456789ABCDEF"; | ||
66 | static UC qpunbase[256]; | ||
67 | enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST}; | ||
68 | |||
69 | /*-------------------------------------------------------------------------*\ | ||
70 | * Base64 globals | ||
71 | \*-------------------------------------------------------------------------*/ | ||
72 | static const UC b64base[] = | ||
73 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
74 | static UC b64unbase[256]; | ||
75 | |||
76 | /*=========================================================================*\ | ||
77 | * Exported functions | ||
78 | \*=========================================================================*/ | ||
79 | /*-------------------------------------------------------------------------*\ | ||
80 | * Initializes module | ||
81 | \*-------------------------------------------------------------------------*/ | ||
82 | void mime_open(lua_State *L) | ||
83 | { | ||
84 | lua_pushstring(L, LUASOCKET_LIBNAME); | ||
85 | lua_gettable(L, LUA_GLOBALSINDEX); | ||
86 | if (lua_isnil(L, -1)) { | ||
87 | lua_pop(L, 1); | ||
88 | lua_newtable(L); | ||
89 | lua_pushstring(L, LUASOCKET_LIBNAME); | ||
90 | lua_pushvalue(L, -2); | ||
91 | lua_settable(L, LUA_GLOBALSINDEX); | ||
92 | } | ||
93 | lua_pushstring(L, "mime"); | ||
94 | lua_newtable(L); | ||
95 | luaL_openlib(L, NULL, func, 0); | ||
96 | lua_settable(L, -3); | ||
97 | lua_pop(L, 1); | ||
98 | /* initialize lookup tables */ | ||
99 | qpfill(qpclass, qpunbase); | ||
100 | b64fill(b64unbase); | ||
101 | } | ||
102 | |||
103 | /*=========================================================================*\ | ||
104 | * Global Lua functions | ||
105 | \*=========================================================================*/ | ||
106 | /*-------------------------------------------------------------------------*\ | ||
107 | * Incrementaly breaks a string into lines | ||
108 | * A, n = fmt(B, length, left) | ||
109 | * A is a copy of B, broken into lines of at most 'length' bytes. | ||
110 | * Left is how many bytes are left in the first line of B. 'n' is the number | ||
111 | * of bytes left in the last line of A. | ||
112 | \*-------------------------------------------------------------------------*/ | ||
113 | static int mime_global_fmt(lua_State *L) | ||
114 | { | ||
115 | size_t size = 0; | ||
116 | const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &size); | ||
117 | const UC *last = input + size; | ||
118 | int length = (int) luaL_checknumber(L, 2); | ||
119 | int left = (int) luaL_optnumber(L, 3, length); | ||
120 | const UC *marker = luaL_optstring(L, 4, CRLF); | ||
121 | luaL_Buffer buffer; | ||
122 | luaL_buffinit(L, &buffer); | ||
123 | while (input < last) { | ||
124 | luaL_putchar(&buffer, *input++); | ||
125 | if (--left <= 0) { | ||
126 | luaL_addstring(&buffer, marker); | ||
127 | left = length; | ||
128 | } | ||
129 | } | ||
130 | if (!input && left < length) { | ||
131 | luaL_addstring(&buffer, marker); | ||
132 | left = length; | ||
133 | } | ||
134 | luaL_pushresult(&buffer); | ||
135 | lua_pushnumber(L, left); | ||
136 | return 2; | ||
137 | } | ||
138 | |||
139 | /*-------------------------------------------------------------------------*\ | ||
140 | * Fill base64 decode map. | ||
141 | \*-------------------------------------------------------------------------*/ | ||
142 | static void b64fill(UC *b64unbase) | ||
143 | { | ||
144 | int i; | ||
145 | for (i = 0; i < 255; i++) b64unbase[i] = 255; | ||
146 | for (i = 0; i < 64; i++) b64unbase[b64base[i]] = i; | ||
147 | b64unbase['='] = 0; | ||
148 | } | ||
149 | |||
150 | /*-------------------------------------------------------------------------*\ | ||
151 | * Acumulates bytes in input buffer until 3 bytes are available. | ||
152 | * Translate the 3 bytes into Base64 form and append to buffer. | ||
153 | * Returns new number of bytes in buffer. | ||
154 | \*-------------------------------------------------------------------------*/ | ||
155 | static size_t b64encode(UC c, UC *input, size_t size, | ||
156 | luaL_Buffer *buffer) | ||
157 | { | ||
158 | input[size++] = c; | ||
159 | if (size == 3) { | ||
160 | UC code[4]; | ||
161 | unsigned long value = 0; | ||
162 | value += input[0]; value <<= 8; | ||
163 | value += input[1]; value <<= 8; | ||
164 | value += input[2]; | ||
165 | code[3] = b64base[value & 0x3f]; value >>= 6; | ||
166 | code[2] = b64base[value & 0x3f]; value >>= 6; | ||
167 | code[1] = b64base[value & 0x3f]; value >>= 6; | ||
168 | code[0] = b64base[value]; | ||
169 | luaL_addlstring(buffer, code, 4); | ||
170 | size = 0; | ||
171 | } | ||
172 | return size; | ||
173 | } | ||
174 | |||
175 | /*-------------------------------------------------------------------------*\ | ||
176 | * Encodes the Base64 last 1 or 2 bytes and adds padding '=' | ||
177 | * Result, if any, is appended to buffer. | ||
178 | * Returns 0. | ||
179 | \*-------------------------------------------------------------------------*/ | ||
180 | static size_t b64pad(const UC *input, size_t size, | ||
181 | luaL_Buffer *buffer) | ||
182 | { | ||
183 | unsigned long value = 0; | ||
184 | UC code[4] = "===="; | ||
185 | switch (size) { | ||
186 | case 1: | ||
187 | value = input[0] << 4; | ||
188 | code[1] = b64base[value & 0x3f]; value >>= 6; | ||
189 | code[0] = b64base[value]; | ||
190 | luaL_addlstring(buffer, code, 4); | ||
191 | break; | ||
192 | case 2: | ||
193 | value = input[0]; value <<= 8; | ||
194 | value |= input[1]; value <<= 2; | ||
195 | code[2] = b64base[value & 0x3f]; value >>= 6; | ||
196 | code[1] = b64base[value & 0x3f]; value >>= 6; | ||
197 | code[0] = b64base[value]; | ||
198 | luaL_addlstring(buffer, code, 4); | ||
199 | break; | ||
200 | case 0: /* fall through */ | ||
201 | default: | ||
202 | break; | ||
203 | } | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | /*-------------------------------------------------------------------------*\ | ||
208 | * Acumulates bytes in input buffer until 4 bytes are available. | ||
209 | * Translate the 4 bytes from Base64 form and append to buffer. | ||
210 | * Returns new number of bytes in buffer. | ||
211 | \*-------------------------------------------------------------------------*/ | ||
212 | static size_t b64decode(UC c, UC *input, size_t size, | ||
213 | luaL_Buffer *buffer) | ||
214 | { | ||
215 | |||
216 | /* ignore invalid characters */ | ||
217 | if (b64unbase[c] > 64) return size; | ||
218 | input[size++] = c; | ||
219 | /* decode atom */ | ||
220 | if (size == 4) { | ||
221 | UC decoded[3]; | ||
222 | int valid, value = 0; | ||
223 | value = b64unbase[input[0]]; value <<= 6; | ||
224 | value |= b64unbase[input[1]]; value <<= 6; | ||
225 | value |= b64unbase[input[2]]; value <<= 6; | ||
226 | value |= b64unbase[input[3]]; | ||
227 | decoded[2] = (UC) (value & 0xff); value >>= 8; | ||
228 | decoded[1] = (UC) (value & 0xff); value >>= 8; | ||
229 | decoded[0] = (UC) value; | ||
230 | /* take care of paddding */ | ||
231 | valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; | ||
232 | luaL_addlstring(buffer, decoded, valid); | ||
233 | return 0; | ||
234 | /* need more data */ | ||
235 | } else return size; | ||
236 | } | ||
237 | |||
238 | /*-------------------------------------------------------------------------*\ | ||
239 | * Incrementally applies the Base64 transfer content encoding to a string | ||
240 | * A, B = b64(C, D) | ||
241 | * A is the encoded version of the largest prefix of C .. D that is | ||
242 | * divisible by 3. B has the remaining bytes of C .. D, *without* encoding. | ||
243 | * The easiest thing would be to concatenate the two strings and | ||
244 | * encode the result, but we can't afford that or Lua would dupplicate | ||
245 | * every chunk we received. | ||
246 | \*-------------------------------------------------------------------------*/ | ||
247 | static int mime_global_b64(lua_State *L) | ||
248 | { | ||
249 | UC atom[3]; | ||
250 | size_t isize = 0, asize = 0; | ||
251 | const UC *input = luaL_checklstring(L, 1, &isize); | ||
252 | const UC *last = input + isize; | ||
253 | luaL_Buffer buffer; | ||
254 | luaL_buffinit(L, &buffer); | ||
255 | while (input < last) | ||
256 | asize = b64encode(*input++, atom, asize, &buffer); | ||
257 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
258 | if (input) { | ||
259 | last = input + isize; | ||
260 | while (input < last) | ||
261 | asize = b64encode(*input++, atom, asize, &buffer); | ||
262 | } else | ||
263 | asize = b64pad(atom, asize, &buffer); | ||
264 | luaL_pushresult(&buffer); | ||
265 | lua_pushlstring(L, atom, asize); | ||
266 | return 2; | ||
267 | } | ||
268 | |||
269 | /*-------------------------------------------------------------------------*\ | ||
270 | * Incrementally removes the Base64 transfer content encoding from a string | ||
271 | * A, B = b64(C, D) | ||
272 | * A is the encoded version of the largest prefix of C .. D that is | ||
273 | * divisible by 4. B has the remaining bytes of C .. D, *without* encoding. | ||
274 | \*-------------------------------------------------------------------------*/ | ||
275 | static int mime_global_unb64(lua_State *L) | ||
276 | { | ||
277 | UC atom[4]; | ||
278 | size_t isize = 0, asize = 0; | ||
279 | const UC *input = luaL_checklstring(L, 1, &isize); | ||
280 | const UC *last = input + isize; | ||
281 | luaL_Buffer buffer; | ||
282 | luaL_buffinit(L, &buffer); | ||
283 | while (input < last) | ||
284 | asize = b64decode(*input++, atom, asize, &buffer); | ||
285 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
286 | if (input) { | ||
287 | last = input + isize; | ||
288 | while (input < last) | ||
289 | asize = b64decode(*input++, atom, asize, &buffer); | ||
290 | } | ||
291 | luaL_pushresult(&buffer); | ||
292 | lua_pushlstring(L, atom, asize); | ||
293 | return 2; | ||
294 | } | ||
295 | |||
296 | /*-------------------------------------------------------------------------*\ | ||
297 | * Quoted-printable encoding scheme | ||
298 | * all (except CRLF in text) can be =XX | ||
299 | * CLRL in not text must be =XX=XX | ||
300 | * 33 through 60 inclusive can be plain | ||
301 | * 62 through 120 inclusive can be plain | ||
302 | * 9 and 32 can be plain, unless in the end of a line, where must be =XX | ||
303 | * encoded lines must be no longer than 76 not counting CRLF | ||
304 | * soft line-break are =CRLF | ||
305 | * !"#$@[\]^`{|}~ should be =XX for EBCDIC compatibility | ||
306 | * To encode one byte, we need to see the next two. | ||
307 | * Worst case is when we see a space, and wonder if a CRLF is comming | ||
308 | \*-------------------------------------------------------------------------*/ | ||
309 | /*-------------------------------------------------------------------------*\ | ||
310 | * Split quoted-printable characters into classes | ||
311 | * Precompute reverse map for encoding | ||
312 | \*-------------------------------------------------------------------------*/ | ||
313 | static void qpfill(UC *qpclass, UC *qpunbase) | ||
314 | { | ||
315 | int i; | ||
316 | for (i = 0; i < 256; i++) qpclass[i] = QP_QUOTED; | ||
317 | for (i = 33; i <= 60; i++) qpclass[i] = QP_PLAIN; | ||
318 | for (i = 62; i <= 120; i++) qpclass[i] = QP_PLAIN; | ||
319 | qpclass[HT] = QP_IF_LAST; qpclass[SP] = QP_IF_LAST; | ||
320 | qpclass['!'] = QP_QUOTED; qpclass['"'] = QP_QUOTED; | ||
321 | qpclass['#'] = QP_QUOTED; qpclass['$'] = QP_QUOTED; | ||
322 | qpclass['@'] = QP_QUOTED; qpclass['['] = QP_QUOTED; | ||
323 | qpclass['\\'] = QP_QUOTED; qpclass[']'] = QP_QUOTED; | ||
324 | qpclass['^'] = QP_QUOTED; qpclass['`'] = QP_QUOTED; | ||
325 | qpclass['{'] = QP_QUOTED; qpclass['|'] = QP_QUOTED; | ||
326 | qpclass['}'] = QP_QUOTED; qpclass['~'] = QP_QUOTED; | ||
327 | qpclass['}'] = QP_QUOTED; qpclass[CR] = QP_CR; | ||
328 | for (i = 0; i < 256; i++) qpunbase[i] = 255; | ||
329 | qpunbase['0'] = 0; qpunbase['1'] = 1; qpunbase['2'] = 2; | ||
330 | qpunbase['3'] = 3; qpunbase['4'] = 4; qpunbase['5'] = 5; | ||
331 | qpunbase['6'] = 6; qpunbase['7'] = 7; qpunbase['8'] = 8; | ||
332 | qpunbase['9'] = 9; qpunbase['A'] = 10; qpunbase['a'] = 10; | ||
333 | qpunbase['B'] = 11; qpunbase['b'] = 11; qpunbase['C'] = 12; | ||
334 | qpunbase['c'] = 12; qpunbase['D'] = 13; qpunbase['d'] = 13; | ||
335 | qpunbase['E'] = 14; qpunbase['e'] = 14; qpunbase['F'] = 15; | ||
336 | qpunbase['f'] = 15; | ||
337 | } | ||
338 | |||
339 | /*-------------------------------------------------------------------------*\ | ||
340 | * Output one character in form =XX | ||
341 | \*-------------------------------------------------------------------------*/ | ||
342 | static void qpquote(UC c, luaL_Buffer *buffer) | ||
343 | { | ||
344 | luaL_putchar(buffer, '='); | ||
345 | luaL_putchar(buffer, qpbase[c >> 4]); | ||
346 | luaL_putchar(buffer, qpbase[c & 0x0F]); | ||
347 | } | ||
348 | |||
349 | /*-------------------------------------------------------------------------*\ | ||
350 | * Accumulate characters until we are sure about how to deal with them. | ||
351 | * Once we are sure, output the to the buffer, in the correct form. | ||
352 | \*-------------------------------------------------------------------------*/ | ||
353 | static size_t qpencode(UC c, UC *input, size_t size, | ||
354 | const UC *marker, luaL_Buffer *buffer) | ||
355 | { | ||
356 | input[size++] = c; | ||
357 | /* deal with all characters we can have */ | ||
358 | while (size > 0) { | ||
359 | switch (qpclass[input[0]]) { | ||
360 | /* might be the CR of a CRLF sequence */ | ||
361 | case QP_CR: | ||
362 | if (size < 2) return size; | ||
363 | if (input[1] == LF) { | ||
364 | luaL_addstring(buffer, marker); | ||
365 | return 0; | ||
366 | } else qpquote(input[0], buffer); | ||
367 | break; | ||
368 | /* might be a space and that has to be quoted if last in line */ | ||
369 | case QP_IF_LAST: | ||
370 | if (size < 3) return size; | ||
371 | /* if it is the last, quote it and we are done */ | ||
372 | if (input[1] == CR && input[2] == LF) { | ||
373 | qpquote(input[0], buffer); | ||
374 | luaL_addstring(buffer, marker); | ||
375 | return 0; | ||
376 | } else luaL_putchar(buffer, input[0]); | ||
377 | break; | ||
378 | /* might have to be quoted always */ | ||
379 | case QP_QUOTED: | ||
380 | qpquote(input[0], buffer); | ||
381 | break; | ||
382 | /* might never have to be quoted */ | ||
383 | default: | ||
384 | luaL_putchar(buffer, input[0]); | ||
385 | break; | ||
386 | } | ||
387 | input[0] = input[1]; input[1] = input[2]; | ||
388 | size--; | ||
389 | } | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | /*-------------------------------------------------------------------------*\ | ||
394 | * Deal with the final characters | ||
395 | \*-------------------------------------------------------------------------*/ | ||
396 | static void qppad(UC *input, size_t size, luaL_Buffer *buffer) | ||
397 | { | ||
398 | size_t i; | ||
399 | for (i = 0; i < size; i++) { | ||
400 | if (qpclass[input[i]] == QP_PLAIN) luaL_putchar(buffer, input[i]); | ||
401 | else qpquote(input[i], buffer); | ||
402 | } | ||
403 | luaL_addstring(buffer, EQCRLF); | ||
404 | } | ||
405 | |||
406 | /*-------------------------------------------------------------------------*\ | ||
407 | * Incrementally converts a string to quoted-printable | ||
408 | * A, B = qp(C, D, marker) | ||
409 | * Crlf is the text to be used to replace CRLF sequences found in A. | ||
410 | * A is the encoded version of the largest prefix of C .. D that | ||
411 | * can be encoded without doubts. | ||
412 | * B has the remaining bytes of C .. D, *without* encoding. | ||
413 | \*-------------------------------------------------------------------------*/ | ||
414 | static int mime_global_qp(lua_State *L) | ||
415 | { | ||
416 | |||
417 | size_t asize = 0, isize = 0; | ||
418 | UC atom[3]; | ||
419 | const UC *input = lua_isnil(L, 1) ? NULL: luaL_checklstring(L, 1, &isize); | ||
420 | const UC *last = input + isize; | ||
421 | const UC *marker = luaL_optstring(L, 3, CRLF); | ||
422 | luaL_Buffer buffer; | ||
423 | luaL_buffinit(L, &buffer); | ||
424 | while (input < last) | ||
425 | asize = qpencode(*input++, atom, asize, marker, &buffer); | ||
426 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
427 | if (input) { | ||
428 | last = input + isize; | ||
429 | while (input < last) | ||
430 | asize = qpencode(*input++, atom, asize, marker, &buffer); | ||
431 | } else qppad(atom, asize, &buffer); | ||
432 | luaL_pushresult(&buffer); | ||
433 | lua_pushlstring(L, atom, asize); | ||
434 | return 2; | ||
435 | } | ||
436 | |||
437 | /*-------------------------------------------------------------------------*\ | ||
438 | * Accumulate characters until we are sure about how to deal with them. | ||
439 | * Once we are sure, output the to the buffer, in the correct form. | ||
440 | \*-------------------------------------------------------------------------*/ | ||
441 | static size_t qpdecode(UC c, UC *input, size_t size, | ||
442 | luaL_Buffer *buffer) | ||
443 | { | ||
444 | input[size++] = c; | ||
445 | /* deal with all characters we can deal */ | ||
446 | while (size > 0) { | ||
447 | int c, d; | ||
448 | switch (input[0]) { | ||
449 | /* if we have an escape character */ | ||
450 | case '=': | ||
451 | if (size < 3) return size; | ||
452 | /* eliminate soft line break */ | ||
453 | if (input[1] == CR && input[2] == LF) return 0; | ||
454 | /* decode quoted representation */ | ||
455 | c = qpunbase[input[1]]; d = qpunbase[input[2]]; | ||
456 | /* if it is an invalid, do not decode */ | ||
457 | if (c > 15 || d > 15) luaL_addlstring(buffer, input, 3); | ||
458 | else luaL_putchar(buffer, (c << 4) + d); | ||
459 | return 0; | ||
460 | case CR: | ||
461 | if (size < 2) return size; | ||
462 | if (input[1] == LF) luaL_addlstring(buffer, input, 2); | ||
463 | return 0; | ||
464 | default: | ||
465 | if (input[0] == HT || (input[0] > 31 && input[0] < 127)) | ||
466 | luaL_putchar(buffer, input[0]); | ||
467 | return 0; | ||
468 | } | ||
469 | input[0] = input[1]; input[1] = input[2]; | ||
470 | size--; | ||
471 | } | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | /*-------------------------------------------------------------------------*\ | ||
476 | * Incrementally decodes a string in quoted-printable | ||
477 | * A, B = qp(C, D) | ||
478 | * A is the decoded version of the largest prefix of C .. D that | ||
479 | * can be decoded without doubts. | ||
480 | * B has the remaining bytes of C .. D, *without* decoding. | ||
481 | \*-------------------------------------------------------------------------*/ | ||
482 | static int mime_global_unqp(lua_State *L) | ||
483 | { | ||
484 | |||
485 | size_t asize = 0, isize = 0; | ||
486 | UC atom[3]; | ||
487 | const UC *input = lua_isnil(L, 1) ? NULL: luaL_checklstring(L, 1, &isize); | ||
488 | const UC *last = input + isize; | ||
489 | luaL_Buffer buffer; | ||
490 | luaL_buffinit(L, &buffer); | ||
491 | while (input < last) | ||
492 | asize = qpdecode(*input++, atom, asize, &buffer); | ||
493 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
494 | if (input) { | ||
495 | last = input + isize; | ||
496 | while (input < last) | ||
497 | asize = qpdecode(*input++, atom, asize, &buffer); | ||
498 | } | ||
499 | luaL_pushresult(&buffer); | ||
500 | lua_pushlstring(L, atom, asize); | ||
501 | return 2; | ||
502 | } | ||
503 | |||
504 | /*-------------------------------------------------------------------------*\ | ||
505 | * Incrementally breaks a quoted-printed string into lines | ||
506 | * A, n = qpfmt(B, length, left) | ||
507 | * A is a copy of B, broken into lines of at most 'length' bytes. | ||
508 | * Left is how many bytes are left in the first line of B. 'n' is the number | ||
509 | * of bytes left in the last line of A. | ||
510 | * There are two complications: lines can't be broken in the middle | ||
511 | * of an encoded =XX, and there might be line breaks already | ||
512 | \*-------------------------------------------------------------------------*/ | ||
513 | static int mime_global_qpfmt(lua_State *L) | ||
514 | { | ||
515 | size_t size = 0; | ||
516 | const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &size); | ||
517 | const UC *last = input + size; | ||
518 | int length = (int) luaL_checknumber(L, 2); | ||
519 | int left = (int) luaL_optnumber(L, 3, length); | ||
520 | luaL_Buffer buffer; | ||
521 | luaL_buffinit(L, &buffer); | ||
522 | while (input < last) { | ||
523 | left--; | ||
524 | switch (*input) { | ||
525 | case '=': | ||
526 | /* if there's no room in this line for the quoted char, | ||
527 | * output a soft line break now */ | ||
528 | if (left <= 3) { | ||
529 | luaL_addstring(&buffer, EQCRLF); | ||
530 | left = length; | ||
531 | } | ||
532 | break; | ||
533 | /* \r\n starts a new line */ | ||
534 | case CR: | ||
535 | break; | ||
536 | case LF: | ||
537 | left = length; | ||
538 | break; | ||
539 | default: | ||
540 | /* if in last column, output a soft line break */ | ||
541 | if (left <= 1) { | ||
542 | luaL_addstring(&buffer, EQCRLF); | ||
543 | left = length; | ||
544 | } | ||
545 | } | ||
546 | luaL_putchar(&buffer, *input); | ||
547 | input++; | ||
548 | } | ||
549 | if (!input && left < length) { | ||
550 | luaL_addstring(&buffer, EQCRLF); | ||
551 | left = length; | ||
552 | } | ||
553 | luaL_pushresult(&buffer); | ||
554 | lua_pushnumber(L, left); | ||
555 | return 2; | ||
556 | } | ||
557 | |||
558 | /*-------------------------------------------------------------------------*\ | ||
559 | * Here is what we do: \n, \r and \f are considered candidates for line | ||
560 | * break. We issue *one* new line marker if any of them is seen alone, or | ||
561 | * followed by a different one. That is, \n\n, \r\r and \f\f will issue two | ||
562 | * end of line markers each, but \r\n, \n\r, \r\f etc will only issue *one* | ||
563 | * marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as | ||
564 | * probably other more obscure conventions. | ||
565 | \*-------------------------------------------------------------------------*/ | ||
566 | #define eolcandidate(c) (c == CR || c == LF) | ||
567 | static size_t eolconvert(UC c, UC *input, size_t size, | ||
568 | const UC *marker, luaL_Buffer *buffer) | ||
569 | { | ||
570 | input[size++] = c; | ||
571 | /* deal with all characters we can deal */ | ||
572 | if (eolcandidate(input[0])) { | ||
573 | if (size < 2) return size; | ||
574 | luaL_addstring(buffer, marker); | ||
575 | if (eolcandidate(input[1])) { | ||
576 | if (input[0] == input[1]) luaL_addstring(buffer, marker); | ||
577 | } else luaL_putchar(buffer, input[1]); | ||
578 | return 0; | ||
579 | } else { | ||
580 | luaL_putchar(buffer, input[0]); | ||
581 | return 0; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | /*-------------------------------------------------------------------------*\ | ||
586 | * Converts a string to uniform EOL convention. | ||
587 | * A, B = eol(C, D, marker) | ||
588 | * A is the converted version of the largest prefix of C .. D that | ||
589 | * can be converted without doubts. | ||
590 | * B has the remaining bytes of C .. D, *without* convertion. | ||
591 | \*-------------------------------------------------------------------------*/ | ||
592 | static int mime_global_eol(lua_State *L) | ||
593 | { | ||
594 | size_t asize = 0, isize = 0; | ||
595 | UC atom[2]; | ||
596 | const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &isize); | ||
597 | const UC *last = input + isize; | ||
598 | const UC *marker = luaL_optstring(L, 3, CRLF); | ||
599 | luaL_Buffer buffer; | ||
600 | luaL_buffinit(L, &buffer); | ||
601 | while (input < last) | ||
602 | asize = eolconvert(*input++, atom, asize, marker, &buffer); | ||
603 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
604 | if (input) { | ||
605 | last = input + isize; | ||
606 | while (input < last) | ||
607 | asize = eolconvert(*input++, atom, asize, marker, &buffer); | ||
608 | /* if there is something in atom, it's one character, and it | ||
609 | * is a candidate. so we output a new line */ | ||
610 | } else if (asize > 0) luaL_addstring(&buffer, marker); | ||
611 | luaL_pushresult(&buffer); | ||
612 | lua_pushlstring(L, atom, asize); | ||
613 | return 2; | ||
614 | } | ||
diff --git a/src/mime.h b/src/mime.h new file mode 100644 index 0000000..8323783 --- /dev/null +++ b/src/mime.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef MIME_H | ||
2 | #define MIME_H | ||
3 | /*=========================================================================*\ | ||
4 | * Mime support functions | ||
5 | * LuaSocket toolkit | ||
6 | * | ||
7 | * This module provides functions to implement transfer content encodings | ||
8 | * and formatting conforming to RFC 2045. It is used by mime.lua, which | ||
9 | * provide a higher level interface to this functionality. | ||
10 | * | ||
11 | * RCS ID: $Id$ | ||
12 | \*=========================================================================*/ | ||
13 | #include <lua.h> | ||
14 | |||
15 | void mime_open(lua_State *L); | ||
16 | |||
17 | #endif /* MIME_H */ | ||
diff --git a/src/mime.lua b/src/mime.lua new file mode 100644 index 0000000..86b3af2 --- /dev/null +++ b/src/mime.lua | |||
@@ -0,0 +1,104 @@ | |||
1 | -- make sure LuaSocket is loaded | ||
2 | if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end | ||
3 | -- get LuaSocket namespace | ||
4 | local socket = _G[LUASOCKET_LIBNAME] | ||
5 | if not socket then error('module requires LuaSocket') end | ||
6 | -- create code namespace inside LuaSocket namespace | ||
7 | local mime = socket.mime or {} | ||
8 | socket.mime = mime | ||
9 | -- make all module globals fall into mime namespace | ||
10 | setmetatable(mime, { __index = _G }) | ||
11 | setfenv(1, mime) | ||
12 | |||
13 | base64 = {} | ||
14 | qprint = {} | ||
15 | |||
16 | function base64.encode() | ||
17 | local unfinished = "" | ||
18 | return function(chunk) | ||
19 | local done | ||
20 | done, unfinished = b64(unfinished, chunk) | ||
21 | return done | ||
22 | end | ||
23 | end | ||
24 | |||
25 | function base64.decode() | ||
26 | local unfinished = "" | ||
27 | return function(chunk) | ||
28 | local done | ||
29 | done, unfinished = unb64(unfinished, chunk) | ||
30 | return done | ||
31 | end | ||
32 | end | ||
33 | |||
34 | function qprint.encode(mode) | ||
35 | mode = (mode == "binary") and "=0D=0A" or "\13\10" | ||
36 | local unfinished = "" | ||
37 | return function(chunk) | ||
38 | local done | ||
39 | done, unfinished = qp(unfinished, chunk, mode) | ||
40 | return done | ||
41 | end | ||
42 | end | ||
43 | |||
44 | function qprint.decode() | ||
45 | local unfinished = "" | ||
46 | return function(chunk) | ||
47 | local done | ||
48 | done, unfinished = unqp(unfinished, chunk) | ||
49 | return done | ||
50 | end | ||
51 | end | ||
52 | |||
53 | function split(length, marker) | ||
54 | length = length or 76 | ||
55 | local left = length | ||
56 | return function(chunk) | ||
57 | local done | ||
58 | done, left = fmt(chunk, length, left, marker) | ||
59 | return done | ||
60 | end | ||
61 | end | ||
62 | |||
63 | function qprint.split(length) | ||
64 | length = length or 76 | ||
65 | local left = length | ||
66 | return function(chunk) | ||
67 | local done | ||
68 | done, left = qpfmt(chunk, length, left) | ||
69 | return done | ||
70 | end | ||
71 | end | ||
72 | |||
73 | function canonic(marker) | ||
74 | local unfinished = "" | ||
75 | return function(chunk) | ||
76 | local done | ||
77 | done, unfinished = eol(unfinished, chunk, marker) | ||
78 | return done | ||
79 | end | ||
80 | end | ||
81 | |||
82 | function chain(...) | ||
83 | local layers = table.getn(arg) | ||
84 | return function (chunk) | ||
85 | if not chunk then | ||
86 | local parts = {} | ||
87 | for i = 1, layers do | ||
88 | for j = i, layers do | ||
89 | chunk = arg[j](chunk) | ||
90 | end | ||
91 | table.insert(parts, chunk) | ||
92 | chunk = nil | ||
93 | end | ||
94 | return table.concat(parts) | ||
95 | else | ||
96 | for j = 1, layers do | ||
97 | chunk = arg[j](chunk) | ||
98 | end | ||
99 | return chunk | ||
100 | end | ||
101 | end | ||
102 | end | ||
103 | |||
104 | return code | ||
@@ -238,7 +238,7 @@ static int meth_bind(lua_State *L) | |||
238 | return 2; | 238 | return 2; |
239 | } | 239 | } |
240 | /* turn master object into a server object if there was a listen */ | 240 | /* turn master object into a server object if there was a listen */ |
241 | if (backlog > 0) aux_setclass(L, "tcp{server}", 1); | 241 | if (backlog >= 0) aux_setclass(L, "tcp{server}", 1); |
242 | lua_pushnumber(L, 1); | 242 | lua_pushnumber(L, 1); |
243 | return 1; | 243 | return 1; |
244 | } | 244 | } |
diff --git a/src/url.lua b/src/url.lua index 27e7928..ab3a922 100644 --- a/src/url.lua +++ b/src/url.lua | |||
@@ -6,9 +6,102 @@ | |||
6 | -- RCS ID: $Id$ | 6 | -- RCS ID: $Id$ |
7 | ---------------------------------------------------------------------------- | 7 | ---------------------------------------------------------------------------- |
8 | 8 | ||
9 | local Public, Private = {}, {} | 9 | -- make sure LuaSocket is loaded |
10 | local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace | 10 | if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end |
11 | socket.url = Public | 11 | -- get LuaSocket namespace |
12 | local socket = _G[LUASOCKET_LIBNAME] | ||
13 | if not socket then error('module requires LuaSocket') end | ||
14 | -- create smtp namespace inside LuaSocket namespace | ||
15 | local url = {} | ||
16 | socket.url = url | ||
17 | -- make all module globals fall into smtp namespace | ||
18 | setmetatable(url, { __index = _G }) | ||
19 | setfenv(1, url) | ||
20 | |||
21 | ----------------------------------------------------------------------------- | ||
22 | -- Encodes a string into its escaped hexadecimal representation | ||
23 | -- Input | ||
24 | -- s: binary string to be encoded | ||
25 | -- Returns | ||
26 | -- escaped representation of string binary | ||
27 | ----------------------------------------------------------------------------- | ||
28 | function escape(s) | ||
29 | return string.gsub(s, "(.)", function(c) | ||
30 | return string.format("%%%02x", string.byte(c)) | ||
31 | end) | ||
32 | end | ||
33 | |||
34 | ----------------------------------------------------------------------------- | ||
35 | -- Protects a path segment, to prevent it from interfering with the | ||
36 | -- url parsing. | ||
37 | -- Input | ||
38 | -- s: binary string to be encoded | ||
39 | -- Returns | ||
40 | -- escaped representation of string binary | ||
41 | ----------------------------------------------------------------------------- | ||
42 | local function make_set(t) | ||
43 | local s = {} | ||
44 | for i = 1, table.getn(t) do | ||
45 | s[t[i]] = 1 | ||
46 | end | ||
47 | return s | ||
48 | end | ||
49 | |||
50 | -- these are allowed withing a path segment, along with alphanum | ||
51 | -- other characters must be escaped | ||
52 | local segment_set = make_set { | ||
53 | "-", "_", ".", "!", "~", "*", "'", "(", | ||
54 | ")", ":", "@", "&", "=", "+", "$", ",", | ||
55 | } | ||
56 | |||
57 | local function protect_segment(s) | ||
58 | return string.gsub(s, "(%W)", function (c) | ||
59 | if segment_set[c] then return c | ||
60 | else return escape(c) end | ||
61 | end) | ||
62 | end | ||
63 | |||
64 | ----------------------------------------------------------------------------- | ||
65 | -- Encodes a string into its escaped hexadecimal representation | ||
66 | -- Input | ||
67 | -- s: binary string to be encoded | ||
68 | -- Returns | ||
69 | -- escaped representation of string binary | ||
70 | ----------------------------------------------------------------------------- | ||
71 | function unescape(s) | ||
72 | return string.gsub(s, "%%(%x%x)", function(hex) | ||
73 | return string.char(tonumber(hex, 16)) | ||
74 | end) | ||
75 | end | ||
76 | |||
77 | ----------------------------------------------------------------------------- | ||
78 | -- Builds a path from a base path and a relative path | ||
79 | -- Input | ||
80 | -- base_path | ||
81 | -- relative_path | ||
82 | -- Returns | ||
83 | -- corresponding absolute path | ||
84 | ----------------------------------------------------------------------------- | ||
85 | local function absolute_path(base_path, relative_path) | ||
86 | if string.sub(relative_path, 1, 1) == "/" then return relative_path end | ||
87 | local path = string.gsub(base_path, "[^/]*$", "") | ||
88 | path = path .. relative_path | ||
89 | path = string.gsub(path, "([^/]*%./)", function (s) | ||
90 | if s ~= "./" then return s else return "" end | ||
91 | end) | ||
92 | path = string.gsub(path, "/%.$", "/") | ||
93 | local reduced | ||
94 | while reduced ~= path do | ||
95 | reduced = path | ||
96 | path = string.gsub(reduced, "([^/]*/%.%./)", function (s) | ||
97 | if s ~= "../../" then return "" else return s end | ||
98 | end) | ||
99 | end | ||
100 | path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) | ||
101 | if s ~= "../.." then return "" else return s end | ||
102 | end) | ||
103 | return path | ||
104 | end | ||
12 | 105 | ||
13 | ----------------------------------------------------------------------------- | 106 | ----------------------------------------------------------------------------- |
14 | -- Parses a url and returns a table with all its parts according to RFC 2396 | 107 | -- Parses a url and returns a table with all its parts according to RFC 2396 |
@@ -28,7 +121,7 @@ socket.url = Public | |||
28 | -- Obs: | 121 | -- Obs: |
29 | -- the leading '/' in {/<path>} is considered part of <path> | 122 | -- the leading '/' in {/<path>} is considered part of <path> |
30 | ----------------------------------------------------------------------------- | 123 | ----------------------------------------------------------------------------- |
31 | function Public.parse(url, default) | 124 | function parse(url, default) |
32 | -- initialize default parameters | 125 | -- initialize default parameters |
33 | local parsed = default or {} | 126 | local parsed = default or {} |
34 | -- empty url is parsed to nil | 127 | -- empty url is parsed to nil |
@@ -66,11 +159,11 @@ end | |||
66 | -- Rebuilds a parsed URL from its components. | 159 | -- Rebuilds a parsed URL from its components. |
67 | -- Components are protected if any reserved or unallowed characters are found | 160 | -- Components are protected if any reserved or unallowed characters are found |
68 | -- Input | 161 | -- Input |
69 | -- parsed: parsed URL, as returned by Public.parse | 162 | -- parsed: parsed URL, as returned by parse |
70 | -- Returns | 163 | -- Returns |
71 | -- a stringing with the corresponding URL | 164 | -- a stringing with the corresponding URL |
72 | ----------------------------------------------------------------------------- | 165 | ----------------------------------------------------------------------------- |
73 | function Public.build(parsed) | 166 | function build(parsed) |
74 | local url = parsed.path or "" | 167 | local url = parsed.path or "" |
75 | if parsed.params then url = url .. ";" .. parsed.params end | 168 | if parsed.params then url = url .. ";" .. parsed.params end |
76 | if parsed.query then url = url .. "?" .. parsed.query end | 169 | if parsed.query then url = url .. "?" .. parsed.query end |
@@ -102,9 +195,9 @@ end | |||
102 | -- Returns | 195 | -- Returns |
103 | -- corresponding absolute url | 196 | -- corresponding absolute url |
104 | ----------------------------------------------------------------------------- | 197 | ----------------------------------------------------------------------------- |
105 | function Public.absolute(base_url, relative_url) | 198 | function absolute(base_url, relative_url) |
106 | local base = Public.parse(base_url) | 199 | local base = parse(base_url) |
107 | local relative = Public.parse(relative_url) | 200 | local relative = parse(relative_url) |
108 | if not base then return relative_url | 201 | if not base then return relative_url |
109 | elseif not relative then return base_url | 202 | elseif not relative then return base_url |
110 | elseif relative.scheme then return relative_url | 203 | elseif relative.scheme then return relative_url |
@@ -121,10 +214,10 @@ function Public.absolute(base_url, relative_url) | |||
121 | end | 214 | end |
122 | end | 215 | end |
123 | else | 216 | else |
124 | relative.path = Private.absolute_path(base.path,relative.path) | 217 | relative.path = absolute_path(base.path,relative.path) |
125 | end | 218 | end |
126 | end | 219 | end |
127 | return Public.build(relative) | 220 | return build(relative) |
128 | end | 221 | end |
129 | end | 222 | end |
130 | 223 | ||
@@ -135,13 +228,13 @@ end | |||
135 | -- Returns | 228 | -- Returns |
136 | -- segment: a table with one entry per segment | 229 | -- segment: a table with one entry per segment |
137 | ----------------------------------------------------------------------------- | 230 | ----------------------------------------------------------------------------- |
138 | function Public.parse_path(path) | 231 | function parse_path(path) |
139 | local parsed = {} | 232 | local parsed = {} |
140 | path = path or "" | 233 | path = path or "" |
141 | path = string.gsub(path, "%s", "") | 234 | path = string.gsub(path, "%s", "") |
142 | string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) | 235 | string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) |
143 | for i = 1, table.getn(parsed) do | 236 | for i = 1, table.getn(parsed) do |
144 | parsed[i] = socket.code.unescape(parsed[i]) | 237 | parsed[i] = unescape(parsed[i]) |
145 | end | 238 | end |
146 | if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end | 239 | if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end |
147 | if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end | 240 | if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end |
@@ -154,9 +247,9 @@ end | |||
154 | -- parsed: path segments | 247 | -- parsed: path segments |
155 | -- unsafe: if true, segments are not protected before path is built | 248 | -- unsafe: if true, segments are not protected before path is built |
156 | -- Returns | 249 | -- Returns |
157 | -- path: correspondin path stringing | 250 | -- path: corresponding path stringing |
158 | ----------------------------------------------------------------------------- | 251 | ----------------------------------------------------------------------------- |
159 | function Public.build_path(parsed, unsafe) | 252 | function build_path(parsed, unsafe) |
160 | local path = "" | 253 | local path = "" |
161 | local n = table.getn(parsed) | 254 | local n = table.getn(parsed) |
162 | if unsafe then | 255 | if unsafe then |
@@ -170,66 +263,14 @@ function Public.build_path(parsed, unsafe) | |||
170 | end | 263 | end |
171 | else | 264 | else |
172 | for i = 1, n-1 do | 265 | for i = 1, n-1 do |
173 | path = path .. Private.protect_segment(parsed[i]) | 266 | path = path .. protect_segment(parsed[i]) |
174 | path = path .. "/" | 267 | path = path .. "/" |
175 | end | 268 | end |
176 | if n > 0 then | 269 | if n > 0 then |
177 | path = path .. Private.protect_segment(parsed[n]) | 270 | path = path .. protect_segment(parsed[n]) |
178 | if parsed.is_directory then path = path .. "/" end | 271 | if parsed.is_directory then path = path .. "/" end |
179 | end | 272 | end |
180 | end | 273 | end |
181 | if parsed.is_absolute then path = "/" .. path end | 274 | if parsed.is_absolute then path = "/" .. path end |
182 | return path | 275 | return path |
183 | end | 276 | end |
184 | |||
185 | function Private.make_set(t) | ||
186 | local s = {} | ||
187 | for i = 1, table.getn(t) do | ||
188 | s[t[i]] = 1 | ||
189 | end | ||
190 | return s | ||
191 | end | ||
192 | |||
193 | -- these are allowed withing a path segment, along with alphanum | ||
194 | -- other characters must be escaped | ||
195 | Private.segment_set = Private.make_set { | ||
196 | "-", "_", ".", "!", "~", "*", "'", "(", | ||
197 | ")", ":", "@", "&", "=", "+", "$", ",", | ||
198 | } | ||
199 | |||
200 | function Private.protect_segment(s) | ||
201 | local segment_set = Private.segment_set | ||
202 | return string.gsub(s, "(%W)", function (c) | ||
203 | if segment_set[c] then return c | ||
204 | else return socket.code.escape(c) end | ||
205 | end) | ||
206 | end | ||
207 | |||
208 | ----------------------------------------------------------------------------- | ||
209 | -- Builds a path from a base path and a relative path | ||
210 | -- Input | ||
211 | -- base_path | ||
212 | -- relative_path | ||
213 | -- Returns | ||
214 | -- corresponding absolute path | ||
215 | ----------------------------------------------------------------------------- | ||
216 | function Private.absolute_path(base_path, relative_path) | ||
217 | if string.sub(relative_path, 1, 1) == "/" then return relative_path end | ||
218 | local path = string.gsub(base_path, "[^/]*$", "") | ||
219 | path = path .. relative_path | ||
220 | path = string.gsub(path, "([^/]*%./)", function (s) | ||
221 | if s ~= "./" then return s else return "" end | ||
222 | end) | ||
223 | path = string.gsub(path, "/%.$", "/") | ||
224 | local reduced | ||
225 | while reduced ~= path do | ||
226 | reduced = path | ||
227 | path = string.gsub(reduced, "([^/]*/%.%./)", function (s) | ||
228 | if s ~= "../../" then return "" else return s end | ||
229 | end) | ||
230 | end | ||
231 | path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) | ||
232 | if s ~= "../.." then return "" else return s end | ||
233 | end) | ||
234 | return path | ||
235 | end | ||