comparison src/mime.c @ 0:4b915342e2a8

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