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 |