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\*=========================================================================*/
16typedef unsigned char UC;
17static const char CRLF[] = "\r\n";
18static const char EQCRLF[] = "=\r\n";
19
20/*=========================================================================*\
21* Internal function prototypes.
22\*=========================================================================*/
23static int mime_global_wrp(lua_State *L);
24static int mime_global_b64(lua_State *L);
25static int mime_global_unb64(lua_State *L);
26static int mime_global_qp(lua_State *L);
27static int mime_global_unqp(lua_State *L);
28static int mime_global_qpwrp(lua_State *L);
29static int mime_global_eol(lua_State *L);
30static int mime_global_dot(lua_State *L);
31
32static size_t dot(int c, size_t state, luaL_Buffer *buffer);
33static void b64setup(UC *base);
34static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
35static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer);
36static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
37
38static void qpsetup(UC *class, UC *unbase);
39static void qpquote(UC c, luaL_Buffer *buffer);
40static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
41static size_t qpencode(UC c, UC *input, size_t size,
42 const char *marker, luaL_Buffer *buffer);
43static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer);
44
45/* code support functions */
46static 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\*-------------------------------------------------------------------------*/
61static UC qpclass[256];
62static UC qpbase[] = "0123456789ABCDEF";
63static UC qpunbase[256];
64enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST};
65
66/*-------------------------------------------------------------------------*\
67* Base64 globals
68\*-------------------------------------------------------------------------*/
69static const UC b64base[] =
70 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
71static UC b64unbase[256];
72
73/*=========================================================================*\
74* Exported functions
75\*=========================================================================*/
76/*-------------------------------------------------------------------------*\
77* Initializes module
78\*-------------------------------------------------------------------------*/
79MIME_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\*-------------------------------------------------------------------------*/
103static 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\*-------------------------------------------------------------------------*/
148static 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\*-------------------------------------------------------------------------*/
161static 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\*-------------------------------------------------------------------------*/
186static 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\*-------------------------------------------------------------------------*/
217static 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\*-------------------------------------------------------------------------*/
251static 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\*-------------------------------------------------------------------------*/
297static 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\*-------------------------------------------------------------------------*/
352static 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\*-------------------------------------------------------------------------*/
375static 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\*-------------------------------------------------------------------------*/
386static 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\*-------------------------------------------------------------------------*/
429static 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\*-------------------------------------------------------------------------*/
448static 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\*-------------------------------------------------------------------------*/
491static 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\*-------------------------------------------------------------------------*/
525static 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\*-------------------------------------------------------------------------*/
570static 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')
631static 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\*-------------------------------------------------------------------------*/
655static int mime_global_eol(lua_State *L)
656{
657 int ctx = 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\*-------------------------------------------------------------------------*/
681static 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 default:
693 return 0;
694 }
695}
696
697/*-------------------------------------------------------------------------*\
698* Incrementally applies smtp stuffing to a string
699* A, n = dot(l, D)
700\*-------------------------------------------------------------------------*/
701static int mime_global_dot(lua_State *L)
702{
703 size_t isize = 0, state = (size_t) luaL_checknumber(L, 1);
704 const char *input = luaL_optlstring(L, 2, NULL, &isize);
705 const char *last = input + isize;
706 luaL_Buffer buffer;
707 /* end-of-input blackhole */
708 if (!input) {
709 lua_pushnil(L);
710 lua_pushnumber(L, 2);
711 return 2;
712 }
713 /* process all input */
714 luaL_buffinit(L, &buffer);
715 while (input < last)
716 state = dot(*input++, state, &buffer);
717 luaL_pushresult(&buffer);
718 lua_pushnumber(L, (lua_Number) state);
719 return 2;
720}
721
722