diff options
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 | ||