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.
This commit is contained in:
Diego Nehab 2004-01-19 05:41:30 +00:00
parent 6ac82d50ee
commit 5b8d7dec54
16 changed files with 1201 additions and 190 deletions

View file

@ -421,7 +421,7 @@ end
-----------------------------------------------------------------------------
local function authorize(reqt, parsed, respt)
reqt.headers["authorization"] = "Basic " ..
(socket.code.b64(parsed.user .. ":" .. parsed.password))
(socket.mime.b64(parsed.user .. ":" .. parsed.password))
local autht = {
nredirects = reqt.nredirects,
method = reqt.method,
@ -429,8 +429,8 @@ local function authorize(reqt, parsed, respt)
body_cb = reqt.body_cb,
headers = reqt.headers,
timeout = reqt.timeout,
host = reqt.host,
port = reqt.port
proxyhost = reqt.proxyhost,
proxyport = reqt.proxyport
}
return request_cb(autht, respt)
end
@ -471,8 +471,8 @@ local function redirect(reqt, respt)
body_cb = reqt.body_cb,
headers = reqt.headers,
timeout = reqt.timeout,
host = reqt.host,
port = reqt.port
proxyhost = reqt.proxyhost,
proxyport = reqt.proxyport
}
respt = request_cb(redirt, respt)
-- we pass the location header as a clue we tried to redirect
@ -482,8 +482,8 @@ end
-----------------------------------------------------------------------------
-- Computes the request URI from the parsed request URL
-- If host and port are given in the request table, we use he
-- absoluteURI format. Otherwise, we use the abs_path format.
-- If we are using a proxy, we use the absoluteURI format.
-- Otherwise, we use the abs_path format.
-- Input
-- parsed: parsed URL
-- Returns
@ -491,7 +491,7 @@ end
-----------------------------------------------------------------------------
local function request_uri(reqt, parsed)
local url
if not reqt.host and not reqt.port then
if not reqt.proxyhost and not reqt.proxyport then
url = {
path = parsed.path,
params = parsed.params,
@ -543,6 +543,7 @@ end
-- error: error message, or nil if successfull
-----------------------------------------------------------------------------
function request_cb(reqt, respt)
local sock, ret
local parsed = socket.url.parse(reqt.url, {
host = "",
port = PORT,
@ -561,14 +562,14 @@ function request_cb(reqt, respt)
-- fill default headers
reqt.headers = fill_headers(reqt.headers, parsed)
-- try to connect to server
local sock
sock, respt.error = socket.tcp()
if not sock then return respt end
-- set connection timeout so that we do not hang forever
sock:settimeout(reqt.timeout or TIMEOUT)
local ret
ret, respt.error = sock:connect(reqt.host or parsed.host,
reqt.port or parsed.port)
ret, respt.error = sock:connect(
reqt.proxyhost or PROXYHOST or parsed.host,
reqt.proxyport or PROXYPORT or parsed.port
)
if not ret then
sock:close()
return respt

View file

@ -234,7 +234,7 @@ const char *inet_trybind(p_sock ps, const char *address, unsigned short port,
return sock_bindstrerror();
} else {
sock_setnonblocking(ps);
if (backlog > 0) sock_listen(ps, backlog);
if (backlog >= 0) sock_listen(ps, backlog);
return NULL;
}
}

View file

@ -33,7 +33,7 @@
#include "tcp.h"
#include "udp.h"
#include "select.h"
#include "code.h"
#include "mime.h"
/*=========================================================================*\
* Exported functions
@ -52,13 +52,13 @@ LUASOCKET_API int luaopen_socket(lua_State *L)
tcp_open(L);
udp_open(L);
select_open(L);
code_open(L);
mime_open(L);
#ifdef LUASOCKET_COMPILED
#include "auxiliar.lch"
#include "concat.lch"
#include "url.lch"
#include "callback.lch"
#include "code.lch"
#include "mime.lch"
#include "smtp.lch"
#include "ftp.lch"
#include "http.lch"
@ -67,7 +67,7 @@ LUASOCKET_API int luaopen_socket(lua_State *L)
lua_dofile(L, "concat.lua");
lua_dofile(L, "url.lua");
lua_dofile(L, "callback.lua");
lua_dofile(L, "code.lua");
lua_dofile(L, "mime.lua");
lua_dofile(L, "smtp.lua");
lua_dofile(L, "ftp.lua");
lua_dofile(L, "http.lua");

614
src/mime.c Normal file
View file

@ -0,0 +1,614 @@
/*=========================================================================*\
* Encoding support functions
* LuaSocket toolkit
*
* RCS ID: $Id$
\*=========================================================================*/
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include "luasocket.h"
#include "mime.h"
/*=========================================================================*\
* Don't want to trust escape character constants
\*=========================================================================*/
#define CR 0x0D
#define LF 0x0A
#define HT 0x09
#define SP 0x20
typedef unsigned char UC;
static const UC CRLF[2] = {CR, LF};
static const UC EQCRLF[3] = {'=', CR, LF};
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
static int mime_global_fmt(lua_State *L);
static int mime_global_b64(lua_State *L);
static int mime_global_unb64(lua_State *L);
static int mime_global_qp(lua_State *L);
static int mime_global_unqp(lua_State *L);
static int mime_global_qpfmt(lua_State *L);
static int mime_global_eol(lua_State *L);
static void b64fill(UC *b64unbase);
static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer);
static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
static void qpfill(UC *qpclass, UC *qpunbase);
static void qpquote(UC c, luaL_Buffer *buffer);
static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
static size_t qpencode(UC c, UC *input, size_t size,
const UC *marker, luaL_Buffer *buffer);
/* code support functions */
static luaL_reg func[] = {
{ "eol", mime_global_eol },
{ "qp", mime_global_qp },
{ "unqp", mime_global_unqp },
{ "qpfmt", mime_global_qpfmt },
{ "b64", mime_global_b64 },
{ "unb64", mime_global_unb64 },
{ "fmt", mime_global_fmt },
{ NULL, NULL }
};
/*-------------------------------------------------------------------------*\
* Quoted-printable globals
\*-------------------------------------------------------------------------*/
static UC qpclass[256];
static UC qpbase[] = "0123456789ABCDEF";
static UC qpunbase[256];
enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST};
/*-------------------------------------------------------------------------*\
* Base64 globals
\*-------------------------------------------------------------------------*/
static const UC b64base[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static UC b64unbase[256];
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
void mime_open(lua_State *L)
{
lua_pushstring(L, LUASOCKET_LIBNAME);
lua_gettable(L, LUA_GLOBALSINDEX);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_newtable(L);
lua_pushstring(L, LUASOCKET_LIBNAME);
lua_pushvalue(L, -2);
lua_settable(L, LUA_GLOBALSINDEX);
}
lua_pushstring(L, "mime");
lua_newtable(L);
luaL_openlib(L, NULL, func, 0);
lua_settable(L, -3);
lua_pop(L, 1);
/* initialize lookup tables */
qpfill(qpclass, qpunbase);
b64fill(b64unbase);
}
/*=========================================================================*\
* Global Lua functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Incrementaly breaks a string into lines
* A, n = fmt(B, length, left)
* A is a copy of B, broken into lines of at most 'length' bytes.
* Left is how many bytes are left in the first line of B. 'n' is the number
* of bytes left in the last line of A.
\*-------------------------------------------------------------------------*/
static int mime_global_fmt(lua_State *L)
{
size_t size = 0;
const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &size);
const UC *last = input + size;
int length = (int) luaL_checknumber(L, 2);
int left = (int) luaL_optnumber(L, 3, length);
const UC *marker = luaL_optstring(L, 4, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input < last) {
luaL_putchar(&buffer, *input++);
if (--left <= 0) {
luaL_addstring(&buffer, marker);
left = length;
}
}
if (!input && left < length) {
luaL_addstring(&buffer, marker);
left = length;
}
luaL_pushresult(&buffer);
lua_pushnumber(L, left);
return 2;
}
/*-------------------------------------------------------------------------*\
* Fill base64 decode map.
\*-------------------------------------------------------------------------*/
static void b64fill(UC *b64unbase)
{
int i;
for (i = 0; i < 255; i++) b64unbase[i] = 255;
for (i = 0; i < 64; i++) b64unbase[b64base[i]] = i;
b64unbase['='] = 0;
}
/*-------------------------------------------------------------------------*\
* Acumulates bytes in input buffer until 3 bytes are available.
* Translate the 3 bytes into Base64 form and append to buffer.
* Returns new number of bytes in buffer.
\*-------------------------------------------------------------------------*/
static size_t b64encode(UC c, UC *input, size_t size,
luaL_Buffer *buffer)
{
input[size++] = c;
if (size == 3) {
UC code[4];
unsigned long value = 0;
value += input[0]; value <<= 8;
value += input[1]; value <<= 8;
value += input[2];
code[3] = b64base[value & 0x3f]; value >>= 6;
code[2] = b64base[value & 0x3f]; value >>= 6;
code[1] = b64base[value & 0x3f]; value >>= 6;
code[0] = b64base[value];
luaL_addlstring(buffer, code, 4);
size = 0;
}
return size;
}
/*-------------------------------------------------------------------------*\
* Encodes the Base64 last 1 or 2 bytes and adds padding '='
* Result, if any, is appended to buffer.
* Returns 0.
\*-------------------------------------------------------------------------*/
static size_t b64pad(const UC *input, size_t size,
luaL_Buffer *buffer)
{
unsigned long value = 0;
UC code[4] = "====";
switch (size) {
case 1:
value = input[0] << 4;
code[1] = b64base[value & 0x3f]; value >>= 6;
code[0] = b64base[value];
luaL_addlstring(buffer, code, 4);
break;
case 2:
value = input[0]; value <<= 8;
value |= input[1]; value <<= 2;
code[2] = b64base[value & 0x3f]; value >>= 6;
code[1] = b64base[value & 0x3f]; value >>= 6;
code[0] = b64base[value];
luaL_addlstring(buffer, code, 4);
break;
case 0: /* fall through */
default:
break;
}
return 0;
}
/*-------------------------------------------------------------------------*\
* Acumulates bytes in input buffer until 4 bytes are available.
* Translate the 4 bytes from Base64 form and append to buffer.
* Returns new number of bytes in buffer.
\*-------------------------------------------------------------------------*/
static size_t b64decode(UC c, UC *input, size_t size,
luaL_Buffer *buffer)
{
/* ignore invalid characters */
if (b64unbase[c] > 64) return size;
input[size++] = c;
/* decode atom */
if (size == 4) {
UC decoded[3];
int valid, value = 0;
value = b64unbase[input[0]]; value <<= 6;
value |= b64unbase[input[1]]; value <<= 6;
value |= b64unbase[input[2]]; value <<= 6;
value |= b64unbase[input[3]];
decoded[2] = (UC) (value & 0xff); value >>= 8;
decoded[1] = (UC) (value & 0xff); value >>= 8;
decoded[0] = (UC) value;
/* take care of paddding */
valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3;
luaL_addlstring(buffer, decoded, valid);
return 0;
/* need more data */
} else return size;
}
/*-------------------------------------------------------------------------*\
* Incrementally applies the Base64 transfer content encoding to a string
* A, B = b64(C, D)
* A is the encoded version of the largest prefix of C .. D that is
* divisible by 3. B has the remaining bytes of C .. D, *without* encoding.
* The easiest thing would be to concatenate the two strings and
* encode the result, but we can't afford that or Lua would dupplicate
* every chunk we received.
\*-------------------------------------------------------------------------*/
static int mime_global_b64(lua_State *L)
{
UC atom[3];
size_t isize = 0, asize = 0;
const UC *input = luaL_checklstring(L, 1, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input < last)
asize = b64encode(*input++, atom, asize, &buffer);
input = luaL_optlstring(L, 2, NULL, &isize);
if (input) {
last = input + isize;
while (input < last)
asize = b64encode(*input++, atom, asize, &buffer);
} else
asize = b64pad(atom, asize, &buffer);
luaL_pushresult(&buffer);
lua_pushlstring(L, atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Incrementally removes the Base64 transfer content encoding from a string
* A, B = b64(C, D)
* A is the encoded version of the largest prefix of C .. D that is
* divisible by 4. B has the remaining bytes of C .. D, *without* encoding.
\*-------------------------------------------------------------------------*/
static int mime_global_unb64(lua_State *L)
{
UC atom[4];
size_t isize = 0, asize = 0;
const UC *input = luaL_checklstring(L, 1, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input < last)
asize = b64decode(*input++, atom, asize, &buffer);
input = luaL_optlstring(L, 2, NULL, &isize);
if (input) {
last = input + isize;
while (input < last)
asize = b64decode(*input++, atom, asize, &buffer);
}
luaL_pushresult(&buffer);
lua_pushlstring(L, atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Quoted-printable encoding scheme
* all (except CRLF in text) can be =XX
* CLRL in not text must be =XX=XX
* 33 through 60 inclusive can be plain
* 62 through 120 inclusive can be plain
* 9 and 32 can be plain, unless in the end of a line, where must be =XX
* encoded lines must be no longer than 76 not counting CRLF
* soft line-break are =CRLF
* !"#$@[\]^`{|}~ should be =XX for EBCDIC compatibility
* To encode one byte, we need to see the next two.
* Worst case is when we see a space, and wonder if a CRLF is comming
\*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*\
* Split quoted-printable characters into classes
* Precompute reverse map for encoding
\*-------------------------------------------------------------------------*/
static void qpfill(UC *qpclass, UC *qpunbase)
{
int i;
for (i = 0; i < 256; i++) qpclass[i] = QP_QUOTED;
for (i = 33; i <= 60; i++) qpclass[i] = QP_PLAIN;
for (i = 62; i <= 120; i++) qpclass[i] = QP_PLAIN;
qpclass[HT] = QP_IF_LAST; qpclass[SP] = QP_IF_LAST;
qpclass['!'] = QP_QUOTED; qpclass['"'] = QP_QUOTED;
qpclass['#'] = QP_QUOTED; qpclass['$'] = QP_QUOTED;
qpclass['@'] = QP_QUOTED; qpclass['['] = QP_QUOTED;
qpclass['\\'] = QP_QUOTED; qpclass[']'] = QP_QUOTED;
qpclass['^'] = QP_QUOTED; qpclass['`'] = QP_QUOTED;
qpclass['{'] = QP_QUOTED; qpclass['|'] = QP_QUOTED;
qpclass['}'] = QP_QUOTED; qpclass['~'] = QP_QUOTED;
qpclass['}'] = QP_QUOTED; qpclass[CR] = QP_CR;
for (i = 0; i < 256; i++) qpunbase[i] = 255;
qpunbase['0'] = 0; qpunbase['1'] = 1; qpunbase['2'] = 2;
qpunbase['3'] = 3; qpunbase['4'] = 4; qpunbase['5'] = 5;
qpunbase['6'] = 6; qpunbase['7'] = 7; qpunbase['8'] = 8;
qpunbase['9'] = 9; qpunbase['A'] = 10; qpunbase['a'] = 10;
qpunbase['B'] = 11; qpunbase['b'] = 11; qpunbase['C'] = 12;
qpunbase['c'] = 12; qpunbase['D'] = 13; qpunbase['d'] = 13;
qpunbase['E'] = 14; qpunbase['e'] = 14; qpunbase['F'] = 15;
qpunbase['f'] = 15;
}
/*-------------------------------------------------------------------------*\
* Output one character in form =XX
\*-------------------------------------------------------------------------*/
static void qpquote(UC c, luaL_Buffer *buffer)
{
luaL_putchar(buffer, '=');
luaL_putchar(buffer, qpbase[c >> 4]);
luaL_putchar(buffer, qpbase[c & 0x0F]);
}
/*-------------------------------------------------------------------------*\
* Accumulate characters until we are sure about how to deal with them.
* Once we are sure, output the to the buffer, in the correct form.
\*-------------------------------------------------------------------------*/
static size_t qpencode(UC c, UC *input, size_t size,
const UC *marker, luaL_Buffer *buffer)
{
input[size++] = c;
/* deal with all characters we can have */
while (size > 0) {
switch (qpclass[input[0]]) {
/* might be the CR of a CRLF sequence */
case QP_CR:
if (size < 2) return size;
if (input[1] == LF) {
luaL_addstring(buffer, marker);
return 0;
} else qpquote(input[0], buffer);
break;
/* might be a space and that has to be quoted if last in line */
case QP_IF_LAST:
if (size < 3) return size;
/* if it is the last, quote it and we are done */
if (input[1] == CR && input[2] == LF) {
qpquote(input[0], buffer);
luaL_addstring(buffer, marker);
return 0;
} else luaL_putchar(buffer, input[0]);
break;
/* might have to be quoted always */
case QP_QUOTED:
qpquote(input[0], buffer);
break;
/* might never have to be quoted */
default:
luaL_putchar(buffer, input[0]);
break;
}
input[0] = input[1]; input[1] = input[2];
size--;
}
return 0;
}
/*-------------------------------------------------------------------------*\
* Deal with the final characters
\*-------------------------------------------------------------------------*/
static void qppad(UC *input, size_t size, luaL_Buffer *buffer)
{
size_t i;
for (i = 0; i < size; i++) {
if (qpclass[input[i]] == QP_PLAIN) luaL_putchar(buffer, input[i]);
else qpquote(input[i], buffer);
}
luaL_addstring(buffer, EQCRLF);
}
/*-------------------------------------------------------------------------*\
* Incrementally converts a string to quoted-printable
* A, B = qp(C, D, marker)
* Crlf is the text to be used to replace CRLF sequences found in A.
* A is the encoded version of the largest prefix of C .. D that
* can be encoded without doubts.
* B has the remaining bytes of C .. D, *without* encoding.
\*-------------------------------------------------------------------------*/
static int mime_global_qp(lua_State *L)
{
size_t asize = 0, isize = 0;
UC atom[3];
const UC *input = lua_isnil(L, 1) ? NULL: luaL_checklstring(L, 1, &isize);
const UC *last = input + isize;
const UC *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input < last)
asize = qpencode(*input++, atom, asize, marker, &buffer);
input = luaL_optlstring(L, 2, NULL, &isize);
if (input) {
last = input + isize;
while (input < last)
asize = qpencode(*input++, atom, asize, marker, &buffer);
} else qppad(atom, asize, &buffer);
luaL_pushresult(&buffer);
lua_pushlstring(L, atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Accumulate characters until we are sure about how to deal with them.
* Once we are sure, output the to the buffer, in the correct form.
\*-------------------------------------------------------------------------*/
static size_t qpdecode(UC c, UC *input, size_t size,
luaL_Buffer *buffer)
{
input[size++] = c;
/* deal with all characters we can deal */
while (size > 0) {
int c, d;
switch (input[0]) {
/* if we have an escape character */
case '=':
if (size < 3) return size;
/* eliminate soft line break */
if (input[1] == CR && input[2] == LF) return 0;
/* decode quoted representation */
c = qpunbase[input[1]]; d = qpunbase[input[2]];
/* if it is an invalid, do not decode */
if (c > 15 || d > 15) luaL_addlstring(buffer, input, 3);
else luaL_putchar(buffer, (c << 4) + d);
return 0;
case CR:
if (size < 2) return size;
if (input[1] == LF) luaL_addlstring(buffer, input, 2);
return 0;
default:
if (input[0] == HT || (input[0] > 31 && input[0] < 127))
luaL_putchar(buffer, input[0]);
return 0;
}
input[0] = input[1]; input[1] = input[2];
size--;
}
return 0;
}
/*-------------------------------------------------------------------------*\
* Incrementally decodes a string in quoted-printable
* A, B = qp(C, D)
* A is the decoded version of the largest prefix of C .. D that
* can be decoded without doubts.
* B has the remaining bytes of C .. D, *without* decoding.
\*-------------------------------------------------------------------------*/
static int mime_global_unqp(lua_State *L)
{
size_t asize = 0, isize = 0;
UC atom[3];
const UC *input = lua_isnil(L, 1) ? NULL: luaL_checklstring(L, 1, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input < last)
asize = qpdecode(*input++, atom, asize, &buffer);
input = luaL_optlstring(L, 2, NULL, &isize);
if (input) {
last = input + isize;
while (input < last)
asize = qpdecode(*input++, atom, asize, &buffer);
}
luaL_pushresult(&buffer);
lua_pushlstring(L, atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Incrementally breaks a quoted-printed string into lines
* A, n = qpfmt(B, length, left)
* A is a copy of B, broken into lines of at most 'length' bytes.
* Left is how many bytes are left in the first line of B. 'n' is the number
* of bytes left in the last line of A.
* There are two complications: lines can't be broken in the middle
* of an encoded =XX, and there might be line breaks already
\*-------------------------------------------------------------------------*/
static int mime_global_qpfmt(lua_State *L)
{
size_t size = 0;
const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &size);
const UC *last = input + size;
int length = (int) luaL_checknumber(L, 2);
int left = (int) luaL_optnumber(L, 3, length);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input < last) {
left--;
switch (*input) {
case '=':
/* if there's no room in this line for the quoted char,
* output a soft line break now */
if (left <= 3) {
luaL_addstring(&buffer, EQCRLF);
left = length;
}
break;
/* \r\n starts a new line */
case CR:
break;
case LF:
left = length;
break;
default:
/* if in last column, output a soft line break */
if (left <= 1) {
luaL_addstring(&buffer, EQCRLF);
left = length;
}
}
luaL_putchar(&buffer, *input);
input++;
}
if (!input && left < length) {
luaL_addstring(&buffer, EQCRLF);
left = length;
}
luaL_pushresult(&buffer);
lua_pushnumber(L, left);
return 2;
}
/*-------------------------------------------------------------------------*\
* Here is what we do: \n, \r and \f are considered candidates for line
* break. We issue *one* new line marker if any of them is seen alone, or
* followed by a different one. That is, \n\n, \r\r and \f\f will issue two
* end of line markers each, but \r\n, \n\r, \r\f etc will only issue *one*
* marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as
* probably other more obscure conventions.
\*-------------------------------------------------------------------------*/
#define eolcandidate(c) (c == CR || c == LF)
static size_t eolconvert(UC c, UC *input, size_t size,
const UC *marker, luaL_Buffer *buffer)
{
input[size++] = c;
/* deal with all characters we can deal */
if (eolcandidate(input[0])) {
if (size < 2) return size;
luaL_addstring(buffer, marker);
if (eolcandidate(input[1])) {
if (input[0] == input[1]) luaL_addstring(buffer, marker);
} else luaL_putchar(buffer, input[1]);
return 0;
} else {
luaL_putchar(buffer, input[0]);
return 0;
}
}
/*-------------------------------------------------------------------------*\
* Converts a string to uniform EOL convention.
* A, B = eol(C, D, marker)
* A is the converted version of the largest prefix of C .. D that
* can be converted without doubts.
* B has the remaining bytes of C .. D, *without* convertion.
\*-------------------------------------------------------------------------*/
static int mime_global_eol(lua_State *L)
{
size_t asize = 0, isize = 0;
UC atom[2];
const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &isize);
const UC *last = input + isize;
const UC *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
while (input < last)
asize = eolconvert(*input++, atom, asize, marker, &buffer);
input = luaL_optlstring(L, 2, NULL, &isize);
if (input) {
last = input + isize;
while (input < last)
asize = eolconvert(*input++, atom, asize, marker, &buffer);
/* if there is something in atom, it's one character, and it
* is a candidate. so we output a new line */
} else if (asize > 0) luaL_addstring(&buffer, marker);
luaL_pushresult(&buffer);
lua_pushlstring(L, atom, asize);
return 2;
}

17
src/mime.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef MIME_H
#define MIME_H
/*=========================================================================*\
* Mime support functions
* LuaSocket toolkit
*
* This module provides functions to implement transfer content encodings
* and formatting conforming to RFC 2045. It is used by mime.lua, which
* provide a higher level interface to this functionality.
*
* RCS ID: $Id$
\*=========================================================================*/
#include <lua.h>
void mime_open(lua_State *L);
#endif /* MIME_H */

104
src/mime.lua Normal file
View file

@ -0,0 +1,104 @@
-- make sure LuaSocket is loaded
if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end
-- get LuaSocket namespace
local socket = _G[LUASOCKET_LIBNAME]
if not socket then error('module requires LuaSocket') end
-- create code namespace inside LuaSocket namespace
local mime = socket.mime or {}
socket.mime = mime
-- make all module globals fall into mime namespace
setmetatable(mime, { __index = _G })
setfenv(1, mime)
base64 = {}
qprint = {}
function base64.encode()
local unfinished = ""
return function(chunk)
local done
done, unfinished = b64(unfinished, chunk)
return done
end
end
function base64.decode()
local unfinished = ""
return function(chunk)
local done
done, unfinished = unb64(unfinished, chunk)
return done
end
end
function qprint.encode(mode)
mode = (mode == "binary") and "=0D=0A" or "\13\10"
local unfinished = ""
return function(chunk)
local done
done, unfinished = qp(unfinished, chunk, mode)
return done
end
end
function qprint.decode()
local unfinished = ""
return function(chunk)
local done
done, unfinished = unqp(unfinished, chunk)
return done
end
end
function split(length, marker)
length = length or 76
local left = length
return function(chunk)
local done
done, left = fmt(chunk, length, left, marker)
return done
end
end
function qprint.split(length)
length = length or 76
local left = length
return function(chunk)
local done
done, left = qpfmt(chunk, length, left)
return done
end
end
function canonic(marker)
local unfinished = ""
return function(chunk)
local done
done, unfinished = eol(unfinished, chunk, marker)
return done
end
end
function chain(...)
local layers = table.getn(arg)
return function (chunk)
if not chunk then
local parts = {}
for i = 1, layers do
for j = i, layers do
chunk = arg[j](chunk)
end
table.insert(parts, chunk)
chunk = nil
end
return table.concat(parts)
else
for j = 1, layers do
chunk = arg[j](chunk)
end
return chunk
end
end
end
return code

View file

@ -238,7 +238,7 @@ static int meth_bind(lua_State *L)
return 2;
}
/* turn master object into a server object if there was a listen */
if (backlog > 0) aux_setclass(L, "tcp{server}", 1);
if (backlog >= 0) aux_setclass(L, "tcp{server}", 1);
lua_pushnumber(L, 1);
return 1;
}

View file

@ -6,9 +6,102 @@
-- RCS ID: $Id$
----------------------------------------------------------------------------
local Public, Private = {}, {}
local socket = _G[LUASOCKET_LIBNAME] -- get LuaSocket namespace
socket.url = Public
-- make sure LuaSocket is loaded
if not LUASOCKET_LIBNAME then error('module requires LuaSocket') end
-- get LuaSocket namespace
local socket = _G[LUASOCKET_LIBNAME]
if not socket then error('module requires LuaSocket') end
-- create smtp namespace inside LuaSocket namespace
local url = {}
socket.url = url
-- make all module globals fall into smtp namespace
setmetatable(url, { __index = _G })
setfenv(1, url)
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
function escape(s)
return string.gsub(s, "(.)", function(c)
return string.format("%%%02x", string.byte(c))
end)
end
-----------------------------------------------------------------------------
-- Protects a path segment, to prevent it from interfering with the
-- url parsing.
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
local function make_set(t)
local s = {}
for i = 1, table.getn(t) do
s[t[i]] = 1
end
return s
end
-- these are allowed withing a path segment, along with alphanum
-- other characters must be escaped
local segment_set = make_set {
"-", "_", ".", "!", "~", "*", "'", "(",
")", ":", "@", "&", "=", "+", "$", ",",
}
local function protect_segment(s)
return string.gsub(s, "(%W)", function (c)
if segment_set[c] then return c
else return escape(c) end
end)
end
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
function unescape(s)
return string.gsub(s, "%%(%x%x)", function(hex)
return string.char(tonumber(hex, 16))
end)
end
-----------------------------------------------------------------------------
-- Builds a path from a base path and a relative path
-- Input
-- base_path
-- relative_path
-- Returns
-- corresponding absolute path
-----------------------------------------------------------------------------
local function absolute_path(base_path, relative_path)
if string.sub(relative_path, 1, 1) == "/" then return relative_path end
local path = string.gsub(base_path, "[^/]*$", "")
path = path .. relative_path
path = string.gsub(path, "([^/]*%./)", function (s)
if s ~= "./" then return s else return "" end
end)
path = string.gsub(path, "/%.$", "/")
local reduced
while reduced ~= path do
reduced = path
path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
if s ~= "../../" then return "" else return s end
end)
end
path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
if s ~= "../.." then return "" else return s end
end)
return path
end
-----------------------------------------------------------------------------
-- Parses a url and returns a table with all its parts according to RFC 2396
@ -28,7 +121,7 @@ socket.url = Public
-- Obs:
-- the leading '/' in {/<path>} is considered part of <path>
-----------------------------------------------------------------------------
function Public.parse(url, default)
function parse(url, default)
-- initialize default parameters
local parsed = default or {}
-- empty url is parsed to nil
@ -66,11 +159,11 @@ end
-- Rebuilds a parsed URL from its components.
-- Components are protected if any reserved or unallowed characters are found
-- Input
-- parsed: parsed URL, as returned by Public.parse
-- parsed: parsed URL, as returned by parse
-- Returns
-- a stringing with the corresponding URL
-----------------------------------------------------------------------------
function Public.build(parsed)
function build(parsed)
local url = parsed.path or ""
if parsed.params then url = url .. ";" .. parsed.params end
if parsed.query then url = url .. "?" .. parsed.query end
@ -102,9 +195,9 @@ end
-- Returns
-- corresponding absolute url
-----------------------------------------------------------------------------
function Public.absolute(base_url, relative_url)
local base = Public.parse(base_url)
local relative = Public.parse(relative_url)
function absolute(base_url, relative_url)
local base = parse(base_url)
local relative = parse(relative_url)
if not base then return relative_url
elseif not relative then return base_url
elseif relative.scheme then return relative_url
@ -121,10 +214,10 @@ function Public.absolute(base_url, relative_url)
end
end
else
relative.path = Private.absolute_path(base.path,relative.path)
relative.path = absolute_path(base.path,relative.path)
end
end
return Public.build(relative)
return build(relative)
end
end
@ -135,13 +228,13 @@ end
-- Returns
-- segment: a table with one entry per segment
-----------------------------------------------------------------------------
function Public.parse_path(path)
function parse_path(path)
local parsed = {}
path = path or ""
path = string.gsub(path, "%s", "")
string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
for i = 1, table.getn(parsed) do
parsed[i] = socket.code.unescape(parsed[i])
parsed[i] = unescape(parsed[i])
end
if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
@ -154,9 +247,9 @@ end
-- parsed: path segments
-- unsafe: if true, segments are not protected before path is built
-- Returns
-- path: correspondin path stringing
-- path: corresponding path stringing
-----------------------------------------------------------------------------
function Public.build_path(parsed, unsafe)
function build_path(parsed, unsafe)
local path = ""
local n = table.getn(parsed)
if unsafe then
@ -170,66 +263,14 @@ function Public.build_path(parsed, unsafe)
end
else
for i = 1, n-1 do
path = path .. Private.protect_segment(parsed[i])
path = path .. protect_segment(parsed[i])
path = path .. "/"
end
if n > 0 then
path = path .. Private.protect_segment(parsed[n])
path = path .. protect_segment(parsed[n])
if parsed.is_directory then path = path .. "/" end
end
end
if parsed.is_absolute then path = "/" .. path end
return path
end
function Private.make_set(t)
local s = {}
for i = 1, table.getn(t) do
s[t[i]] = 1
end
return s
end
-- these are allowed withing a path segment, along with alphanum
-- other characters must be escaped
Private.segment_set = Private.make_set {
"-", "_", ".", "!", "~", "*", "'", "(",
")", ":", "@", "&", "=", "+", "$", ",",
}
function Private.protect_segment(s)
local segment_set = Private.segment_set
return string.gsub(s, "(%W)", function (c)
if segment_set[c] then return c
else return socket.code.escape(c) end
end)
end
-----------------------------------------------------------------------------
-- Builds a path from a base path and a relative path
-- Input
-- base_path
-- relative_path
-- Returns
-- corresponding absolute path
-----------------------------------------------------------------------------
function Private.absolute_path(base_path, relative_path)
if string.sub(relative_path, 1, 1) == "/" then return relative_path end
local path = string.gsub(base_path, "[^/]*$", "")
path = path .. relative_path
path = string.gsub(path, "([^/]*%./)", function (s)
if s ~= "./" then return s else return "" end
end)
path = string.gsub(path, "/%.$", "/")
local reduced
while reduced ~= path do
reduced = path
path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
if s ~= "../../" then return "" else return s end
end)
end
path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
if s ~= "../.." then return "" else return s end
end)
return path
end