aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2004-01-19 05:41:30 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2004-01-19 05:41:30 +0000
commit5b8d7dec541a618b4ca7f2205470a28cde2e3e25 (patch)
tree209ad0c80c9a938068401fc5b8fa51942972418f /src
parent6ac82d50eecdf9bf55f4234ed3a5449afd7a2992 (diff)
downloadluasocket-5b8d7dec541a618b4ca7f2205470a28cde2e3e25.tar.gz
luasocket-5b8d7dec541a618b4ca7f2205470a28cde2e3e25.tar.bz2
luasocket-5b8d7dec541a618b4ca7f2205470a28cde2e3e25.zip
Updated some of the callbacks in callback.lua.
Update get.lua to use the new callbacks. The old "code" module is now the "mime" module. Updated all modules that depended on it. Updated url.lua to use the new namespace scheme, and moved the escape and unescape functions that used to be in the code.lua module to it, since these are specific to urls. Updated the callback entries in the manual.
Diffstat (limited to 'src')
-rw-r--r--src/http.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