742 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			742 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /*=========================================================================*\
 | ||
|  | * MIME support functions | ||
|  | * LuaSocket toolkit | ||
|  | \*=========================================================================*/ | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | #include "lua.h"
 | ||
|  | #include "lauxlib.h"
 | ||
|  | 
 | ||
|  | #if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501)
 | ||
|  | #include "compat-5.1.h"
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #include "mime.h"
 | ||
|  | 
 | ||
|  | #ifndef _WIN32
 | ||
|  | #pragma clang diagnostic push
 | ||
|  | #pragma clang diagnostic ignored "-Wchar-subscripts"
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 
 | ||
|  | namespace NS_SLUA {     | ||
|  | 
 | ||
|  | /*=========================================================================*\
 | ||
|  | * Don't want to trust escape character constants | ||
|  | \*=========================================================================*/ | ||
|  | typedef unsigned char UC; | ||
|  | static const char CRLF[] = "\r\n"; | ||
|  | static const char EQCRLF[] = "=\r\n"; | ||
|  | 
 | ||
|  | /*=========================================================================*\
 | ||
|  | * Internal function prototypes. | ||
|  | \*=========================================================================*/ | ||
|  | static int mime_global_wrp(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_qpwrp(lua_State *L); | ||
|  | static int mime_global_eol(lua_State *L); | ||
|  | static int mime_global_dot(lua_State *L); | ||
|  | 
 | ||
|  | static size_t dot(int c, size_t state, luaL_Buffer *buffer); | ||
|  | static void b64setup(UC *base); | ||
|  | 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 qpsetup(UC *cl, UC *unbase); | ||
|  | 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 char *marker, luaL_Buffer *buffer); | ||
|  | static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer); | ||
|  | 
 | ||
|  | /* code support functions */ | ||
|  | static luaL_Reg mine_func[] = { | ||
|  |     { "dot", mime_global_dot }, | ||
|  |     { "b64", mime_global_b64 }, | ||
|  |     { "eol", mime_global_eol }, | ||
|  |     { "qp", mime_global_qp }, | ||
|  |     { "qpwrp", mime_global_qpwrp }, | ||
|  |     { "unb64", mime_global_unb64 }, | ||
|  |     { "unqp", mime_global_unqp }, | ||
|  |     { "wrp", mime_global_wrp }, | ||
|  |     { 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 | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | MIME_API int luaopen_mime_core(lua_State *L) | ||
|  | { | ||
|  | #if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
 | ||
|  |     lua_newtable(L); | ||
|  |     luaL_setfuncs(L, mine_func, 0); | ||
|  | #else
 | ||
|  |     luaL_openlib(L, "mime", mine_func, 0); | ||
|  | #endif
 | ||
|  |     /* make version string available to scripts */ | ||
|  |     lua_pushstring(L, "_VERSION"); | ||
|  |     lua_pushstring(L, MIME_VERSION); | ||
|  |     lua_rawset(L, -3); | ||
|  |     /* initialize lookup tables */ | ||
|  |     qpsetup(qpclass, qpunbase); | ||
|  |     b64setup(b64unbase); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /*=========================================================================*\
 | ||
|  | * Global Lua functions | ||
|  | \*=========================================================================*/ | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Incrementaly breaks a string into lines. The string can have CRLF breaks. | ||
|  | * A, n = wrp(l, B, length) | ||
|  | * A is a copy of B, broken into lines of at most 'length' bytes.  | ||
|  | * 'l' is how many bytes are left for the first line of B.  | ||
|  | * 'n' is the number of bytes left in the last line of A.  | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int mime_global_wrp(lua_State *L) | ||
|  | { | ||
|  |     size_t size = 0; | ||
|  |     int left = (int) luaL_checknumber(L, 1); | ||
|  |     const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size); | ||
|  |     const UC *last = input + size; | ||
|  |     int length = (int) luaL_optnumber(L, 3, 76); | ||
|  |     luaL_Buffer buffer; | ||
|  |     /* end of input black-hole */ | ||
|  |     if (!input) { | ||
|  |         /* if last line has not been terminated, add a line break */ | ||
|  |         if (left < length) lua_pushstring(L, CRLF); | ||
|  |         /* otherwise, we are done */ | ||
|  |         else lua_pushnil(L); | ||
|  |         lua_pushnumber(L, length); | ||
|  |         return 2; | ||
|  |     }  | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     while (input < last) { | ||
|  |         switch (*input) { | ||
|  |             case '\r': | ||
|  |                 break; | ||
|  |             case '\n': | ||
|  |                 luaL_addstring(&buffer, CRLF); | ||
|  |                 left = length; | ||
|  |                 break; | ||
|  |             default: | ||
|  |                 if (left <= 0) { | ||
|  |                     left = length; | ||
|  |                     luaL_addstring(&buffer, CRLF); | ||
|  |                 } | ||
|  |                 luaL_addchar(&buffer, *input); | ||
|  |                 left--; | ||
|  |                 break; | ||
|  |         } | ||
|  |         input++; | ||
|  |     } | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushnumber(L, left); | ||
|  |     return 2; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Fill base64 decode map.  | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static void b64setup(UC *unbase)  | ||
|  | { | ||
|  |     int i; | ||
|  |     for (i = 0; i <= 255; i++) unbase[i] = (UC) 255; | ||
|  |     for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i; | ||
|  |     unbase['='] = 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, (char *) 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, (char *) 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, (char *) code, 4); | ||
|  |             break; | ||
|  |         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, (char *) 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 = (UC *) luaL_optlstring(L, 1, NULL, &isize); | ||
|  |     const UC *last = input + isize; | ||
|  |     luaL_Buffer buffer; | ||
|  |     /* end-of-input blackhole */ | ||
|  |     if (!input) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* make sure we don't confuse buffer stuff with arguments */ | ||
|  |     lua_settop(L, 2); | ||
|  |     /* process first part of the input */ | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     while (input < last)  | ||
|  |         asize = b64encode(*input++, atom, asize, &buffer); | ||
|  |     input = (UC *) luaL_optlstring(L, 2, NULL, &isize); | ||
|  |     /* if second part is nil, we are done */ | ||
|  |     if (!input) { | ||
|  |         size_t osize = 0; | ||
|  |         asize = b64pad(atom, asize, &buffer); | ||
|  |         luaL_pushresult(&buffer); | ||
|  |         /* if the output is empty  and the input is nil, return nil */ | ||
|  |         lua_tolstring(L, -1, &osize); | ||
|  |         if (osize == 0) lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* otherwise process the second part */ | ||
|  |     last = input + isize; | ||
|  |     while (input < last)  | ||
|  |         asize = b64encode(*input++, atom, asize, &buffer); | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushlstring(L, (char *) 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 = (UC *) luaL_optlstring(L, 1, NULL, &isize); | ||
|  |     const UC *last = input + isize; | ||
|  |     luaL_Buffer buffer; | ||
|  |     /* end-of-input blackhole */ | ||
|  |     if (!input) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* make sure we don't confuse buffer stuff with arguments */ | ||
|  |     lua_settop(L, 2); | ||
|  |     /* process first part of the input */ | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     while (input < last)  | ||
|  |         asize = b64decode(*input++, atom, asize, &buffer); | ||
|  |     input = (UC *) luaL_optlstring(L, 2, NULL, &isize); | ||
|  |     /* if second is nil, we are done */ | ||
|  |     if (!input) { | ||
|  |         size_t osize = 0; | ||
|  |         luaL_pushresult(&buffer); | ||
|  |         /* if the output is empty  and the input is nil, return nil */ | ||
|  |         lua_tolstring(L, -1, &osize); | ||
|  |         if (osize == 0) lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* otherwise, process the rest of the input */ | ||
|  |     last = input + isize; | ||
|  |     while (input < last)  | ||
|  |         asize = b64decode(*input++, atom, asize, &buffer); | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushlstring(L, (char *) 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 126 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 | ||
|  | * 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 qpsetup(UC *cl, UC *unbase) | ||
|  | { | ||
|  |     int i; | ||
|  |     for (i = 0; i < 256; i++) cl[i] = QP_QUOTED; | ||
|  |     for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN; | ||
|  |     for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN; | ||
|  |     cl['\t'] = QP_IF_LAST;  | ||
|  |     cl[' '] = QP_IF_LAST; | ||
|  |     cl['\r'] = QP_CR; | ||
|  |     for (i = 0; i < 256; i++) unbase[i] = 255; | ||
|  |     unbase['0'] = 0; unbase['1'] = 1; unbase['2'] = 2; | ||
|  |     unbase['3'] = 3; unbase['4'] = 4; unbase['5'] = 5; | ||
|  |     unbase['6'] = 6; unbase['7'] = 7; unbase['8'] = 8; | ||
|  |     unbase['9'] = 9; unbase['A'] = 10; unbase['a'] = 10; | ||
|  |     unbase['B'] = 11; unbase['b'] = 11; unbase['C'] = 12; | ||
|  |     unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13; | ||
|  |     unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15; | ||
|  |     unbase['f'] = 15; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Output one character in form =XX | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static void qpquote(UC c, luaL_Buffer *buffer) | ||
|  | { | ||
|  |     luaL_addchar(buffer, '='); | ||
|  |     luaL_addchar(buffer, qpbase[c >> 4]); | ||
|  |     luaL_addchar(buffer, qpbase[c & 0x0F]); | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Accumulate characters until we are sure about how to deal with them. | ||
|  | * Once we are sure, output to the buffer, in the correct form.  | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static size_t qpencode(UC c, UC *input, size_t size,  | ||
|  |         const char *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] == '\n') { | ||
|  |                     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] == '\r' && input[2] == '\n') { | ||
|  |                     qpquote(input[0], buffer); | ||
|  |                     luaL_addstring(buffer, marker); | ||
|  |                     return 0; | ||
|  |                 } else luaL_addchar(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_addchar(buffer, input[0]); | ||
|  |                 break; | ||
|  |         } | ||
|  |         input[0] = input[1]; input[1] = input[2]; | ||
|  |         size--; | ||
|  |     } | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Deal with the final characters  | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static size_t 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_addchar(buffer, input[i]); | ||
|  |         else qpquote(input[i], buffer); | ||
|  |     } | ||
|  |     if (size > 0) luaL_addstring(buffer, EQCRLF); | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Incrementally converts a string to quoted-printable | ||
|  | * A, B = qp(C, D, marker) | ||
|  | * Marker 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 = (UC *) luaL_optlstring(L, 1, NULL, &isize); | ||
|  |     const UC *last = input + isize; | ||
|  |     const char *marker = luaL_optstring(L, 3, CRLF); | ||
|  |     luaL_Buffer buffer; | ||
|  |     /* end-of-input blackhole */ | ||
|  |     if (!input) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* make sure we don't confuse buffer stuff with arguments */ | ||
|  |     lua_settop(L, 3); | ||
|  |     /* process first part of input */ | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     while (input < last) | ||
|  |         asize = qpencode(*input++, atom, asize, marker, &buffer); | ||
|  |     input = (UC *) luaL_optlstring(L, 2, NULL, &isize); | ||
|  |     /* if second part is nil, we are done */ | ||
|  |     if (!input) { | ||
|  |         asize = qppad(atom, asize, &buffer); | ||
|  |         luaL_pushresult(&buffer); | ||
|  |         if (!(*lua_tostring(L, -1))) lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* otherwise process rest of input */ | ||
|  |     last = input + isize; | ||
|  |     while (input < last) | ||
|  |         asize = qpencode(*input++, atom, asize, marker, &buffer); | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushlstring(L, (char *) 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) { | ||
|  |     int d; | ||
|  |     input[size++] = c; | ||
|  |     /* deal with all characters we can deal */ | ||
|  |     switch (input[0]) { | ||
|  |         /* if we have an escape character */ | ||
|  |         case '=':  | ||
|  |             if (size < 3) return size;  | ||
|  |             /* eliminate soft line break */ | ||
|  |             if (input[1] == '\r' && input[2] == '\n') 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, (char *)input, 3); | ||
|  |             else luaL_addchar(buffer, (char) ((c << 4) + d)); | ||
|  |             return 0; | ||
|  |         case '\r': | ||
|  |             if (size < 2) return size;  | ||
|  |             if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2); | ||
|  |             return 0; | ||
|  |         default: | ||
|  |             if (input[0] == '\t' || (input[0] > 31 && input[0] < 127)) | ||
|  |                 luaL_addchar(buffer, input[0]); | ||
|  |             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 = (UC *) luaL_optlstring(L, 1, NULL, &isize); | ||
|  |     const UC *last = input + isize; | ||
|  |     luaL_Buffer buffer; | ||
|  |     /* end-of-input blackhole */ | ||
|  |     if (!input) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* make sure we don't confuse buffer stuff with arguments */ | ||
|  |     lua_settop(L, 2); | ||
|  |     /* process first part of input */ | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     while (input < last) | ||
|  |         asize = qpdecode(*input++, atom, asize, &buffer); | ||
|  |     input = (UC *) luaL_optlstring(L, 2, NULL, &isize); | ||
|  |     /* if second part is nil, we are done */ | ||
|  |     if (!input) { | ||
|  |         luaL_pushresult(&buffer); | ||
|  |         if (!(*lua_tostring(L, -1))) lua_pushnil(L); | ||
|  |         lua_pushnil(L); | ||
|  |         return 2; | ||
|  |     }  | ||
|  |     /* otherwise process rest of input */ | ||
|  |     last = input + isize; | ||
|  |     while (input < last) | ||
|  |         asize = qpdecode(*input++, atom, asize, &buffer); | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushlstring(L, (char *) atom, asize); | ||
|  |     return 2; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Incrementally breaks a quoted-printed string into lines | ||
|  | * A, n = qpwrp(l, B, length) | ||
|  | * A is a copy of B, broken into lines of at most 'length' bytes.  | ||
|  | * 'l' is how many bytes are left for 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_qpwrp(lua_State *L) | ||
|  | { | ||
|  |     size_t size = 0; | ||
|  |     int left = (int) luaL_checknumber(L, 1); | ||
|  |     const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size); | ||
|  |     const UC *last = input + size; | ||
|  |     int length = (int) luaL_optnumber(L, 3, 76); | ||
|  |     luaL_Buffer buffer; | ||
|  |     /* end-of-input blackhole */ | ||
|  |     if (!input) { | ||
|  |         if (left < length) lua_pushstring(L, EQCRLF); | ||
|  |         else lua_pushnil(L); | ||
|  |         lua_pushnumber(L, length); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* process all input */ | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     while (input < last) { | ||
|  |         switch (*input) { | ||
|  |             case '\r': | ||
|  |                 break; | ||
|  |             case '\n': | ||
|  |                 left = length; | ||
|  |                 luaL_addstring(&buffer, CRLF); | ||
|  |                 break; | ||
|  |             case '=': | ||
|  |                 if (left <= 3) { | ||
|  |                     left = length; | ||
|  |                     luaL_addstring(&buffer, EQCRLF); | ||
|  |                 }  | ||
|  |                 luaL_addchar(&buffer, *input); | ||
|  |                 left--; | ||
|  |                 break; | ||
|  |             default:  | ||
|  |                 if (left <= 1) { | ||
|  |                     left = length; | ||
|  |                     luaL_addstring(&buffer, EQCRLF); | ||
|  |                 } | ||
|  |                 luaL_addchar(&buffer, *input); | ||
|  |                 left--; | ||
|  |                 break; | ||
|  |         } | ||
|  |         input++; | ||
|  |     } | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushnumber(L, left); | ||
|  |     return 2; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Here is what we do: \n, and \r 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 and \r\r will issue two | ||
|  | * end of line markers each, but \r\n, \n\r 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. | ||
|  | * | ||
|  | * c is the current character being processed | ||
|  | * last is the previous character | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | #define eolcandidate(c) (c == '\r' || c == '\n')
 | ||
|  | static int eolprocess(int c, int last, const char *marker,  | ||
|  |         luaL_Buffer *buffer) | ||
|  | { | ||
|  |     if (eolcandidate(c)) { | ||
|  |         if (eolcandidate(last)) { | ||
|  |             if (c == last) luaL_addstring(buffer, marker); | ||
|  |             return 0; | ||
|  |         } else { | ||
|  |             luaL_addstring(buffer, marker); | ||
|  |             return c; | ||
|  |         } | ||
|  |     } else { | ||
|  |         luaL_addchar(buffer, (char) c); | ||
|  |         return 0; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Converts a string to uniform EOL convention.  | ||
|  | * A, n = eol(o, B, marker) | ||
|  | * A is the converted version of the largest prefix of B that can be | ||
|  | * converted unambiguously. 'o' is the context returned by the previous  | ||
|  | * call. 'n' is the new context. | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int mime_global_eol(lua_State *L) | ||
|  | { | ||
|  |     int ctx = luaL_checkinteger(L, 1); | ||
|  |     size_t isize = 0; | ||
|  |     const char *input = luaL_optlstring(L, 2, NULL, &isize); | ||
|  |     const char *last = input + isize; | ||
|  |     const char *marker = luaL_optstring(L, 3, CRLF); | ||
|  |     luaL_Buffer buffer; | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     /* end of input blackhole */ | ||
|  |     if (!input) { | ||
|  |        lua_pushnil(L); | ||
|  |        lua_pushnumber(L, 0); | ||
|  |        return 2; | ||
|  |     } | ||
|  |     /* process all input */ | ||
|  |     while (input < last) | ||
|  |         ctx = eolprocess(*input++, ctx, marker, &buffer); | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushnumber(L, ctx); | ||
|  |     return 2; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Takes one byte and stuff it if needed.  | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static size_t dot(int c, size_t state, luaL_Buffer *buffer) | ||
|  | { | ||
|  |     luaL_addchar(buffer, (char) c); | ||
|  |     switch (c) { | ||
|  |         case '\r':  | ||
|  |             return 1; | ||
|  |         case '\n':  | ||
|  |             return (state == 1)? 2: 0;  | ||
|  |         case '.':   | ||
|  |             if (state == 2)  | ||
|  |                 luaL_addchar(buffer, '.'); | ||
|  |         default: | ||
|  |             return 0; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Incrementally applies smtp stuffing to a string | ||
|  | * A, n = dot(l, D) | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int mime_global_dot(lua_State *L) | ||
|  | { | ||
|  |     size_t isize = 0, state = (size_t) luaL_checknumber(L, 1); | ||
|  |     const char *input = luaL_optlstring(L, 2, NULL, &isize); | ||
|  |     const char *last = input + isize; | ||
|  |     luaL_Buffer buffer; | ||
|  |     /* end-of-input blackhole */ | ||
|  |     if (!input) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushnumber(L, 2); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* process all input */ | ||
|  |     luaL_buffinit(L, &buffer); | ||
|  |     while (input < last)  | ||
|  |         state = dot(*input++, state, &buffer); | ||
|  |     luaL_pushresult(&buffer); | ||
|  |     lua_pushnumber(L, (lua_Number) state); | ||
|  |     return 2; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | } // end NS_SLUA
 | ||
|  | 
 | ||
|  | #ifndef _WIN32
 | ||
|  | #pragma clang diagnostic pop
 | ||
|  | #endif
 |