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 |