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