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