Mercurial > luasocket
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 |