1 /*=========================================================================*\
2 * MIME support functions
3 * LuaSocket toolkit
4 \*=========================================================================*/
5 #include <string.h>
6 
7 #include "lua.h"
8 #include "lauxlib.h"
9 #include "compat.h"
10 
11 #include "mime.h"
12 
13 /*=========================================================================*\
14 * Don't want to trust escape character constants
15 \*=========================================================================*/
16 typedef unsigned char UC;
17 static const char CRLF[] = "\r\n";
18 static const char EQCRLF[] = "=\r\n";
19 
20 /*=========================================================================*\
21 * Internal function prototypes.
22 \*=========================================================================*/
23 static int mime_global_wrp(lua_State *L);
24 static int mime_global_b64(lua_State *L);
25 static int mime_global_unb64(lua_State *L);
26 static int mime_global_qp(lua_State *L);
27 static int mime_global_unqp(lua_State *L);
28 static int mime_global_qpwrp(lua_State *L);
29 static int mime_global_eol(lua_State *L);
30 static int mime_global_dot(lua_State *L);
31 
32 static size_t dot(int c, size_t state, luaL_Buffer *buffer);
33 static void b64setup(UC *base);
34 static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
35 static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer);
36 static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
37 
38 static void qpsetup(UC *class, UC *unbase);
39 static void qpquote(UC c, luaL_Buffer *buffer);
40 static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
41 static size_t qpencode(UC c, UC *input, size_t size,
42         const char *marker, luaL_Buffer *buffer);
43 static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer);
44 
45 /* code support functions */
46 static luaL_Reg func[] = {
47     { "dot", mime_global_dot },
48     { "b64", mime_global_b64 },
49     { "eol", mime_global_eol },
50     { "qp", mime_global_qp },
51     { "qpwrp", mime_global_qpwrp },
52     { "unb64", mime_global_unb64 },
53     { "unqp", mime_global_unqp },
54     { "wrp", mime_global_wrp },
55     { NULL, NULL }
56 };
57 
58 /*-------------------------------------------------------------------------*\
59 * Quoted-printable globals
60 \*-------------------------------------------------------------------------*/
61 static UC qpclass[256];
62 static UC qpbase[] = "0123456789ABCDEF";
63 static UC qpunbase[256];
64 enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST};
65 
66 /*-------------------------------------------------------------------------*\
67 * Base64 globals
68 \*-------------------------------------------------------------------------*/
69 static const UC b64base[] =
70         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
71 static UC b64unbase[256];
72 
73 /*=========================================================================*\
74 * Exported functions
75 \*=========================================================================*/
76 /*-------------------------------------------------------------------------*\
77 * Initializes module
78 \*-------------------------------------------------------------------------*/
luaopen_mime_core(lua_State * L)79 MIME_API int luaopen_mime_core(lua_State *L)
80 {
81     lua_newtable(L);
82     luaL_setfuncs(L, func, 0);
83     /* make version string available to scripts */
84     lua_pushstring(L, "_VERSION");
85     lua_pushstring(L, MIME_VERSION);
86     lua_rawset(L, -3);
87     /* initialize lookup tables */
88     qpsetup(qpclass, qpunbase);
89     b64setup(b64unbase);
90     return 1;
91 }
92 
93 /*=========================================================================*\
94 * Global Lua functions
95 \*=========================================================================*/
96 /*-------------------------------------------------------------------------*\
97 * Incrementaly breaks a string into lines. The string can have CRLF breaks.
98 * A, n = wrp(l, B, length)
99 * A is a copy of B, broken into lines of at most 'length' bytes.
100 * 'l' is how many bytes are left for the first line of B.
101 * 'n' is the number of bytes left in the last line of A.
102 \*-------------------------------------------------------------------------*/
mime_global_wrp(lua_State * L)103 static int mime_global_wrp(lua_State *L)
104 {
105     size_t size = 0;
106     int left = (int) luaL_checknumber(L, 1);
107     const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
108     const UC *last = input + size;
109     int length = (int) luaL_optnumber(L, 3, 76);
110     luaL_Buffer buffer;
111     /* end of input black-hole */
112     if (!input) {
113         /* if last line has not been terminated, add a line break */
114         if (left < length) lua_pushstring(L, CRLF);
115         /* otherwise, we are done */
116         else lua_pushnil(L);
117         lua_pushnumber(L, length);
118         return 2;
119     }
120     luaL_buffinit(L, &buffer);
121     while (input < last) {
122         switch (*input) {
123             case '\r':
124                 break;
125             case '\n':
126                 luaL_addstring(&buffer, CRLF);
127                 left = length;
128                 break;
129             default:
130                 if (left <= 0) {
131                     left = length;
132                     luaL_addstring(&buffer, CRLF);
133                 }
134                 luaL_addchar(&buffer, *input);
135                 left--;
136                 break;
137         }
138         input++;
139     }
140     luaL_pushresult(&buffer);
141     lua_pushnumber(L, left);
142     return 2;
143 }
144 
145 /*-------------------------------------------------------------------------*\
146 * Fill base64 decode map.
147 \*-------------------------------------------------------------------------*/
b64setup(UC * unbase)148 static void b64setup(UC *unbase)
149 {
150     int i;
151     for (i = 0; i <= 255; i++) unbase[i] = (UC) 255;
152     for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i;
153     unbase['='] = 0;
154 }
155 
156 /*-------------------------------------------------------------------------*\
157 * Acumulates bytes in input buffer until 3 bytes are available.
158 * Translate the 3 bytes into Base64 form and append to buffer.
159 * Returns new number of bytes in buffer.
160 \*-------------------------------------------------------------------------*/
b64encode(UC c,UC * input,size_t size,luaL_Buffer * buffer)161 static size_t b64encode(UC c, UC *input, size_t size,
162         luaL_Buffer *buffer)
163 {
164     input[size++] = c;
165     if (size == 3) {
166         UC code[4];
167         unsigned long value = 0;
168         value += input[0]; value <<= 8;
169         value += input[1]; value <<= 8;
170         value += input[2];
171         code[3] = b64base[value & 0x3f]; value >>= 6;
172         code[2] = b64base[value & 0x3f]; value >>= 6;
173         code[1] = b64base[value & 0x3f]; value >>= 6;
174         code[0] = b64base[value];
175         luaL_addlstring(buffer, (char *) code, 4);
176         size = 0;
177     }
178     return size;
179 }
180 
181 /*-------------------------------------------------------------------------*\
182 * Encodes the Base64 last 1 or 2 bytes and adds padding '='
183 * Result, if any, is appended to buffer.
184 * Returns 0.
185 \*-------------------------------------------------------------------------*/
b64pad(const UC * input,size_t size,luaL_Buffer * buffer)186 static size_t b64pad(const UC *input, size_t size,
187         luaL_Buffer *buffer)
188 {
189     unsigned long value = 0;
190     UC code[4] = {'=', '=', '=', '='};
191     switch (size) {
192         case 1:
193             value = input[0] << 4;
194             code[1] = b64base[value & 0x3f]; value >>= 6;
195             code[0] = b64base[value];
196             luaL_addlstring(buffer, (char *) code, 4);
197             break;
198         case 2:
199             value = input[0]; value <<= 8;
200             value |= input[1]; value <<= 2;
201             code[2] = b64base[value & 0x3f]; value >>= 6;
202             code[1] = b64base[value & 0x3f]; value >>= 6;
203             code[0] = b64base[value];
204             luaL_addlstring(buffer, (char *) code, 4);
205             break;
206         default:
207             break;
208     }
209     return 0;
210 }
211 
212 /*-------------------------------------------------------------------------*\
213 * Acumulates bytes in input buffer until 4 bytes are available.
214 * Translate the 4 bytes from Base64 form and append to buffer.
215 * Returns new number of bytes in buffer.
216 \*-------------------------------------------------------------------------*/
b64decode(UC c,UC * input,size_t size,luaL_Buffer * buffer)217 static size_t b64decode(UC c, UC *input, size_t size,
218         luaL_Buffer *buffer)
219 {
220     /* ignore invalid characters */
221     if (b64unbase[c] > 64) return size;
222     input[size++] = c;
223     /* decode atom */
224     if (size == 4) {
225         UC decoded[3];
226         int valid, value = 0;
227         value =  b64unbase[input[0]]; value <<= 6;
228         value |= b64unbase[input[1]]; value <<= 6;
229         value |= b64unbase[input[2]]; value <<= 6;
230         value |= b64unbase[input[3]];
231         decoded[2] = (UC) (value & 0xff); value >>= 8;
232         decoded[1] = (UC) (value & 0xff); value >>= 8;
233         decoded[0] = (UC) value;
234         /* take care of paddding */
235         valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3;
236         luaL_addlstring(buffer, (char *) decoded, valid);
237         return 0;
238     /* need more data */
239     } else return size;
240 }
241 
242 /*-------------------------------------------------------------------------*\
243 * Incrementally applies the Base64 transfer content encoding to a string
244 * A, B = b64(C, D)
245 * A is the encoded version of the largest prefix of C .. D that is
246 * divisible by 3. B has the remaining bytes of C .. D, *without* encoding.
247 * The easiest thing would be to concatenate the two strings and
248 * encode the result, but we can't afford that or Lua would dupplicate
249 * every chunk we received.
250 \*-------------------------------------------------------------------------*/
mime_global_b64(lua_State * L)251 static int mime_global_b64(lua_State *L)
252 {
253     UC atom[3];
254     size_t isize = 0, asize = 0;
255     const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
256     const UC *last = input + isize;
257     luaL_Buffer buffer;
258     /* end-of-input blackhole */
259     if (!input) {
260         lua_pushnil(L);
261         lua_pushnil(L);
262         return 2;
263     }
264     /* make sure we don't confuse buffer stuff with arguments */
265     lua_settop(L, 2);
266     /* process first part of the input */
267     luaL_buffinit(L, &buffer);
268     while (input < last)
269         asize = b64encode(*input++, atom, asize, &buffer);
270     input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
271     /* if second part is nil, we are done */
272     if (!input) {
273         size_t osize = 0;
274         asize = b64pad(atom, asize, &buffer);
275         luaL_pushresult(&buffer);
276         /* if the output is empty  and the input is nil, return nil */
277         lua_tolstring(L, -1, &osize);
278         if (osize == 0) lua_pushnil(L);
279         lua_pushnil(L);
280         return 2;
281     }
282     /* otherwise process the second part */
283     last = input + isize;
284     while (input < last)
285         asize = b64encode(*input++, atom, asize, &buffer);
286     luaL_pushresult(&buffer);
287     lua_pushlstring(L, (char *) atom, asize);
288     return 2;
289 }
290 
291 /*-------------------------------------------------------------------------*\
292 * Incrementally removes the Base64 transfer content encoding from a string
293 * A, B = b64(C, D)
294 * A is the encoded version of the largest prefix of C .. D that is
295 * divisible by 4. B has the remaining bytes of C .. D, *without* encoding.
296 \*-------------------------------------------------------------------------*/
mime_global_unb64(lua_State * L)297 static int mime_global_unb64(lua_State *L)
298 {
299     UC atom[4];
300     size_t isize = 0, asize = 0;
301     const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
302     const UC *last = input + isize;
303     luaL_Buffer buffer;
304     /* end-of-input blackhole */
305     if (!input) {
306         lua_pushnil(L);
307         lua_pushnil(L);
308         return 2;
309     }
310     /* make sure we don't confuse buffer stuff with arguments */
311     lua_settop(L, 2);
312     /* process first part of the input */
313     luaL_buffinit(L, &buffer);
314     while (input < last)
315         asize = b64decode(*input++, atom, asize, &buffer);
316     input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
317     /* if second is nil, we are done */
318     if (!input) {
319         size_t osize = 0;
320         luaL_pushresult(&buffer);
321         /* if the output is empty  and the input is nil, return nil */
322         lua_tolstring(L, -1, &osize);
323         if (osize == 0) lua_pushnil(L);
324         lua_pushnil(L);
325         return 2;
326     }
327     /* otherwise, process the rest of the input */
328     last = input + isize;
329     while (input < last)
330         asize = b64decode(*input++, atom, asize, &buffer);
331     luaL_pushresult(&buffer);
332     lua_pushlstring(L, (char *) atom, asize);
333     return 2;
334 }
335 
336 /*-------------------------------------------------------------------------*\
337 * Quoted-printable encoding scheme
338 * all (except CRLF in text) can be =XX
339 * CLRL in not text must be =XX=XX
340 * 33 through 60 inclusive can be plain
341 * 62 through 126 inclusive can be plain
342 * 9 and 32 can be plain, unless in the end of a line, where must be =XX
343 * encoded lines must be no longer than 76 not counting CRLF
344 * soft line-break are =CRLF
345 * To encode one byte, we need to see the next two.
346 * Worst case is when we see a space, and wonder if a CRLF is comming
347 \*-------------------------------------------------------------------------*/
348 /*-------------------------------------------------------------------------*\
349 * Split quoted-printable characters into classes
350 * Precompute reverse map for encoding
351 \*-------------------------------------------------------------------------*/
qpsetup(UC * cl,UC * unbase)352 static void qpsetup(UC *cl, UC *unbase)
353 {
354     int i;
355     for (i = 0; i < 256; i++) cl[i] = QP_QUOTED;
356     for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN;
357     for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN;
358     cl['\t'] = QP_IF_LAST;
359     cl[' '] = QP_IF_LAST;
360     cl['\r'] = QP_CR;
361     for (i = 0; i < 256; i++) unbase[i] = 255;
362     unbase['0'] = 0; unbase['1'] = 1; unbase['2'] = 2;
363     unbase['3'] = 3; unbase['4'] = 4; unbase['5'] = 5;
364     unbase['6'] = 6; unbase['7'] = 7; unbase['8'] = 8;
365     unbase['9'] = 9; unbase['A'] = 10; unbase['a'] = 10;
366     unbase['B'] = 11; unbase['b'] = 11; unbase['C'] = 12;
367     unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13;
368     unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15;
369     unbase['f'] = 15;
370 }
371 
372 /*-------------------------------------------------------------------------*\
373 * Output one character in form =XX
374 \*-------------------------------------------------------------------------*/
qpquote(UC c,luaL_Buffer * buffer)375 static void qpquote(UC c, luaL_Buffer *buffer)
376 {
377     luaL_addchar(buffer, '=');
378     luaL_addchar(buffer, qpbase[c >> 4]);
379     luaL_addchar(buffer, qpbase[c & 0x0F]);
380 }
381 
382 /*-------------------------------------------------------------------------*\
383 * Accumulate characters until we are sure about how to deal with them.
384 * Once we are sure, output to the buffer, in the correct form.
385 \*-------------------------------------------------------------------------*/
qpencode(UC c,UC * input,size_t size,const char * marker,luaL_Buffer * buffer)386 static size_t qpencode(UC c, UC *input, size_t size,
387         const char *marker, luaL_Buffer *buffer)
388 {
389     input[size++] = c;
390     /* deal with all characters we can have */
391     while (size > 0) {
392         switch (qpclass[input[0]]) {
393             /* might be the CR of a CRLF sequence */
394             case QP_CR:
395                 if (size < 2) return size;
396                 if (input[1] == '\n') {
397                     luaL_addstring(buffer, marker);
398                     return 0;
399                 } else qpquote(input[0], buffer);
400                 break;
401             /* might be a space and that has to be quoted if last in line */
402             case QP_IF_LAST:
403                 if (size < 3) return size;
404                 /* if it is the last, quote it and we are done */
405                 if (input[1] == '\r' && input[2] == '\n') {
406                     qpquote(input[0], buffer);
407                     luaL_addstring(buffer, marker);
408                     return 0;
409                 } else luaL_addchar(buffer, input[0]);
410                 break;
411                 /* might have to be quoted always */
412             case QP_QUOTED:
413                 qpquote(input[0], buffer);
414                 break;
415                 /* might never have to be quoted */
416             default:
417                 luaL_addchar(buffer, input[0]);
418                 break;
419         }
420         input[0] = input[1]; input[1] = input[2];
421         size--;
422     }
423     return 0;
424 }
425 
426 /*-------------------------------------------------------------------------*\
427 * Deal with the final characters
428 \*-------------------------------------------------------------------------*/
qppad(UC * input,size_t size,luaL_Buffer * buffer)429 static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer)
430 {
431     size_t i;
432     for (i = 0; i < size; i++) {
433         if (qpclass[input[i]] == QP_PLAIN) luaL_addchar(buffer, input[i]);
434         else qpquote(input[i], buffer);
435     }
436     if (size > 0) luaL_addstring(buffer, EQCRLF);
437     return 0;
438 }
439 
440 /*-------------------------------------------------------------------------*\
441 * Incrementally converts a string to quoted-printable
442 * A, B = qp(C, D, marker)
443 * Marker is the text to be used to replace CRLF sequences found in A.
444 * A is the encoded version of the largest prefix of C .. D that
445 * can be encoded without doubts.
446 * B has the remaining bytes of C .. D, *without* encoding.
447 \*-------------------------------------------------------------------------*/
mime_global_qp(lua_State * L)448 static int mime_global_qp(lua_State *L)
449 {
450 
451     size_t asize = 0, isize = 0;
452     UC atom[3];
453     const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
454     const UC *last = input + isize;
455     const char *marker = luaL_optstring(L, 3, CRLF);
456     luaL_Buffer buffer;
457     /* end-of-input blackhole */
458     if (!input) {
459         lua_pushnil(L);
460         lua_pushnil(L);
461         return 2;
462     }
463     /* make sure we don't confuse buffer stuff with arguments */
464     lua_settop(L, 3);
465     /* process first part of input */
466     luaL_buffinit(L, &buffer);
467     while (input < last)
468         asize = qpencode(*input++, atom, asize, marker, &buffer);
469     input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
470     /* if second part is nil, we are done */
471     if (!input) {
472         asize = qppad(atom, asize, &buffer);
473         luaL_pushresult(&buffer);
474         if (!(*lua_tostring(L, -1))) lua_pushnil(L);
475         lua_pushnil(L);
476         return 2;
477     }
478     /* otherwise process rest of input */
479     last = input + isize;
480     while (input < last)
481         asize = qpencode(*input++, atom, asize, marker, &buffer);
482     luaL_pushresult(&buffer);
483     lua_pushlstring(L, (char *) atom, asize);
484     return 2;
485 }
486 
487 /*-------------------------------------------------------------------------*\
488 * Accumulate characters until we are sure about how to deal with them.
489 * Once we are sure, output the to the buffer, in the correct form.
490 \*-------------------------------------------------------------------------*/
qpdecode(UC c,UC * input,size_t size,luaL_Buffer * buffer)491 static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {
492     int d;
493     input[size++] = c;
494     /* deal with all characters we can deal */
495     switch (input[0]) {
496         /* if we have an escape character */
497         case '=':
498             if (size < 3) return size;
499             /* eliminate soft line break */
500             if (input[1] == '\r' && input[2] == '\n') return 0;
501             /* decode quoted representation */
502             c = qpunbase[input[1]]; d = qpunbase[input[2]];
503             /* if it is an invalid, do not decode */
504             if (c > 15 || d > 15) luaL_addlstring(buffer, (char *)input, 3);
505             else luaL_addchar(buffer, (char) ((c << 4) + d));
506             return 0;
507         case '\r':
508             if (size < 2) return size;
509             if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2);
510             return 0;
511         default:
512             if (input[0] == '\t' || (input[0] > 31 && input[0] < 127))
513                 luaL_addchar(buffer, input[0]);
514             return 0;
515     }
516 }
517 
518 /*-------------------------------------------------------------------------*\
519 * Incrementally decodes a string in quoted-printable
520 * A, B = qp(C, D)
521 * A is the decoded version of the largest prefix of C .. D that
522 * can be decoded without doubts.
523 * B has the remaining bytes of C .. D, *without* decoding.
524 \*-------------------------------------------------------------------------*/
mime_global_unqp(lua_State * L)525 static int mime_global_unqp(lua_State *L)
526 {
527     size_t asize = 0, isize = 0;
528     UC atom[3];
529     const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
530     const UC *last = input + isize;
531     luaL_Buffer buffer;
532     /* end-of-input blackhole */
533     if (!input) {
534         lua_pushnil(L);
535         lua_pushnil(L);
536         return 2;
537     }
538     /* make sure we don't confuse buffer stuff with arguments */
539     lua_settop(L, 2);
540     /* process first part of input */
541     luaL_buffinit(L, &buffer);
542     while (input < last)
543         asize = qpdecode(*input++, atom, asize, &buffer);
544     input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
545     /* if second part is nil, we are done */
546     if (!input) {
547         luaL_pushresult(&buffer);
548         if (!(*lua_tostring(L, -1))) lua_pushnil(L);
549         lua_pushnil(L);
550         return 2;
551     }
552     /* otherwise process rest of input */
553     last = input + isize;
554     while (input < last)
555         asize = qpdecode(*input++, atom, asize, &buffer);
556     luaL_pushresult(&buffer);
557     lua_pushlstring(L, (char *) atom, asize);
558     return 2;
559 }
560 
561 /*-------------------------------------------------------------------------*\
562 * Incrementally breaks a quoted-printed string into lines
563 * A, n = qpwrp(l, B, length)
564 * A is a copy of B, broken into lines of at most 'length' bytes.
565 * 'l' is how many bytes are left for the first line of B.
566 * 'n' is the number of bytes left in the last line of A.
567 * There are two complications: lines can't be broken in the middle
568 * of an encoded =XX, and there might be line breaks already
569 \*-------------------------------------------------------------------------*/
mime_global_qpwrp(lua_State * L)570 static int mime_global_qpwrp(lua_State *L)
571 {
572     size_t size = 0;
573     int left = (int) luaL_checknumber(L, 1);
574     const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
575     const UC *last = input + size;
576     int length = (int) luaL_optnumber(L, 3, 76);
577     luaL_Buffer buffer;
578     /* end-of-input blackhole */
579     if (!input) {
580         if (left < length) lua_pushstring(L, EQCRLF);
581         else lua_pushnil(L);
582         lua_pushnumber(L, length);
583         return 2;
584     }
585     /* process all input */
586     luaL_buffinit(L, &buffer);
587     while (input < last) {
588         switch (*input) {
589             case '\r':
590                 break;
591             case '\n':
592                 left = length;
593                 luaL_addstring(&buffer, CRLF);
594                 break;
595             case '=':
596                 if (left <= 3) {
597                     left = length;
598                     luaL_addstring(&buffer, EQCRLF);
599                 }
600                 luaL_addchar(&buffer, *input);
601                 left--;
602                 break;
603             default:
604                 if (left <= 1) {
605                     left = length;
606                     luaL_addstring(&buffer, EQCRLF);
607                 }
608                 luaL_addchar(&buffer, *input);
609                 left--;
610                 break;
611         }
612         input++;
613     }
614     luaL_pushresult(&buffer);
615     lua_pushnumber(L, left);
616     return 2;
617 }
618 
619 /*-------------------------------------------------------------------------*\
620 * Here is what we do: \n, and \r are considered candidates for line
621 * break. We issue *one* new line marker if any of them is seen alone, or
622 * followed by a different one. That is, \n\n and \r\r will issue two
623 * end of line markers each, but \r\n, \n\r etc will only issue *one*
624 * marker.  This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as
625 * probably other more obscure conventions.
626 *
627 * c is the current character being processed
628 * last is the previous character
629 \*-------------------------------------------------------------------------*/
630 #define eolcandidate(c) (c == '\r' || c == '\n')
eolprocess(int c,int last,const char * marker,luaL_Buffer * buffer)631 static int eolprocess(int c, int last, const char *marker,
632         luaL_Buffer *buffer)
633 {
634     if (eolcandidate(c)) {
635         if (eolcandidate(last)) {
636             if (c == last) luaL_addstring(buffer, marker);
637             return 0;
638         } else {
639             luaL_addstring(buffer, marker);
640             return c;
641         }
642     } else {
643         luaL_addchar(buffer, (char) c);
644         return 0;
645     }
646 }
647 
648 /*-------------------------------------------------------------------------*\
649 * Converts a string to uniform EOL convention.
650 * A, n = eol(o, B, marker)
651 * A is the converted version of the largest prefix of B that can be
652 * converted unambiguously. 'o' is the context returned by the previous
653 * call. 'n' is the new context.
654 \*-------------------------------------------------------------------------*/
mime_global_eol(lua_State * L)655 static int mime_global_eol(lua_State *L)
656 {
657     int ctx = (int) luaL_checkinteger(L, 1);
658     size_t isize = 0;
659     const char *input = luaL_optlstring(L, 2, NULL, &isize);
660     const char *last = input + isize;
661     const char *marker = luaL_optstring(L, 3, CRLF);
662     luaL_Buffer buffer;
663     luaL_buffinit(L, &buffer);
664     /* end of input blackhole */
665     if (!input) {
666        lua_pushnil(L);
667        lua_pushnumber(L, 0);
668        return 2;
669     }
670     /* process all input */
671     while (input < last)
672         ctx = eolprocess(*input++, ctx, marker, &buffer);
673     luaL_pushresult(&buffer);
674     lua_pushnumber(L, ctx);
675     return 2;
676 }
677 
678 /*-------------------------------------------------------------------------*\
679 * Takes one byte and stuff it if needed.
680 \*-------------------------------------------------------------------------*/
dot(int c,size_t state,luaL_Buffer * buffer)681 static size_t dot(int c, size_t state, luaL_Buffer *buffer)
682 {
683     luaL_addchar(buffer, (char) c);
684     switch (c) {
685         case '\r':
686             return 1;
687         case '\n':
688             return (state == 1)? 2: 0;
689         case '.':
690             if (state == 2)
691                 luaL_addchar(buffer, '.');
692             /* Falls through. */
693         default:
694             return 0;
695     }
696 }
697 
698 /*-------------------------------------------------------------------------*\
699 * Incrementally applies smtp stuffing to a string
700 * A, n = dot(l, D)
701 \*-------------------------------------------------------------------------*/
mime_global_dot(lua_State * L)702 static int mime_global_dot(lua_State *L)
703 {
704     size_t isize = 0, state = (size_t) luaL_checknumber(L, 1);
705     const char *input = luaL_optlstring(L, 2, NULL, &isize);
706     const char *last = input + isize;
707     luaL_Buffer buffer;
708     /* end-of-input blackhole */
709     if (!input) {
710         lua_pushnil(L);
711         lua_pushnumber(L, 2);
712         return 2;
713     }
714     /* process all input */
715     luaL_buffinit(L, &buffer);
716     while (input < last)
717         state = dot(*input++, state, &buffer);
718     luaL_pushresult(&buffer);
719     lua_pushnumber(L, (lua_Number) state);
720     return 2;
721 }
722 
723