diff options
Diffstat (limited to 'src/mime.c')
-rw-r--r-- | src/mime.c | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/src/mime.c b/src/mime.c new file mode 100644 index 0000000..6807af5 --- /dev/null +++ b/src/mime.c | |||
@@ -0,0 +1,614 @@ | |||
1 | /*=========================================================================*\ | ||
2 | * Encoding support functions | ||
3 | * LuaSocket toolkit | ||
4 | * | ||
5 | * RCS ID: $Id$ | ||
6 | \*=========================================================================*/ | ||
7 | #include <string.h> | ||
8 | |||
9 | #include <lua.h> | ||
10 | #include <lauxlib.h> | ||
11 | |||
12 | #include "luasocket.h" | ||
13 | #include "mime.h" | ||
14 | |||
15 | /*=========================================================================*\ | ||
16 | * Don't want to trust escape character constants | ||
17 | \*=========================================================================*/ | ||
18 | #define CR 0x0D | ||
19 | #define LF 0x0A | ||
20 | #define HT 0x09 | ||
21 | #define SP 0x20 | ||
22 | |||
23 | typedef unsigned char UC; | ||
24 | static const UC CRLF[2] = {CR, LF}; | ||
25 | static const UC EQCRLF[3] = {'=', CR, LF}; | ||
26 | |||
27 | /*=========================================================================*\ | ||
28 | * Internal function prototypes. | ||
29 | \*=========================================================================*/ | ||
30 | static int mime_global_fmt(lua_State *L); | ||
31 | static int mime_global_b64(lua_State *L); | ||
32 | static int mime_global_unb64(lua_State *L); | ||
33 | static int mime_global_qp(lua_State *L); | ||
34 | static int mime_global_unqp(lua_State *L); | ||
35 | static int mime_global_qpfmt(lua_State *L); | ||
36 | static int mime_global_eol(lua_State *L); | ||
37 | |||
38 | static void b64fill(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 qpfill(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 UC *marker, luaL_Buffer *buffer); | ||
48 | |||
49 | /* code support functions */ | ||
50 | static luaL_reg func[] = { | ||
51 | { "eol", mime_global_eol }, | ||
52 | { "qp", mime_global_qp }, | ||
53 | { "unqp", mime_global_unqp }, | ||
54 | { "qpfmt", mime_global_qpfmt }, | ||
55 | { "b64", mime_global_b64 }, | ||
56 | { "unb64", mime_global_unb64 }, | ||
57 | { "fmt", mime_global_fmt }, | ||
58 | { NULL, NULL } | ||
59 | }; | ||
60 | |||
61 | /*-------------------------------------------------------------------------*\ | ||
62 | * Quoted-printable globals | ||
63 | \*-------------------------------------------------------------------------*/ | ||
64 | static UC qpclass[256]; | ||
65 | static UC qpbase[] = "0123456789ABCDEF"; | ||
66 | static UC qpunbase[256]; | ||
67 | enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST}; | ||
68 | |||
69 | /*-------------------------------------------------------------------------*\ | ||
70 | * Base64 globals | ||
71 | \*-------------------------------------------------------------------------*/ | ||
72 | static const UC b64base[] = | ||
73 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
74 | static UC b64unbase[256]; | ||
75 | |||
76 | /*=========================================================================*\ | ||
77 | * Exported functions | ||
78 | \*=========================================================================*/ | ||
79 | /*-------------------------------------------------------------------------*\ | ||
80 | * Initializes module | ||
81 | \*-------------------------------------------------------------------------*/ | ||
82 | void mime_open(lua_State *L) | ||
83 | { | ||
84 | lua_pushstring(L, LUASOCKET_LIBNAME); | ||
85 | lua_gettable(L, LUA_GLOBALSINDEX); | ||
86 | if (lua_isnil(L, -1)) { | ||
87 | lua_pop(L, 1); | ||
88 | lua_newtable(L); | ||
89 | lua_pushstring(L, LUASOCKET_LIBNAME); | ||
90 | lua_pushvalue(L, -2); | ||
91 | lua_settable(L, LUA_GLOBALSINDEX); | ||
92 | } | ||
93 | lua_pushstring(L, "mime"); | ||
94 | lua_newtable(L); | ||
95 | luaL_openlib(L, NULL, func, 0); | ||
96 | lua_settable(L, -3); | ||
97 | lua_pop(L, 1); | ||
98 | /* initialize lookup tables */ | ||
99 | qpfill(qpclass, qpunbase); | ||
100 | b64fill(b64unbase); | ||
101 | } | ||
102 | |||
103 | /*=========================================================================*\ | ||
104 | * Global Lua functions | ||
105 | \*=========================================================================*/ | ||
106 | /*-------------------------------------------------------------------------*\ | ||
107 | * Incrementaly breaks a string into lines | ||
108 | * A, n = fmt(B, length, left) | ||
109 | * A is a copy of B, broken into lines of at most 'length' bytes. | ||
110 | * Left is how many bytes are left in the first line of B. 'n' is the number | ||
111 | * of bytes left in the last line of A. | ||
112 | \*-------------------------------------------------------------------------*/ | ||
113 | static int mime_global_fmt(lua_State *L) | ||
114 | { | ||
115 | size_t size = 0; | ||
116 | const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &size); | ||
117 | const UC *last = input + size; | ||
118 | int length = (int) luaL_checknumber(L, 2); | ||
119 | int left = (int) luaL_optnumber(L, 3, length); | ||
120 | const UC *marker = luaL_optstring(L, 4, CRLF); | ||
121 | luaL_Buffer buffer; | ||
122 | luaL_buffinit(L, &buffer); | ||
123 | while (input < last) { | ||
124 | luaL_putchar(&buffer, *input++); | ||
125 | if (--left <= 0) { | ||
126 | luaL_addstring(&buffer, marker); | ||
127 | left = length; | ||
128 | } | ||
129 | } | ||
130 | if (!input && left < length) { | ||
131 | luaL_addstring(&buffer, marker); | ||
132 | left = length; | ||
133 | } | ||
134 | luaL_pushresult(&buffer); | ||
135 | lua_pushnumber(L, left); | ||
136 | return 2; | ||
137 | } | ||
138 | |||
139 | /*-------------------------------------------------------------------------*\ | ||
140 | * Fill base64 decode map. | ||
141 | \*-------------------------------------------------------------------------*/ | ||
142 | static void b64fill(UC *b64unbase) | ||
143 | { | ||
144 | int i; | ||
145 | for (i = 0; i < 255; i++) b64unbase[i] = 255; | ||
146 | for (i = 0; i < 64; i++) b64unbase[b64base[i]] = i; | ||
147 | b64unbase['='] = 0; | ||
148 | } | ||
149 | |||
150 | /*-------------------------------------------------------------------------*\ | ||
151 | * Acumulates bytes in input buffer until 3 bytes are available. | ||
152 | * Translate the 3 bytes into Base64 form and append to buffer. | ||
153 | * Returns new number of bytes in buffer. | ||
154 | \*-------------------------------------------------------------------------*/ | ||
155 | static size_t b64encode(UC c, UC *input, size_t size, | ||
156 | luaL_Buffer *buffer) | ||
157 | { | ||
158 | input[size++] = c; | ||
159 | if (size == 3) { | ||
160 | UC code[4]; | ||
161 | unsigned long value = 0; | ||
162 | value += input[0]; value <<= 8; | ||
163 | value += input[1]; value <<= 8; | ||
164 | value += input[2]; | ||
165 | code[3] = b64base[value & 0x3f]; value >>= 6; | ||
166 | code[2] = b64base[value & 0x3f]; value >>= 6; | ||
167 | code[1] = b64base[value & 0x3f]; value >>= 6; | ||
168 | code[0] = b64base[value]; | ||
169 | luaL_addlstring(buffer, code, 4); | ||
170 | size = 0; | ||
171 | } | ||
172 | return size; | ||
173 | } | ||
174 | |||
175 | /*-------------------------------------------------------------------------*\ | ||
176 | * Encodes the Base64 last 1 or 2 bytes and adds padding '=' | ||
177 | * Result, if any, is appended to buffer. | ||
178 | * Returns 0. | ||
179 | \*-------------------------------------------------------------------------*/ | ||
180 | static size_t b64pad(const UC *input, size_t size, | ||
181 | luaL_Buffer *buffer) | ||
182 | { | ||
183 | unsigned long value = 0; | ||
184 | UC code[4] = "===="; | ||
185 | switch (size) { | ||
186 | case 1: | ||
187 | value = input[0] << 4; | ||
188 | code[1] = b64base[value & 0x3f]; value >>= 6; | ||
189 | code[0] = b64base[value]; | ||
190 | luaL_addlstring(buffer, code, 4); | ||
191 | break; | ||
192 | case 2: | ||
193 | value = input[0]; value <<= 8; | ||
194 | value |= input[1]; value <<= 2; | ||
195 | code[2] = b64base[value & 0x3f]; value >>= 6; | ||
196 | code[1] = b64base[value & 0x3f]; value >>= 6; | ||
197 | code[0] = b64base[value]; | ||
198 | luaL_addlstring(buffer, code, 4); | ||
199 | break; | ||
200 | case 0: /* fall through */ | ||
201 | default: | ||
202 | break; | ||
203 | } | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | /*-------------------------------------------------------------------------*\ | ||
208 | * Acumulates bytes in input buffer until 4 bytes are available. | ||
209 | * Translate the 4 bytes from Base64 form and append to buffer. | ||
210 | * Returns new number of bytes in buffer. | ||
211 | \*-------------------------------------------------------------------------*/ | ||
212 | static size_t b64decode(UC c, UC *input, size_t size, | ||
213 | luaL_Buffer *buffer) | ||
214 | { | ||
215 | |||
216 | /* ignore invalid characters */ | ||
217 | if (b64unbase[c] > 64) return size; | ||
218 | input[size++] = c; | ||
219 | /* decode atom */ | ||
220 | if (size == 4) { | ||
221 | UC decoded[3]; | ||
222 | int valid, value = 0; | ||
223 | value = b64unbase[input[0]]; value <<= 6; | ||
224 | value |= b64unbase[input[1]]; value <<= 6; | ||
225 | value |= b64unbase[input[2]]; value <<= 6; | ||
226 | value |= b64unbase[input[3]]; | ||
227 | decoded[2] = (UC) (value & 0xff); value >>= 8; | ||
228 | decoded[1] = (UC) (value & 0xff); value >>= 8; | ||
229 | decoded[0] = (UC) value; | ||
230 | /* take care of paddding */ | ||
231 | valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; | ||
232 | luaL_addlstring(buffer, decoded, valid); | ||
233 | return 0; | ||
234 | /* need more data */ | ||
235 | } else return size; | ||
236 | } | ||
237 | |||
238 | /*-------------------------------------------------------------------------*\ | ||
239 | * Incrementally applies the Base64 transfer content encoding to a string | ||
240 | * A, B = b64(C, D) | ||
241 | * A is the encoded version of the largest prefix of C .. D that is | ||
242 | * divisible by 3. B has the remaining bytes of C .. D, *without* encoding. | ||
243 | * The easiest thing would be to concatenate the two strings and | ||
244 | * encode the result, but we can't afford that or Lua would dupplicate | ||
245 | * every chunk we received. | ||
246 | \*-------------------------------------------------------------------------*/ | ||
247 | static int mime_global_b64(lua_State *L) | ||
248 | { | ||
249 | UC atom[3]; | ||
250 | size_t isize = 0, asize = 0; | ||
251 | const UC *input = luaL_checklstring(L, 1, &isize); | ||
252 | const UC *last = input + isize; | ||
253 | luaL_Buffer buffer; | ||
254 | luaL_buffinit(L, &buffer); | ||
255 | while (input < last) | ||
256 | asize = b64encode(*input++, atom, asize, &buffer); | ||
257 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
258 | if (input) { | ||
259 | last = input + isize; | ||
260 | while (input < last) | ||
261 | asize = b64encode(*input++, atom, asize, &buffer); | ||
262 | } else | ||
263 | asize = b64pad(atom, asize, &buffer); | ||
264 | luaL_pushresult(&buffer); | ||
265 | lua_pushlstring(L, atom, asize); | ||
266 | return 2; | ||
267 | } | ||
268 | |||
269 | /*-------------------------------------------------------------------------*\ | ||
270 | * Incrementally removes the Base64 transfer content encoding from a string | ||
271 | * A, B = b64(C, D) | ||
272 | * A is the encoded version of the largest prefix of C .. D that is | ||
273 | * divisible by 4. B has the remaining bytes of C .. D, *without* encoding. | ||
274 | \*-------------------------------------------------------------------------*/ | ||
275 | static int mime_global_unb64(lua_State *L) | ||
276 | { | ||
277 | UC atom[4]; | ||
278 | size_t isize = 0, asize = 0; | ||
279 | const UC *input = luaL_checklstring(L, 1, &isize); | ||
280 | const UC *last = input + isize; | ||
281 | luaL_Buffer buffer; | ||
282 | luaL_buffinit(L, &buffer); | ||
283 | while (input < last) | ||
284 | asize = b64decode(*input++, atom, asize, &buffer); | ||
285 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
286 | if (input) { | ||
287 | last = input + isize; | ||
288 | while (input < last) | ||
289 | asize = b64decode(*input++, atom, asize, &buffer); | ||
290 | } | ||
291 | luaL_pushresult(&buffer); | ||
292 | lua_pushlstring(L, atom, asize); | ||
293 | return 2; | ||
294 | } | ||
295 | |||
296 | /*-------------------------------------------------------------------------*\ | ||
297 | * Quoted-printable encoding scheme | ||
298 | * all (except CRLF in text) can be =XX | ||
299 | * CLRL in not text must be =XX=XX | ||
300 | * 33 through 60 inclusive can be plain | ||
301 | * 62 through 120 inclusive can be plain | ||
302 | * 9 and 32 can be plain, unless in the end of a line, where must be =XX | ||
303 | * encoded lines must be no longer than 76 not counting CRLF | ||
304 | * soft line-break are =CRLF | ||
305 | * !"#$@[\]^`{|}~ should be =XX for EBCDIC compatibility | ||
306 | * To encode one byte, we need to see the next two. | ||
307 | * Worst case is when we see a space, and wonder if a CRLF is comming | ||
308 | \*-------------------------------------------------------------------------*/ | ||
309 | /*-------------------------------------------------------------------------*\ | ||
310 | * Split quoted-printable characters into classes | ||
311 | * Precompute reverse map for encoding | ||
312 | \*-------------------------------------------------------------------------*/ | ||
313 | static void qpfill(UC *qpclass, UC *qpunbase) | ||
314 | { | ||
315 | int i; | ||
316 | for (i = 0; i < 256; i++) qpclass[i] = QP_QUOTED; | ||
317 | for (i = 33; i <= 60; i++) qpclass[i] = QP_PLAIN; | ||
318 | for (i = 62; i <= 120; i++) qpclass[i] = QP_PLAIN; | ||
319 | qpclass[HT] = QP_IF_LAST; qpclass[SP] = QP_IF_LAST; | ||
320 | qpclass['!'] = QP_QUOTED; qpclass['"'] = QP_QUOTED; | ||
321 | qpclass['#'] = QP_QUOTED; qpclass['$'] = QP_QUOTED; | ||
322 | qpclass['@'] = QP_QUOTED; qpclass['['] = QP_QUOTED; | ||
323 | qpclass['\\'] = QP_QUOTED; qpclass[']'] = QP_QUOTED; | ||
324 | qpclass['^'] = QP_QUOTED; qpclass['`'] = QP_QUOTED; | ||
325 | qpclass['{'] = QP_QUOTED; qpclass['|'] = QP_QUOTED; | ||
326 | qpclass['}'] = QP_QUOTED; qpclass['~'] = QP_QUOTED; | ||
327 | qpclass['}'] = QP_QUOTED; qpclass[CR] = QP_CR; | ||
328 | for (i = 0; i < 256; i++) qpunbase[i] = 255; | ||
329 | qpunbase['0'] = 0; qpunbase['1'] = 1; qpunbase['2'] = 2; | ||
330 | qpunbase['3'] = 3; qpunbase['4'] = 4; qpunbase['5'] = 5; | ||
331 | qpunbase['6'] = 6; qpunbase['7'] = 7; qpunbase['8'] = 8; | ||
332 | qpunbase['9'] = 9; qpunbase['A'] = 10; qpunbase['a'] = 10; | ||
333 | qpunbase['B'] = 11; qpunbase['b'] = 11; qpunbase['C'] = 12; | ||
334 | qpunbase['c'] = 12; qpunbase['D'] = 13; qpunbase['d'] = 13; | ||
335 | qpunbase['E'] = 14; qpunbase['e'] = 14; qpunbase['F'] = 15; | ||
336 | qpunbase['f'] = 15; | ||
337 | } | ||
338 | |||
339 | /*-------------------------------------------------------------------------*\ | ||
340 | * Output one character in form =XX | ||
341 | \*-------------------------------------------------------------------------*/ | ||
342 | static void qpquote(UC c, luaL_Buffer *buffer) | ||
343 | { | ||
344 | luaL_putchar(buffer, '='); | ||
345 | luaL_putchar(buffer, qpbase[c >> 4]); | ||
346 | luaL_putchar(buffer, qpbase[c & 0x0F]); | ||
347 | } | ||
348 | |||
349 | /*-------------------------------------------------------------------------*\ | ||
350 | * Accumulate characters until we are sure about how to deal with them. | ||
351 | * Once we are sure, output the to the buffer, in the correct form. | ||
352 | \*-------------------------------------------------------------------------*/ | ||
353 | static size_t qpencode(UC c, UC *input, size_t size, | ||
354 | const UC *marker, luaL_Buffer *buffer) | ||
355 | { | ||
356 | input[size++] = c; | ||
357 | /* deal with all characters we can have */ | ||
358 | while (size > 0) { | ||
359 | switch (qpclass[input[0]]) { | ||
360 | /* might be the CR of a CRLF sequence */ | ||
361 | case QP_CR: | ||
362 | if (size < 2) return size; | ||
363 | if (input[1] == LF) { | ||
364 | luaL_addstring(buffer, marker); | ||
365 | return 0; | ||
366 | } else qpquote(input[0], buffer); | ||
367 | break; | ||
368 | /* might be a space and that has to be quoted if last in line */ | ||
369 | case QP_IF_LAST: | ||
370 | if (size < 3) return size; | ||
371 | /* if it is the last, quote it and we are done */ | ||
372 | if (input[1] == CR && input[2] == LF) { | ||
373 | qpquote(input[0], buffer); | ||
374 | luaL_addstring(buffer, marker); | ||
375 | return 0; | ||
376 | } else luaL_putchar(buffer, input[0]); | ||
377 | break; | ||
378 | /* might have to be quoted always */ | ||
379 | case QP_QUOTED: | ||
380 | qpquote(input[0], buffer); | ||
381 | break; | ||
382 | /* might never have to be quoted */ | ||
383 | default: | ||
384 | luaL_putchar(buffer, input[0]); | ||
385 | break; | ||
386 | } | ||
387 | input[0] = input[1]; input[1] = input[2]; | ||
388 | size--; | ||
389 | } | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | /*-------------------------------------------------------------------------*\ | ||
394 | * Deal with the final characters | ||
395 | \*-------------------------------------------------------------------------*/ | ||
396 | static void qppad(UC *input, size_t size, luaL_Buffer *buffer) | ||
397 | { | ||
398 | size_t i; | ||
399 | for (i = 0; i < size; i++) { | ||
400 | if (qpclass[input[i]] == QP_PLAIN) luaL_putchar(buffer, input[i]); | ||
401 | else qpquote(input[i], buffer); | ||
402 | } | ||
403 | luaL_addstring(buffer, EQCRLF); | ||
404 | } | ||
405 | |||
406 | /*-------------------------------------------------------------------------*\ | ||
407 | * Incrementally converts a string to quoted-printable | ||
408 | * A, B = qp(C, D, marker) | ||
409 | * Crlf is the text to be used to replace CRLF sequences found in A. | ||
410 | * A is the encoded version of the largest prefix of C .. D that | ||
411 | * can be encoded without doubts. | ||
412 | * B has the remaining bytes of C .. D, *without* encoding. | ||
413 | \*-------------------------------------------------------------------------*/ | ||
414 | static int mime_global_qp(lua_State *L) | ||
415 | { | ||
416 | |||
417 | size_t asize = 0, isize = 0; | ||
418 | UC atom[3]; | ||
419 | const UC *input = lua_isnil(L, 1) ? NULL: luaL_checklstring(L, 1, &isize); | ||
420 | const UC *last = input + isize; | ||
421 | const UC *marker = luaL_optstring(L, 3, CRLF); | ||
422 | luaL_Buffer buffer; | ||
423 | luaL_buffinit(L, &buffer); | ||
424 | while (input < last) | ||
425 | asize = qpencode(*input++, atom, asize, marker, &buffer); | ||
426 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
427 | if (input) { | ||
428 | last = input + isize; | ||
429 | while (input < last) | ||
430 | asize = qpencode(*input++, atom, asize, marker, &buffer); | ||
431 | } else qppad(atom, asize, &buffer); | ||
432 | luaL_pushresult(&buffer); | ||
433 | lua_pushlstring(L, atom, asize); | ||
434 | return 2; | ||
435 | } | ||
436 | |||
437 | /*-------------------------------------------------------------------------*\ | ||
438 | * Accumulate characters until we are sure about how to deal with them. | ||
439 | * Once we are sure, output the to the buffer, in the correct form. | ||
440 | \*-------------------------------------------------------------------------*/ | ||
441 | static size_t qpdecode(UC c, UC *input, size_t size, | ||
442 | luaL_Buffer *buffer) | ||
443 | { | ||
444 | input[size++] = c; | ||
445 | /* deal with all characters we can deal */ | ||
446 | while (size > 0) { | ||
447 | int c, d; | ||
448 | switch (input[0]) { | ||
449 | /* if we have an escape character */ | ||
450 | case '=': | ||
451 | if (size < 3) return size; | ||
452 | /* eliminate soft line break */ | ||
453 | if (input[1] == CR && input[2] == LF) return 0; | ||
454 | /* decode quoted representation */ | ||
455 | c = qpunbase[input[1]]; d = qpunbase[input[2]]; | ||
456 | /* if it is an invalid, do not decode */ | ||
457 | if (c > 15 || d > 15) luaL_addlstring(buffer, input, 3); | ||
458 | else luaL_putchar(buffer, (c << 4) + d); | ||
459 | return 0; | ||
460 | case CR: | ||
461 | if (size < 2) return size; | ||
462 | if (input[1] == LF) luaL_addlstring(buffer, input, 2); | ||
463 | return 0; | ||
464 | default: | ||
465 | if (input[0] == HT || (input[0] > 31 && input[0] < 127)) | ||
466 | luaL_putchar(buffer, input[0]); | ||
467 | return 0; | ||
468 | } | ||
469 | input[0] = input[1]; input[1] = input[2]; | ||
470 | size--; | ||
471 | } | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | /*-------------------------------------------------------------------------*\ | ||
476 | * Incrementally decodes a string in quoted-printable | ||
477 | * A, B = qp(C, D) | ||
478 | * A is the decoded version of the largest prefix of C .. D that | ||
479 | * can be decoded without doubts. | ||
480 | * B has the remaining bytes of C .. D, *without* decoding. | ||
481 | \*-------------------------------------------------------------------------*/ | ||
482 | static int mime_global_unqp(lua_State *L) | ||
483 | { | ||
484 | |||
485 | size_t asize = 0, isize = 0; | ||
486 | UC atom[3]; | ||
487 | const UC *input = lua_isnil(L, 1) ? NULL: luaL_checklstring(L, 1, &isize); | ||
488 | const UC *last = input + isize; | ||
489 | luaL_Buffer buffer; | ||
490 | luaL_buffinit(L, &buffer); | ||
491 | while (input < last) | ||
492 | asize = qpdecode(*input++, atom, asize, &buffer); | ||
493 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
494 | if (input) { | ||
495 | last = input + isize; | ||
496 | while (input < last) | ||
497 | asize = qpdecode(*input++, atom, asize, &buffer); | ||
498 | } | ||
499 | luaL_pushresult(&buffer); | ||
500 | lua_pushlstring(L, atom, asize); | ||
501 | return 2; | ||
502 | } | ||
503 | |||
504 | /*-------------------------------------------------------------------------*\ | ||
505 | * Incrementally breaks a quoted-printed string into lines | ||
506 | * A, n = qpfmt(B, length, left) | ||
507 | * A is a copy of B, broken into lines of at most 'length' bytes. | ||
508 | * Left is how many bytes are left in the first line of B. 'n' is the number | ||
509 | * of bytes left in the last line of A. | ||
510 | * There are two complications: lines can't be broken in the middle | ||
511 | * of an encoded =XX, and there might be line breaks already | ||
512 | \*-------------------------------------------------------------------------*/ | ||
513 | static int mime_global_qpfmt(lua_State *L) | ||
514 | { | ||
515 | size_t size = 0; | ||
516 | const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &size); | ||
517 | const UC *last = input + size; | ||
518 | int length = (int) luaL_checknumber(L, 2); | ||
519 | int left = (int) luaL_optnumber(L, 3, length); | ||
520 | luaL_Buffer buffer; | ||
521 | luaL_buffinit(L, &buffer); | ||
522 | while (input < last) { | ||
523 | left--; | ||
524 | switch (*input) { | ||
525 | case '=': | ||
526 | /* if there's no room in this line for the quoted char, | ||
527 | * output a soft line break now */ | ||
528 | if (left <= 3) { | ||
529 | luaL_addstring(&buffer, EQCRLF); | ||
530 | left = length; | ||
531 | } | ||
532 | break; | ||
533 | /* \r\n starts a new line */ | ||
534 | case CR: | ||
535 | break; | ||
536 | case LF: | ||
537 | left = length; | ||
538 | break; | ||
539 | default: | ||
540 | /* if in last column, output a soft line break */ | ||
541 | if (left <= 1) { | ||
542 | luaL_addstring(&buffer, EQCRLF); | ||
543 | left = length; | ||
544 | } | ||
545 | } | ||
546 | luaL_putchar(&buffer, *input); | ||
547 | input++; | ||
548 | } | ||
549 | if (!input && left < length) { | ||
550 | luaL_addstring(&buffer, EQCRLF); | ||
551 | left = length; | ||
552 | } | ||
553 | luaL_pushresult(&buffer); | ||
554 | lua_pushnumber(L, left); | ||
555 | return 2; | ||
556 | } | ||
557 | |||
558 | /*-------------------------------------------------------------------------*\ | ||
559 | * Here is what we do: \n, \r and \f are considered candidates for line | ||
560 | * break. We issue *one* new line marker if any of them is seen alone, or | ||
561 | * followed by a different one. That is, \n\n, \r\r and \f\f will issue two | ||
562 | * end of line markers each, but \r\n, \n\r, \r\f etc will only issue *one* | ||
563 | * marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as | ||
564 | * probably other more obscure conventions. | ||
565 | \*-------------------------------------------------------------------------*/ | ||
566 | #define eolcandidate(c) (c == CR || c == LF) | ||
567 | static size_t eolconvert(UC c, UC *input, size_t size, | ||
568 | const UC *marker, luaL_Buffer *buffer) | ||
569 | { | ||
570 | input[size++] = c; | ||
571 | /* deal with all characters we can deal */ | ||
572 | if (eolcandidate(input[0])) { | ||
573 | if (size < 2) return size; | ||
574 | luaL_addstring(buffer, marker); | ||
575 | if (eolcandidate(input[1])) { | ||
576 | if (input[0] == input[1]) luaL_addstring(buffer, marker); | ||
577 | } else luaL_putchar(buffer, input[1]); | ||
578 | return 0; | ||
579 | } else { | ||
580 | luaL_putchar(buffer, input[0]); | ||
581 | return 0; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | /*-------------------------------------------------------------------------*\ | ||
586 | * Converts a string to uniform EOL convention. | ||
587 | * A, B = eol(C, D, marker) | ||
588 | * A is the converted version of the largest prefix of C .. D that | ||
589 | * can be converted without doubts. | ||
590 | * B has the remaining bytes of C .. D, *without* convertion. | ||
591 | \*-------------------------------------------------------------------------*/ | ||
592 | static int mime_global_eol(lua_State *L) | ||
593 | { | ||
594 | size_t asize = 0, isize = 0; | ||
595 | UC atom[2]; | ||
596 | const UC *input = lua_isnil(L, 1)? NULL: luaL_checklstring(L, 1, &isize); | ||
597 | const UC *last = input + isize; | ||
598 | const UC *marker = luaL_optstring(L, 3, CRLF); | ||
599 | luaL_Buffer buffer; | ||
600 | luaL_buffinit(L, &buffer); | ||
601 | while (input < last) | ||
602 | asize = eolconvert(*input++, atom, asize, marker, &buffer); | ||
603 | input = luaL_optlstring(L, 2, NULL, &isize); | ||
604 | if (input) { | ||
605 | last = input + isize; | ||
606 | while (input < last) | ||
607 | asize = eolconvert(*input++, atom, asize, marker, &buffer); | ||
608 | /* if there is something in atom, it's one character, and it | ||
609 | * is a candidate. so we output a new line */ | ||
610 | } else if (asize > 0) luaL_addstring(&buffer, marker); | ||
611 | luaL_pushresult(&buffer); | ||
612 | lua_pushlstring(L, atom, asize); | ||
613 | return 2; | ||
614 | } | ||