aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/http.lua25
-rw-r--r--src/inet.c2
-rw-r--r--src/luasocket.c8
-rw-r--r--src/mime.c614
-rw-r--r--src/mime.h17
-rw-r--r--src/mime.lua104
-rw-r--r--src/tcp.c2
-rw-r--r--src/url.lua179
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-----------------------------------------------------------------------------
422local function authorize(reqt, parsed, respt) 422local 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)
436end 436end
@@ -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-----------------------------------------------------------------------------
492local function request_uri(reqt, parsed) 492local 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-----------------------------------------------------------------------------
545function request_cb(reqt, respt) 545function 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
diff --git a/src/inet.c b/src/inet.c
index 282d616..6aea596 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -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
23typedef unsigned char UC;
24static const UC CRLF[2] = {CR, LF};
25static const UC EQCRLF[3] = {'=', CR, LF};
26
27/*=========================================================================*\
28* Internal function prototypes.
29\*=========================================================================*/
30static int mime_global_fmt(lua_State *L);
31static int mime_global_b64(lua_State *L);
32static int mime_global_unb64(lua_State *L);
33static int mime_global_qp(lua_State *L);
34static int mime_global_unqp(lua_State *L);
35static int mime_global_qpfmt(lua_State *L);
36static int mime_global_eol(lua_State *L);
37
38static void b64fill(UC *b64unbase);
39static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
40static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer);
41static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
42
43static void qpfill(UC *qpclass, UC *qpunbase);
44static void qpquote(UC c, luaL_Buffer *buffer);
45static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
46static size_t qpencode(UC c, UC *input, size_t size,
47 const UC *marker, luaL_Buffer *buffer);
48
49/* code support functions */
50static 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\*-------------------------------------------------------------------------*/
64static UC qpclass[256];
65static UC qpbase[] = "0123456789ABCDEF";
66static UC qpunbase[256];
67enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST};
68
69/*-------------------------------------------------------------------------*\
70* Base64 globals
71\*-------------------------------------------------------------------------*/
72static const UC b64base[] =
73 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
74static UC b64unbase[256];
75
76/*=========================================================================*\
77* Exported functions
78\*=========================================================================*/
79/*-------------------------------------------------------------------------*\
80* Initializes module
81\*-------------------------------------------------------------------------*/
82void 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\*-------------------------------------------------------------------------*/
113static 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\*-------------------------------------------------------------------------*/
142static 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\*-------------------------------------------------------------------------*/
155static 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\*-------------------------------------------------------------------------*/
180static 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\*-------------------------------------------------------------------------*/
212static 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\*-------------------------------------------------------------------------*/
247static 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\*-------------------------------------------------------------------------*/
275static 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\*-------------------------------------------------------------------------*/
313static 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\*-------------------------------------------------------------------------*/
342static 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\*-------------------------------------------------------------------------*/
353static 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\*-------------------------------------------------------------------------*/
396static 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\*-------------------------------------------------------------------------*/
414static 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\*-------------------------------------------------------------------------*/
441static 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\*-------------------------------------------------------------------------*/
482static 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\*-------------------------------------------------------------------------*/
513static 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)
567static 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\*-------------------------------------------------------------------------*/
592static 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
15void 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
2if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end
3-- get LuaSocket namespace
4local socket = _G[LUASOCKET_LIBNAME]
5if not socket then error('module requires LuaSocket') end
6-- create code namespace inside LuaSocket namespace
7local mime = socket.mime or {}
8socket.mime = mime
9-- make all module globals fall into mime namespace
10setmetatable(mime, { __index = _G })
11setfenv(1, mime)
12
13base64 = {}
14qprint = {}
15
16function base64.encode()
17 local unfinished = ""
18 return function(chunk)
19 local done
20 done, unfinished = b64(unfinished, chunk)
21 return done
22 end
23end
24
25function base64.decode()
26 local unfinished = ""
27 return function(chunk)
28 local done
29 done, unfinished = unb64(unfinished, chunk)
30 return done
31 end
32end
33
34function 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
42end
43
44function qprint.decode()
45 local unfinished = ""
46 return function(chunk)
47 local done
48 done, unfinished = unqp(unfinished, chunk)
49 return done
50 end
51end
52
53function 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
61end
62
63function 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
71end
72
73function canonic(marker)
74 local unfinished = ""
75 return function(chunk)
76 local done
77 done, unfinished = eol(unfinished, chunk, marker)
78 return done
79 end
80end
81
82function 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
102end
103
104return code
diff --git a/src/tcp.c b/src/tcp.c
index ce2ae17..b4b9fd9 100644
--- a/src/tcp.c
+++ b/src/tcp.c
@@ -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
9local Public, Private = {}, {} 9-- make sure LuaSocket is loaded
10local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace 10if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end
11socket.url = Public 11-- get LuaSocket namespace
12local socket = _G[LUASOCKET_LIBNAME]
13if not socket then error('module requires LuaSocket') end
14-- create smtp namespace inside LuaSocket namespace
15local url = {}
16socket.url = url
17-- make all module globals fall into smtp namespace
18setmetatable(url, { __index = _G })
19setfenv(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-----------------------------------------------------------------------------
28function escape(s)
29 return string.gsub(s, "(.)", function(c)
30 return string.format("%%%02x", string.byte(c))
31 end)
32end
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-----------------------------------------------------------------------------
42local 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
48end
49
50-- these are allowed withing a path segment, along with alphanum
51-- other characters must be escaped
52local segment_set = make_set {
53 "-", "_", ".", "!", "~", "*", "'", "(",
54 ")", ":", "@", "&", "=", "+", "$", ",",
55}
56
57local 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)
62end
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-----------------------------------------------------------------------------
71function unescape(s)
72 return string.gsub(s, "%%(%x%x)", function(hex)
73 return string.char(tonumber(hex, 16))
74 end)
75end
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-----------------------------------------------------------------------------
85local 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
104end
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-----------------------------------------------------------------------------
31function Public.parse(url, default) 124function 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-----------------------------------------------------------------------------
73function Public.build(parsed) 166function 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-----------------------------------------------------------------------------
105function Public.absolute(base_url, relative_url) 198function 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
129end 222end
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-----------------------------------------------------------------------------
138function Public.parse_path(path) 231function 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-----------------------------------------------------------------------------
159function Public.build_path(parsed, unsafe) 252function 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
183end 276end
184
185function 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
191end
192
193-- these are allowed withing a path segment, along with alphanum
194-- other characters must be escaped
195Private.segment_set = Private.make_set {
196 "-", "_", ".", "!", "~", "*", "'", "(",
197 ")", ":", "@", "&", "=", "+", "$", ",",
198}
199
200function 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)
206end
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-----------------------------------------------------------------------------
216function 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
235end