tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

rfc2047.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : rfc2047.c
4 * Author : Chris Blum <chris@resolution.de>
5 * Created : 1995-09-01
6 * Updated : 2021-03-04
7 * Notes : MIME header encoding/decoding stuff
8 *
9 * Copyright (c) 1995-2022 Chris Blum <chris@resolution.de>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41#ifndef TIN_H
42# include "tin.h"
43#endif /* !TIN_H */
44
45
46#define isreturn(c) ((c) == '\r' || ((c) == '\n'))
47
48/*
49 * Modified to return TRUE for '(' and ')' only if
50 * it's in structured header field. '(' and ')' are
51 * NOT to be treated differently than other characters
52 * in unstructured headers like Subject, Keyword and Summary
53 * c.f. RFC 2047
54 */
55/*
56 * On some systems isspace(0xa0) returns TRUE (UTF-8 locale).
57 * 0xa0 can be the second byte of a UTF-8 character and must not be
58 * treated as whitespace, otherwise Q and B encoding fails.
59 */
60#if 0
61# define isbetween(c, s) (isspace((unsigned char) c) || ((s) && ((c) == '(' || (c) == ')' || (c) == '"')))
62#else
63# define my_isspace(c) ((c) == '\t' || (c) == '\n' || (c) == '\v' || (c) == '\f' || (c) == '\r' || (c) == ' ')
64# define isbetween(c, s) (my_isspace(c) || ((s) && ((c) == '(' || (c) == ')' || (c) == '"')))
65#endif /* 0 */
66#define NOT_RANKED 255
67
68#if 0
69 /* inside a quoted word these 7bit chars need to be encoded too */
70# define RFC2047_ESPECIALS "[]<>.;@,=?_\"\\"
71#endif /* 0 */
72
73const char base64_alphabet[64] =
74{
75 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
76 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
77 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
78 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
79
80static unsigned char base64_rank[256];
82
83/* fixed prefix and default part for tin-generated MIME boundaries */
84static const char MIME_BOUNDARY_PREFIX[] = "=_tin=_";
85static const char MIME_BOUNDARY_DEFAULT_PART[] = "====____====____====____";
86/* required size of a buffer containing a MIME boundary, including the final '\0' */
87enum {
89};
90
91/*
92 * local prototypes
93 */
94static FILE *compose_message_rfc822(FILE *articlefp, t_bool *is_8bit);
95static FILE *compose_multipart_mixed(FILE *textfp, FILE *articlefp);
96static int do_b_encode(char *w, char *b, size_t max_ewsize, t_bool isstruct_head);
97static int sizeofnextword(char *w);
98static int which_encoding(char *w);
99static t_bool contains_8bit_characters(FILE *fp);
100static t_bool contains_nonprintables(char *w, t_bool isstruct_head);
101static t_bool contains_string(FILE *fp, const char *str);
102static t_bool rfc1522_do_encode(char *what, char **where, const char *charset, t_bool break_long_line);
103static t_bool split_mail(const char *filename, FILE **headerfp, FILE **textfp);
104static unsigned hex2bin(int x);
105static void build_base64_rank_table(void);
106static void do_rfc15211522_encode(FILE *f, constext * mime_encoding, struct t_group *group, t_bool allow_8bit_header, t_bool ismail, t_bool contains_headers);
107static void generate_mime_boundary(char *boundary, FILE *f, FILE *g);
108static void generate_random_mime_boundary(char *boundary, size_t len);
109static void str2b64(const char *from, char *to);
110
111
112static void
114 void)
115{
116 int i;
117
119 for (i = 0; i < 256; i++)
121 for (i = 0; i < 64; i++)
122 base64_rank[(int) base64_alphabet[i]] = (unsigned char) i;
124 }
125}
126
127
128static unsigned
130 int x)
131{
132 if (x >= '0' && x <= '9')
133 return (unsigned) (x - '0');
134 if (x >= 'A' && x <= 'F')
135 return (unsigned) ((x - 'A') + 10);
136 if (x >= 'a' && x <= 'f')
137 return (unsigned) ((x - 'a') + 10);
138 return 255;
139}
140
141
142/*
143 * Do B or Q decoding of a chunk of data in 'what' to 'where'
144 * Return number of bytes decoded into 'where' or -1.
145 */
146int
148 const char *what,
149 int encoding,
150 int delimiter,
151 char *where)
152{
153 char *t;
154
155 t = where;
156 encoding = my_tolower((unsigned char) encoding);
157 if (encoding == 'q') { /* quoted-printable */
158 int x;
159 unsigned hi, lo;
160
161 if (!what || !where) /* should not happen with 'q'-encoding */
162 return -1;
163
164 while (*what != delimiter) {
165 if (*what != '=') {
166 if (!delimiter || *what != '_')
167 *t++ = *what++;
168 else {
169 *t++ = ' ';
170 what++;
171 }
172 continue;
173 }
174 what++;
175 if (*what == delimiter) /* failed */
176 return -1;
177
178 x = *what++;
179 if (x == '\n')
180 continue;
181 if (*what == delimiter)
182 return -1;
183
184 hi = hex2bin(x);
185 lo = hex2bin(*what);
186 what++;
187 if (hi == 255 || lo == 255)
188 return -1;
189 x = (int) ((hi << 4) + lo);
190 *EIGHT_BIT(t)++ = (unsigned char) x;
191 }
192 return (int) (t - where);
193 } else if (encoding == 'b') { /* base64 */
194 static unsigned short pattern = 0;
195 static int bits = 0;
196 unsigned char x;
197
198 if (!what || !where) { /* flush */
199 pattern = 0;
200 bits = 0;
201 return 0;
202 }
203
205
206 while (*what != delimiter) {
207 x = base64_rank[(unsigned char) (*what++)];
208 /* ignore everything not in the alphabet, including '=' */
209 if (x == NOT_RANKED)
210 continue;
211 pattern <<= 6;
212 pattern |= x;
213 bits += 6;
214 if (bits >= 8) {
215 x = (unsigned char) ((pattern >> (bits - 8)) & 0xff);
216 *t++ = (char) x;
217 bits -= 8;
218 }
219 }
220 return (int) (t - where);
221 }
222 return -1;
223}
224
225
226/*
227 * This routine decodes encoded headers in the
228 * =?charset?encoding?coded text?=
229 * format
230 */
231char *
233 const char *s)
234{
235 char *c, *sc;
236 const char *d;
237 char *t;
238 static char *buffer = NULL;
239 static int buffer_len = 0;
240 size_t max_len;
241 char charset[1024];
242 char encoding;
243 t_bool adjacentflag = FALSE;
244
245 if (!s) {
246 FreeAndNull(buffer);
247 return NULL;
248 }
249
250 charset[0] = '\0';
251 c = my_strdup(s);
252 max_len = strlen(c) + 1;
253
254 if (!buffer) {
255 buffer_len = (int) max_len;
256 buffer = my_malloc((size_t) buffer_len);
257 } else if (max_len > (size_t) buffer_len) {
258 buffer_len = (int) max_len;
259 buffer = my_realloc(buffer, (size_t) buffer_len);
260 }
261
262 t = buffer;
263
264 /*
265 * remove non-ASCII chars if MIME_STRICT_CHARSET is set
266 * must be changed if UTF-8 becomes default charset for headers:
267 *
268 * process_charsets(c, len, "UTF-8", tinrc.mm_local_charset, FALSE);
269 */
270#ifndef CHARSET_CONVERSION
271 process_charsets(&c, &max_len, "US-ASCII", tinrc.mm_local_charset, FALSE);
272#else
273 process_charsets(&c, &max_len, (CURR_GROUP.attribute->undeclared_charset) ? (CURR_GROUP.attribute->undeclared_charset) : "US-ASCII", tinrc.mm_local_charset, FALSE);
274#endif /* !CHARSET_CONVERSION */
275 sc = c;
276
277 while (*c && t - buffer < buffer_len - 1) {
278 if (*c != '=') {
279 if (adjacentflag && isspace((unsigned char) *c)) {
280 const char *dd;
281
282 dd = c + 1;
283 while (isspace((unsigned char) *dd))
284 dd++;
285 if (*dd == '=') { /* brute hack, makes mistakes under certain circumstances comp. 6.2 */
286 c++;
287 continue;
288 }
289 }
290 adjacentflag = FALSE;
291 *t++ = *c++;
292 continue;
293 }
294 d = c++;
295 if (*c == '?') {
296 char *e;
297
298 e = charset;
299 c++;
300 while (*c && *c != '?') {
301 /* skip over optional language tags (RFC2231, RFC5646) */
302 if (*c == '*') {
303 while (*++c && *c != '?')
304 ;
305 continue;
306 }
307 *e++ = *c++;
308 }
309 *e = '\0';
310 if (*c == '?') {
311 c++;
312 encoding = (char) my_tolower((unsigned char) *c);
313 if (encoding == 'b')
314 (void) mmdecode(NULL, 'b', 0, NULL); /* flush */
315 if (*c)
316 c++;
317 if (*c == '?') {
318 c++;
319 if ((e = strchr(c, '?'))) {
320 int i;
321
322 i = mmdecode(c, encoding, '?', t);
323 if (i > 0) {
324 char *tmpbuf;
325 int chars_to_copy;
326
327 max_len = (size_t) (i + 1);
328 tmpbuf = my_malloc(max_len);
329 strncpy(tmpbuf, t, (size_t) i);
330 *(tmpbuf + i) = '\0';
331 process_charsets(&tmpbuf, &max_len, charset, tinrc.mm_local_charset, FALSE);
332 chars_to_copy = (int) strlen(tmpbuf);
333 if (chars_to_copy > buffer_len - (t - buffer) - 1)
334 chars_to_copy = (int) (buffer_len - (t - buffer) - 1);
335 strncpy(t, tmpbuf, (size_t) chars_to_copy);
336 free(tmpbuf);
337 t += chars_to_copy;
338 e++;
339 if (*e == '=')
340 e++;
341 d = c = e;
342 adjacentflag = TRUE;
343 }
344 }
345 }
346 }
347 }
348 while (d != c && t - buffer < buffer_len - 1)
349 *t++ = *d++;
350 }
351 *t = '\0';
352 free(sc);
353
354 return buffer;
355}
356
357
358/*
359 * adopted by J. Shin(jshin@pantheon.yale.edu) from
360 * Woohyung Choi's(whchoi@cosmos.kaist.ac.kr) sdn2ks and ks2sdn
361 */
362static void
364 const char *from,
365 char *to)
366{
367 short int i, count;
368 unsigned long tmp;
369
370 while (*from) {
371 for (i = count = 0, tmp = 0; i < 3; i++)
372 if (*from) {
373 tmp = (tmp << 8) | (unsigned long) (*from++ & 0x0ff);
374 count++;
375 } else
376 tmp = (tmp << 8) | (unsigned long) 0;
377
378 *to++ = base64_alphabet[(0x0fc0000 & tmp) >> 18];
379 *to++ = base64_alphabet[(0x003f000 & tmp) >> 12];
380 *to++ = count >= 2 ? base64_alphabet[(0x0000fc0 & tmp) >> 6] : '=';
381 *to++ = count >= 3 ? base64_alphabet[0x000003f & tmp] : '=';
382 }
383
384 *to = '\0';
385}
386
387
388static int
390 char *w,
391 char *b,
392 size_t max_ewsize,
393 t_bool isstruct_head)
394{
395 char tmp[60]; /* strings to be B encoded */
396 char *t = tmp;
397 int count = (int) (max_ewsize / 4 * 3);
398 t_bool isleading_between = TRUE; /* are we still processing leading space */
399
400#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
401 while (count-- > 0 && (!isbetween(*w, isstruct_head) || isleading_between) && *w) {
402 if (!isbetween(*w, isstruct_head))
403 isleading_between = FALSE;
404 *(t++) = *(w++);
405 /*
406 * ensure that the next multi-octet character
407 * fits into the remaining space
408 */
409 if (mbtowc(NULL, w, MB_CUR_MAX) > count)
410 break;
411 }
412#else
413 int len8 = 0; /* the number of trailing 8bit chars, which
414 should be even (i.e. the first and second byte
415 of wide_char should NOT be split into two
416 encoded words) in order to be compatible with
417 some CJK mail client */
418
419 while (count-- > 0 && (!isbetween(*w, isstruct_head) || isleading_between) && *w) {
420 len8 += (is_EIGHT_BIT(w) ? 1 : -len8);
421 if (!isbetween(*w, isstruct_head))
422 isleading_between = FALSE;
423 *(t++) = *(w++);
424 }
425
426/* if (len8 & (unsigned long) 1 && !isbetween(*w,isstruct_head)) */
427 if (len8 != len8 / 2 * 2 && !isbetween(*w, isstruct_head) && (*w))
428 t--;
429#endif /* MULTIBYTE_ABLE && !NOLOCALE */
430
431 *t = '\0';
432
433 str2b64(tmp, b);
434
435 return (int) (t - tmp);
436}
437
438
439/*
440 * find out whether encoding is necessary and which encoding
441 * to use if necessary by scanning the whole header field
442 * instead of each fragment of it.
443 * This will ensure that either Q or B encoding will be used in a single
444 * header (i.e. two encoding won't be mixed in a single header line).
445 * Mixing two encodings is not a violation of RFC 2047 but may break
446 * some news/mail clients.
447 *
448 * mmnwcharset is ignored unless CHARSET_CONVERSION
449 */
450static int
452 char *w)
453{
454 int chars = 0;
455 int schars = 0;
456 int nonprint = 0;
457
458 while (*w && isspace((unsigned char) *w))
459 w++;
460 while (*w) {
461 if (is_EIGHT_BIT(w))
462 nonprint++;
463 if (!nonprint && *w == '=' && *(w + 1) == '?')
464 nonprint = 1;
465 if (*w == '=' || *w == '?' || *w == '_')
466 schars++;
467 chars++;
468 w++;
469 }
470 if (nonprint) {
471 if (chars + 2 * (nonprint + schars) /* QP size */ >
472 (chars * 4 + 3) / 3 /* B64 size */)
473 return 'B';
474 return 'Q';
475 }
476 return 0;
477}
478
479
480/* now only checks if there's any 8bit chars in a given "fragment" */
481static t_bool
483 char *w,
484 t_bool isstruct_head)
485{
486 t_bool nonprint = FALSE;
487
488 /* first skip all leading whitespaces */
489 while (*w && isspace((unsigned char) *w))
490 w++;
491
492 /* then check the next word */
493 while (!nonprint && *w && !isbetween(*w, isstruct_head)) {
494 if (is_EIGHT_BIT(w))
495 nonprint = TRUE;
496 else if (*w == '=' && *(w + 1) == '?') {
497 /*
498 * to be exact we must look for ?= in the same word
499 * not in the whole string and check ?B? or ?Q? in the word...
500 * best would be using a regexp like
501 * ^=\?\S+\?[qQbB]\?\S+\?=
502 */
503 if (strstr(w, "?=") != NULL)
504 nonprint = TRUE;
505 }
506 w++;
507 }
508 return nonprint;
509}
510
511
512/*
513 * implement mandatory break-up of long lines in mail messages in accordance
514 * with rfc 2047 (rfc 1522)
515 */
516static int
518 char *w)
519{
520 char *x;
521
522 x = w;
523 while (*x && isspace((unsigned char) *x))
524 x++;
525 while (*x && !isspace((unsigned char) *x))
526 x++;
527 return (int) (x - w);
528}
529
530
531static t_bool
533 char *what,
534 char **where,
535 const char *charset,
536 t_bool break_long_line)
537{
538 /*
539 * We need to meet several partly contradictional requirements here.
540 * First of all, a line containing MIME encodings must not be longer
541 * than 76 chars (including delimiters, charset, encoding). Second,
542 * we should not encode more than necessary. Third, we should not
543 * produce more overhead than absolutely necessary; this means we
544 * should extend chunks over several words if there are more
545 * characters-to-quote to come. This means we have to rely on some
546 * heuristics. We process whole words, checking if it contains
547 * characters to be quoted. If not, the word is output 'as is',
548 * previous quoting being terminated before. If two adjoining words
549 * contain non-printable characters, they are encoded together (up
550 * to 60 characters). If a resulting encoded word would break the
551 * 76 characters boundary, we 'break' the line, output a SPACE, then
552 * output the encoded word. Note that many wide-spread news applications,
553 * notably INN's xover support, does not understand multiple-lines,
554 * so it's a compile-time feature with default off.
555 *
556 * To make things a bit easier, we do all processing in two stages;
557 * first we build all encoded words without any bells and whistles
558 * (just checking that they don get longer than 76 characters),
559 * then, in a second pass, we replace all SPACEs inside encoded
560 * words by '_', break long lines, etc.
561 */
562 char *buffer; /* buffer for encoded stuff */
563 char *c;
564 char *t;
565 char buf2[80]; /* buffer for this and that */
566 int encoding; /* which encoding to use ('B' or 'Q') */
567 size_t ew_taken_len;
568 int word_cnt = 0;
569 int offset;
570 size_t bufferlen = 2048; /* size of buffer */
571 size_t ewsize = 0; /* size of current encoded-word */
572 t_bool quoting = FALSE; /* currently inside quote block? */
573 t_bool any_quoting_done = FALSE;
574 t_bool isbroken_within = FALSE; /* is word broken due to length restriction on encoded of word? */
575 t_bool isstruct_head = FALSE; /* are we dealing with structured header? */
576 t_bool rightafter_ew = FALSE;
577/*
578 * the list of structured header fields where '(' and ')' are
579 * treated specially in rfc 1522 encoding
580 */
581 static const char *struct_header[] = {
582 "Approved: ", "From: ", "Originator: ",
583 "Reply-To: ", "Sender: ", "X-Cancelled-By: ", "X-Comment-To: ",
584 "X-Submissions-To: ", "To: ", "Cc: ", "Bcc: ", "X-Originator: ", NULL };
585 const char **strptr = struct_header;
586
587 do {
588 if (!strncasecmp(what, *strptr, strlen(*strptr))) {
589 isstruct_head = TRUE;
590 break;
591 }
592 } while (*(++strptr) != NULL);
593
594 t = buffer = my_malloc(bufferlen);
595 encoding = which_encoding(what);
596 ew_taken_len = strlen(charset) + 7 /* =?c?E?d?= */;
597 while (*what) {
598 if (break_long_line)
599 word_cnt++;
600 /*
601 * if a word with 8bit chars is broken in the middle, whatever
602 * follows after the point where it's split should be encoded (i.e.
603 * even if they are made of only 7bit chars)
604 */
605 if (contains_nonprintables(what, isstruct_head) || isbroken_within) {
606 if (encoding == 'Q') {
607 if (!quoting) {
608 snprintf(buf2, sizeof(buf2), "=?%s?%c?", charset, encoding);
609 while ((size_t) (t - buffer) + strlen(buf2) >= bufferlen) {
610 /* buffer too small, double its size */
611 offset = (int) (t - buffer);
612 bufferlen <<= 1;
613 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
614 t = buffer + offset;
615 }
616 ewsize = mystrcat(&t, buf2);
617 if (break_long_line) {
618 if (word_cnt == 2) {
619 /*
620 * Make sure we fit the first encoded
621 * word in with the header keyword,
622 * since we cannot break the line
623 * directly after the keyword.
624 */
625 ewsize = (size_t) (t - buffer);
626 }
627 }
628 quoting = TRUE;
629 any_quoting_done = TRUE;
630 }
631 isbroken_within = FALSE;
632 while (*what && !isbetween(*what, isstruct_head)) {
633#if 0
634 if (is_EIGHT_BIT(what) || (strchr(RFC2047_ESPECIALS, *what)))
635#else
636 if (is_EIGHT_BIT(what) || !isalnum((int)(unsigned char) *what))
637#endif /* 0 */
638 {
639 snprintf(buf2, sizeof(buf2), "=%2.2X", *EIGHT_BIT(what));
640 if ((size_t) (t - buffer + 3) >= bufferlen) {
641 /* buffer too small, double its size */
642 offset = (int) (t - buffer);
643 bufferlen <<= 1;
644 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
645 t = buffer + offset;
646 }
647 *t++ = buf2[0];
648 *t++ = buf2[1];
649 *t++ = buf2[2];
650 ewsize += 3;
651 } else {
652 if ((size_t) (t - buffer + 1) >= bufferlen) {
653 /* buffer too small, double its size */
654 offset = (int) (t - buffer);
655 bufferlen <<= 1;
656 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
657 t = buffer + offset;
658 }
659 *t++ = *what;
660 ewsize++;
661 }
662 what++;
663 /*
664 * Be sure to encode at least one char, even if
665 * that overflows the line limit, otherwise, we
666 * will be stuck in a loop (if this were in the
667 * while condition above). (Can only happen in
668 * the first line, if we have a very long
669 * header keyword, I think).
670 */
671 if (ewsize >= 71) {
672 isbroken_within = TRUE;
673 break;
674 }
675 }
676 if (!contains_nonprintables(what, isstruct_head) || ewsize >= 70 - strlen(charset)) {
677 /* next word is 'clean', close encoding */
678 if ((size_t) (t - buffer + 2) >= bufferlen) {
679 /* buffer too small, double its size */
680 offset = (int) (t - buffer);
681 bufferlen <<= 1;
682 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
683 t = buffer + offset;
684 }
685 *t++ = '?';
686 *t++ = '=';
687 ewsize += 2;
688 /*
689 */
690 if (ewsize >= 70 - strlen(charset) && (contains_nonprintables(what, isstruct_head) || isbroken_within)) {
691 if ((size_t) (t - buffer + 1) >= bufferlen) {
692 /* buffer too small, double its size */
693 offset = (int) (t - buffer);
694 bufferlen <<= 1;
695 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
696 t = buffer + offset;
697 }
698 *t++ = ' ';
699 ewsize++;
700 }
701 quoting = FALSE;
702 } else {
703 /* process whitespace in-between by quoting it properly */
704 while (*what && isspace((unsigned char) *what)) {
705 if ((size_t) (t - buffer + 3) >= bufferlen) {
706 /* buffer probably too small, double its size */
707 offset = (int) (t - buffer);
708 bufferlen <<= 1;
709 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
710 t = buffer + offset;
711 }
712 if (*what == 32 /* not ' ', compare chapter 4! */ ) {
713 *t++ = '_';
714 ewsize++;
715 } else {
716 snprintf(buf2, sizeof(buf2), "=%2.2X", *EIGHT_BIT(what));
717 *t++ = buf2[0];
718 *t++ = buf2[1];
719 *t++ = buf2[2];
720 ewsize += 3;
721 }
722 what++;
723 } /* end of while */
724 } /* end of else */
725 } else { /* end of Q encoding and beg. of B encoding */
726 /*
727 * if what immediately precedes the current fragment with 8bit
728 * char is encoded word, the leading spaces should be encoded
729 * together with 8bit chars following them. No need to worry
730 * about '(',')' and '"' as they're already excluded with
731 * contain_nonprintables used in outer if-clause
732 */
733 while (*what && (!isbetween(*what, isstruct_head) || rightafter_ew)) {
734 snprintf(buf2, sizeof(buf2), "=?%s?%c?", charset, encoding);
735 while ((size_t) (t - buffer) + strlen(buf2) >= bufferlen) {
736 /* buffer too small, double its size */
737 offset = (int) (t - buffer);
738 bufferlen <<= 1;
739 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
740 t = buffer + offset;
741 }
742 ewsize = mystrcat(&t, buf2);
743
744 if (word_cnt == 2)
745 ewsize = (size_t) (t - buffer);
746 what += do_b_encode(what, buf2, 75 - ew_taken_len, isstruct_head);
747 while ((size_t) (t - buffer) + strlen(buf2) + 3 >= bufferlen) {
748 /* buffer too small, double its size */
749 offset = (int) (t - buffer);
750 bufferlen <<= 1;
751 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
752 t = buffer + offset;
753 }
754 ewsize += mystrcat(&t, buf2);
755 *t++ = '?';
756 *t++ = '=';
757 *t++ = ' ';
758 ewsize += 3;
759 if (break_long_line)
760 word_cnt++;
761 rightafter_ew = FALSE;
762 any_quoting_done = TRUE;
763 }
764 rightafter_ew = TRUE;
765 word_cnt--; /* compensate double counting */
766 /*
767 * if encoded word is followed by 7bit-only fragment, we need to
768 * eliminate ' ' inserted in while-block above
769 */
770 if (!contains_nonprintables(what, isstruct_head)) {
771 t--;
772 ewsize--;
773 }
774 } /* end of B encoding */
775 } else {
776 while (*what && !isbetween(*what, isstruct_head)) {
777 if ((size_t) (t - buffer + 1) >= bufferlen) {
778 /* buffer too small, double its size */
779 offset = (int) (t - buffer);
780 bufferlen <<= 1;
781 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
782 t = buffer + offset;
783 }
784 *t++ = *what++; /* output word unencoded */
785 }
786 while (*what && isbetween(*what, isstruct_head)) {
787 if ((size_t) (t - buffer + 1) >= bufferlen) {
788 /* buffer too small, double its size */
789 offset = (int) (t - buffer);
790 bufferlen <<= 1;
791 buffer = my_realloc(buffer, bufferlen * sizeof(*buffer));
792 t = buffer + offset;
793 }
794 *t++ = *what++; /* output trailing whitespace unencoded */
795 }
796 rightafter_ew = FALSE;
797 }
798 } /* end of pass 1 while loop */
799 *t = '\0';
800
801 /* Pass 2: break long lines if there are MIME-sequences in the result */
802 c = buffer;
803 if (break_long_line && any_quoting_done) {
804 char *new_buffer;
805 size_t new_bufferlen = strlen(buffer) * 2 + 1; /* maximum length if
806every "word" were a space ... */
807 int column = 0; /* current column */
808
809 new_buffer = my_malloc(new_bufferlen);
810 t = new_buffer;
811 word_cnt = 1; /*
812 * note, if the user has typed a continuation
813 * line, we will consider the initial
814 * whitespace to be delimiting word one (well,
815 * just assume an empty word).
816 */
817 while (*c) {
818 if (isspace((unsigned char) *c)) {
819 /*
820 * According to rfc1522, header lines containing encoded
821 * words are limited to 76 chars, but if the first line is
822 * too long (due to a long header keyword), we cannot stick
823 * to that, since we would break the line directly after the
824 * keyword's colon, which is not allowed. The same is
825 * necessary for a continuation line with an unencoded word
826 * that is too long.
827 */
828 if (sizeofnextword(c) + column > 76 && word_cnt != 1) {
829 *t++ = '\n';
830 column = 0;
831 }
832 if (c > buffer && !isspace((unsigned char) *(c - 1)))
833 word_cnt++;
834 *t++ = *c++;
835 column++;
836 } else
837 while (*c && !isspace((unsigned char) *c)) {
838 *t++ = *c++;
839 column++;
840 }
841 }
842 FreeIfNeeded(buffer);
843 buffer = new_buffer;
844 }
845 *t = '\0';
846 *where = buffer;
847 return any_quoting_done;
848}
849
850
851/*
852 * calling code must free() the result if it's no longer needed
853 */
854char *
856 char *s,
857 const char *charset,
858 t_bool ismail)
859{
860 char *buf;
861
862 /*
863 * break_long_line is FALSE for news posting unless
864 * MIME_BREAK_LONG_LINES is defined, but it's TRUE for mail messages
865 * regardless of whether or not MIME_BREAK_LONG_LINES is defined
866 */
867#ifdef MIME_BREAK_LONG_LINES
868 t_bool break_long_line = TRUE;
869#else
870 /*
871 * Even if MIME_BREAK_LONG_LINES is NOT defined, long headers in mail
872 * messages should be broken up in accordance with RFC 2047(1522)
873 */
874 t_bool break_long_line = ismail;
875#endif /* MIME_BREAK_LONG_LINES */
876
877 rfc1522_do_encode(s, &buf, charset, break_long_line);
878
879 return buf;
880}
881
882
883/*
884 * Helper function doing the hard work for rfc15211522_encode().
885 * Code moved from rfc15211522_encode(), with some adjustments to work on a
886 * file specified by a FILE* instead of a filename.
887 */
888static void
890 FILE *f,
891 constext * mime_encoding,
892 struct t_group *group,
893 t_bool allow_8bit_header,
894 t_bool ismail,
895 t_bool contains_headers)
896{
897 FILE *g;
898 char *c;
899 char *header;
900 char encoding;
901 char buffer[2048];
902 t_bool mime_headers_needed = FALSE;
903 BodyPtr body_encode;
904 int i;
905#ifdef CHARSET_CONVERSION
906 int mmnwcharset;
907
908 if (group) /* Posting */
909 mmnwcharset = group->attribute->mm_network_charset;
910 else /* E-Mail */
911 mmnwcharset = tinrc.mm_network_charset;
912#endif /* CHARSET_CONVERSION */
913
914 if ((g = tmpfile()) == NULL)
915 return;
916
917 while (contains_headers && (header = tin_fgets(f, TRUE))) {
918#ifdef CHARSET_CONVERSION
919 buffer_to_network(header, mmnwcharset);
920#endif /* CHARSET_CONVERSION */
921 if (*header == '\0')
922 break;
923
924 /*
925 * TODO: - what about 8bit chars in the mentioned headers
926 * when !allow_8bit_header?
927 * - what about lines longer 998 octets?
928 */
929 if (allow_8bit_header || (!strncasecmp(header, "References: ", 12) || !strncasecmp(header, "Message-ID: ", 12) || !strncasecmp(header, "Date: ", 6) || !strncasecmp(header, "Newsgroups: ", 12) || !strncasecmp(header, "Distribution: ", 14) || !strncasecmp(header, "Followup-To: ", 13) || !strncasecmp(header, "X-Face: ", 8) || !strncasecmp(header, "Cancel-Lock: ", 13) || !strncasecmp(header, "Cancel-Key: ", 12)))
930 fputs(header, g);
931 else {
932 char *p;
933
934#ifdef CHARSET_CONVERSION
935 p = rfc1522_encode(header, txt_mime_charsets[mmnwcharset], ismail);
936#else
937 p = rfc1522_encode(header, tinrc.mm_charset, ismail);
938#endif /* CHARSET_CONVERSION */
939
940 fputs(p, g);
941 free(p);
942 }
943 fputc('\n', g);
944 }
945
946 fputc('\n', g);
947
948 while (fgets(buffer, 2048, f)) {
949#ifdef CHARSET_CONVERSION
950 buffer_to_network(buffer, mmnwcharset);
951#endif /* CHARSET_CONVERSION */
952 fputs(buffer, g);
953 if (!allow_8bit_header) {
954 /* see if there are any 8bit chars in the body... */
955 for (c = buffer; *c && !isreturn(*c); c++) {
956 if (is_EIGHT_BIT(c)) {
957 mime_headers_needed = TRUE;
958 break;
959 }
960 }
961 }
962 }
963
964 rewind(g);
965 rewind(f);
966#ifdef HAVE_FTRUNCATE
967 if (ftruncate(fileno(f), 0) == -1) {
968# ifdef DEBUG
969 int e = errno;
970 if (debug & DEBUG_MISC)
971 error_message(2, "ftruncate(): Error: %s", strerror(e));
972# endif /* DEBUG */
973 }
974#endif /* HAVE_FTRUNCATE */
975
976 /* copy header */
977 while (fgets(buffer, 2048, g) && !isreturn(buffer[0]))
978 fputs(buffer, f);
979
980 if (!allow_8bit_header) {
981 /*
982 * 7bit charsets except US-ASCII also need mime headers
983 */
984 for (i = 1; txt_mime_7bit_charsets[i] != NULL; i++) {
985#ifdef CHARSET_CONVERSION
986 if (!strcasecmp(txt_mime_charsets[mmnwcharset], txt_mime_7bit_charsets[i])) {
987 mime_headers_needed = TRUE;
988 break;
989 }
990#else
992 mime_headers_needed = TRUE;
993 break;
994 }
995#endif /* CHARSET_CONVERSION */
996 }
997
998 /*
999 * now add MIME headers as necessary
1000 */
1001 if (mime_headers_needed) {
1002 if (contains_headers)
1003 fprintf(f, "MIME-Version: %s\n", MIME_SUPPORTED_VERSION);
1004#ifdef CHARSET_CONVERSION
1005 fprintf(f, "Content-Type: text/plain; charset=%s\n", txt_mime_charsets[mmnwcharset]);
1006#else
1007 fprintf(f, "Content-Type: text/plain; charset=%s\n", tinrc.mm_charset);
1008#endif /* CHARSET_CONVERSION */
1009 fprintf(f, "Content-Transfer-Encoding: %s\n", mime_encoding);
1010 }
1011 }
1012 fputc('\n', f);
1013
1014 if (!allow_8bit_header) {
1015 if (!strcasecmp(mime_encoding, txt_base64))
1016 encoding = 'b';
1017 else if (!strcasecmp(mime_encoding, txt_quoted_printable))
1018 encoding = 'q';
1019 else if (!strcasecmp(mime_encoding, txt_7bit))
1020 encoding = '7';
1021 else
1022 encoding = '8';
1023
1024 /* avoid break of long lines for US-ASCII/quoted-printable */
1025 if (!mime_headers_needed)
1026 encoding = '8';
1027
1028 body_encode = rfc1521_encode;
1029
1030 while (fgets(buffer, 2048, g))
1031 body_encode(buffer, f, encoding);
1032
1033 if (encoding == 'b' || encoding == 'q' || encoding == '7')
1034 body_encode(NULL, f, encoding); /* flush */
1035 } else {
1036 while (fgets(buffer, 2048, g))
1037 fputs(buffer, f);
1038 }
1039
1040 fclose(g);
1041}
1042
1043
1044void
1046 const char *filename,
1047 constext * mime_encoding,
1048 struct t_group *group,
1049 t_bool allow_8bit_header,
1050 t_bool ismail)
1051{
1052 FILE *fp;
1053
1054 if ((fp = fopen(filename, "r+")) == NULL)
1055 return;
1056
1057 do_rfc15211522_encode(fp, mime_encoding, group, allow_8bit_header, ismail, TRUE);
1058
1059 fclose(fp);
1060}
1061
1062
1063/*
1064 * Generate a MIME boundary being unique with high probability, consisting
1065 * of len - 1 random characters.
1066 * This function is used as a last resort if anything else failed to
1067 * generate a truly unique boundary.
1068 */
1069static void
1071 char *boundary,
1072 size_t len)
1073{
1074 size_t i;
1075
1076 srand((unsigned int) time(NULL));
1077 for (i = 0; i < len - 1; i++)
1078 boundary[i] = base64_alphabet[(size_t) rand() % sizeof(base64_alphabet)];
1079 boundary[len - 1] = '\0';
1080}
1081
1082
1083/*
1084 * Generate a unique MIME boundary.
1085 * boundary must have enough space for at least MIME_BOUNDARY_SIZE characters.
1086 */
1087static void
1089 char *boundary,
1090 FILE *f,
1091 FILE *g)
1092{
1093 const char nice_chars[] = { '-', '_', '=' };
1094 const size_t prefix_len = sizeof(MIME_BOUNDARY_PREFIX) - 1;
1095 char *s;
1096 size_t i = 0;
1097 t_bool unique = FALSE;
1098
1099 /*
1100 * Choose MIME boundary as follows:
1101 * - Always start with MIME_BOUNDARY_PREFIX.
1102 * - Append MIME_BOUNDARY_DEFAULT_PART.
1103 * - If necessary, change it from right to left, choosing from a set of
1104 * `nice_chars' characters.
1105 * - After that, if it is still not unique, replace MIME_BOUNDARY_DEFAULT_PART
1106 * with random characters and hope the best.
1107 */
1108
1109 strcpy(boundary, MIME_BOUNDARY_PREFIX);
1110 strcat(boundary, MIME_BOUNDARY_DEFAULT_PART);
1111
1112 s = boundary + MIME_BOUNDARY_SIZE - 2; /* set s to last character before '\0' */
1113 do {
1114 /*
1115 * Scan for entire boundary in both f and g.
1116 * When found: modify and redo.
1117 */
1118 if (contains_string(f, boundary) || contains_string(g, boundary)) {
1119 *s = nice_chars[i];
1120 if ((i = (i + 1) % sizeof(nice_chars)) == 0)
1121 --s;
1122 } else
1123 unique = TRUE;
1124 } while (!unique && s >= boundary + prefix_len);
1125
1126 if (!unique)
1127 generate_random_mime_boundary(boundary + prefix_len, sizeof(MIME_BOUNDARY_DEFAULT_PART));
1128}
1129
1130
1131/*
1132 * Split mail into header and (optionally) body.
1133 *
1134 * If textfp is not NULL, everything behind the header is stored in it.
1135 * Whenever an error is encountered, all files are closed and FALSE is returned.
1136 */
1137static t_bool
1139 const char *filename,
1140 FILE **headerfp,
1141 FILE **textfp)
1142{
1143 FILE *fp;
1144 char *line;
1145
1146 if ((fp = fopen(filename, "r")) == NULL)
1147 return FALSE;
1148
1149 /* Header */
1150 if ((*headerfp = tmpfile()) == NULL) {
1151 fclose(fp);
1152 return FALSE;
1153 }
1154
1155 while ((line = tin_fgets(fp, TRUE))) {
1156 if (*line == '\0')
1157 break;
1158 else
1159 fprintf(*headerfp, "%s\n", line);
1160 }
1161
1162 /* Body */
1163 if (textfp != NULL) {
1164 if ((*textfp = tmpfile()) == NULL) {
1165 fclose(fp);
1166 fclose(*headerfp);
1167 return FALSE;
1168 }
1169
1170 while ((line = tin_fgets(fp, FALSE)))
1171 fprintf(*textfp, "%s\n", line);
1172 }
1173
1174 fclose(fp);
1175 return TRUE;
1176}
1177
1178
1179/*
1180 * Compose a mail consisting of a sole text/plain MIME part.
1181 */
1182void
1184 const char *filename,
1185 struct t_group *group)
1186{
1188}
1189
1190
1191/*
1192 * Compose a mail consisting of an optional text/plain and a message/rfc822
1193 * part.
1194 *
1195 * At this point, the file denoted by `filename' contains some common headers
1196 * and any text the user entered. The file `articlefp' contains the forwarded
1197 * article in raw form.
1198 */
1199void
1201 const char *filename,
1202 FILE *articlefp,
1203 t_bool include_text,
1204 struct t_group *group)
1205{
1206 FILE *fp;
1207 FILE *headerfp;
1208 FILE *textfp = NULL;
1209 FILE *entityfp;
1210 char *line;
1212 t_bool allow_8bit_header = (group ? group->attribute->mail_8bit_header : tinrc.mail_8bit_header);
1213 t_bool _8bit;
1214
1215 /* Split mail into headers and text */
1216 if (!split_mail(filename, &headerfp, include_text ? &textfp : NULL))
1217 return;
1218
1219 /* Encode header and text */
1220 rewind(headerfp);
1221 do_rfc15211522_encode(headerfp, encoding, group, allow_8bit_header, TRUE, TRUE);
1222
1223 if (textfp) {
1224 rewind(textfp);
1225 do_rfc15211522_encode(textfp, encoding, group, allow_8bit_header, TRUE, FALSE);
1226 entityfp = compose_multipart_mixed(textfp, articlefp); /* Compose top-level MIME entity */
1227 } else
1228 entityfp = compose_message_rfc822(articlefp, &_8bit);
1229
1230 if (entityfp == NULL) {
1231 fclose(headerfp);
1232 if (textfp)
1233 fclose(textfp);
1234 return;
1235 }
1236
1237 if ((fp = fopen(filename, "w")) == NULL) {
1238 fclose(headerfp);
1239 fclose(entityfp);
1240 if (textfp)
1241 fclose(textfp);
1242 return;
1243 }
1244
1245 /* Put it all together */
1246 rewind(headerfp);
1247 while ((line = tin_fgets(headerfp, TRUE))) {
1248 if (*line != '\0')
1249 fprintf(fp, "%s\n", line);
1250 }
1251 fprintf(fp, "MIME-Version: %s\n", MIME_SUPPORTED_VERSION);
1252 rewind(entityfp);
1253 copy_fp(entityfp, fp);
1254
1255 /* Clean up */
1256 fclose(fp);
1257 fclose(headerfp);
1258 fclose(entityfp);
1259 if (textfp)
1260 fclose(textfp);
1261}
1262
1263
1264/*
1265 * Compose a message/rfc822 MIME entity containing articlefp.
1266 */
1267static FILE *
1269 FILE *articlefp,
1270 t_bool *is_8bit)
1271{
1272 FILE *fp;
1273
1274 if ((fp = tmpfile()) == NULL)
1275 return NULL;
1276
1277 *is_8bit = contains_8bit_characters(articlefp);
1278
1279 /* Header: CT, CD, CTE */
1280 fprintf(fp, "Content-Type: message/rfc822\n");
1281 fprintf(fp, "Content-Disposition: inline\n");
1282 fprintf(fp, "Content-Transfer-Encoding: %s\n", *is_8bit ? txt_8bit : txt_7bit);
1283 fputc('\n', fp);
1284
1285 /* Body: articlefp */
1286 rewind(articlefp);
1287 copy_fp(articlefp, fp);
1288
1289 return fp;
1290}
1291
1292
1293/*
1294 * Compose a multipart/mixed MIME entity consisting of a text/plain and a
1295 * message/rfc822 part.
1296 */
1297static FILE *
1299 FILE *textfp,
1300 FILE *articlefp)
1301{
1302 FILE *fp;
1303 FILE *messagefp;
1304 char boundary[MIME_BOUNDARY_SIZE];
1305 t_bool requires_8bit;
1306
1307 if ((fp = tmpfile()) == NULL)
1308 return NULL;
1309
1310 /* First compose message/rfc822 part (needed for choosing the appropriate CTE) */
1311 if ((messagefp = compose_message_rfc822(articlefp, &requires_8bit)) == NULL) {
1312 fclose(fp);
1313 return NULL;
1314 }
1315
1316 requires_8bit = (requires_8bit || contains_8bit_characters(textfp));
1317
1318 /*
1319 * Header: CT with multipart boundary, CTE
1320 * TODO: -> lang.c
1321 */
1322 generate_mime_boundary(boundary, textfp, articlefp);
1323 fprintf(fp, "Content-Type: multipart/mixed; boundary=\"%s\"\n", boundary);
1324 fprintf(fp, "Content-Transfer-Encoding: %s\n\n", requires_8bit ? txt_8bit : txt_7bit);
1325
1326 /*
1327 * preamble
1328 * TODO: -> lang.c
1329 */
1330 fprintf(fp, _("This message has been composed in the 'multipart/mixed' MIME-format. If you\n\
1331are reading this prefix, your mail reader probably has not yet been modified\n\
1332to understand the new format, and some of what follows may look strange.\n\n"));
1333
1334 /*
1335 * Body: boundary+text, message/rfc822 part, closing boundary
1336 */
1337 /* text */
1338 fprintf(fp, "--%s\n", boundary);
1339 rewind(textfp);
1340 copy_fp(textfp, fp);
1341 fputc('\n', fp);
1342
1343 /* message/rfc822 part */
1344 fprintf(fp, "--%s\n", boundary);
1345 rewind(messagefp);
1346 copy_fp(messagefp, fp);
1347 fclose(messagefp);
1348 fputc('\n', fp);
1349
1350 /* closing boundary */
1351 fprintf(fp, "--%s--\n", boundary);
1352 /* TODO: insert an epilogue here? */
1353 return fp;
1354}
1355
1356
1357/*
1358 * Determines whether the file denoted by fp contains 8bit characters.
1359 */
1360static t_bool
1362 FILE *fp)
1363{
1364 char *line;
1365
1366 rewind(fp);
1367 while ((line = tin_fgets(fp, FALSE))) {
1368 for (; *line != '\0'; line++) {
1369 if (is_EIGHT_BIT(line))
1370 return TRUE;
1371 }
1372 }
1373
1374 return FALSE;
1375}
1376
1377
1378/*
1379 * Determines whether any line of the file denoted by fp contains str.
1380 */
1381static t_bool
1383 FILE *fp,
1384 const char *str)
1385{
1386 char *line;
1387
1388 rewind(fp);
1389 while ((line = tin_fgets(fp, FALSE))) {
1390 if (strstr(line, str))
1391 return TRUE;
1392 }
1393
1394 return FALSE;
1395}
unsigned t_bool
Definition: bool.h:77
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
#define DEBUG_MISC
Definition: debug.h:54
constext txt_quoted_printable[]
Definition: lang.c:767
constext * txt_mime_encodings[]
Definition: lang.c:1456
constext * txt_mime_7bit_charsets[]
Definition: lang.c:1561
constext txt_8bit[]
Definition: lang.c:47
constext txt_base64[]
Definition: lang.c:113
struct t_config tinrc
Definition: init.c:192
unsigned short debug
Definition: debug.c:51
constext txt_7bit[]
Definition: lang.c:46
static char buf[16]
Definition: langinfo.c:50
int errno
int my_tolower(int)
Definition: string.c:261
FILE * tmpfile(void)
Definition: tmpfile.c:53
void process_charsets(char **line, size_t *max_line_len, const char *network_charset, const char *local_charset, t_bool conv_tex2iso)
Definition: misc.c:2656
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:224
char * strstr(const char *text, const char *pattern)
Definition: string.c:334
t_bool copy_fp(FILE *fp_ip, FILE *fp_op)
Definition: misc.c:179
void rfc1521_encode(char *line, FILE *f, int e)
Definition: rfc2045.c:75
char * my_strdup(const char *str)
Definition: string.c:139
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
int strncasecmp(const char *p, const char *q, size_t n)
Definition: string.c:491
size_t mystrcat(char **t, const char *s)
Definition: string.c:241
int strcasecmp(const char *p, const char *q)
Definition: string.c:475
#define strerror(n)
Definition: proto.h:700
static int offset
Definition: read.c:62
#define MIME_SUPPORTED_VERSION
Definition: rfc2046.h:44
const char base64_alphabet[64]
Definition: rfc2047.c:73
#define NOT_RANKED
Definition: rfc2047.c:66
static const char MIME_BOUNDARY_DEFAULT_PART[]
Definition: rfc2047.c:85
int mmdecode(const char *what, int encoding, int delimiter, char *where)
Definition: rfc2047.c:147
void rfc15211522_encode(const char *filename, constext *mime_encoding, struct t_group *group, t_bool allow_8bit_header, t_bool ismail)
Definition: rfc2047.c:1045
static unsigned char base64_rank[256]
Definition: rfc2047.c:80
static void do_rfc15211522_encode(FILE *f, constext *mime_encoding, struct t_group *group, t_bool allow_8bit_header, t_bool ismail, t_bool contains_headers)
Definition: rfc2047.c:889
void compose_mail_text_plain(const char *filename, struct t_group *group)
Definition: rfc2047.c:1183
char * rfc1522_encode(char *s, const char *charset, t_bool ismail)
Definition: rfc2047.c:855
static void str2b64(const char *from, char *to)
Definition: rfc2047.c:363
static int do_b_encode(char *w, char *b, size_t max_ewsize, t_bool isstruct_head)
Definition: rfc2047.c:389
static FILE * compose_message_rfc822(FILE *articlefp, t_bool *is_8bit)
Definition: rfc2047.c:1268
static t_bool contains_nonprintables(char *w, t_bool isstruct_head)
Definition: rfc2047.c:482
static int sizeofnextword(char *w)
Definition: rfc2047.c:517
static t_bool contains_8bit_characters(FILE *fp)
Definition: rfc2047.c:1361
static void generate_random_mime_boundary(char *boundary, size_t len)
Definition: rfc2047.c:1070
static t_bool rfc1522_do_encode(char *what, char **where, const char *charset, t_bool break_long_line)
Definition: rfc2047.c:532
static FILE * compose_multipart_mixed(FILE *textfp, FILE *articlefp)
Definition: rfc2047.c:1298
static void generate_mime_boundary(char *boundary, FILE *f, FILE *g)
Definition: rfc2047.c:1088
static t_bool split_mail(const char *filename, FILE **headerfp, FILE **textfp)
Definition: rfc2047.c:1138
@ MIME_BOUNDARY_SIZE
Definition: rfc2047.c:88
#define isbetween(c, s)
Definition: rfc2047.c:64
static int which_encoding(char *w)
Definition: rfc2047.c:451
void compose_mail_mime_forwarded(const char *filename, FILE *articlefp, t_bool include_text, struct t_group *group)
Definition: rfc2047.c:1200
static t_bool contains_string(FILE *fp, const char *str)
Definition: rfc2047.c:1382
static void build_base64_rank_table(void)
Definition: rfc2047.c:113
#define isreturn(c)
Definition: rfc2047.c:46
static const char MIME_BOUNDARY_PREFIX[]
Definition: rfc2047.c:84
char * rfc1522_decode(const char *s)
Definition: rfc2047.c:232
static int base64_rank_table_built
Definition: rfc2047.c:81
static unsigned hex2bin(int x)
Definition: rfc2047.c:129
unsigned mail_mime_encoding
Definition: tin.h:1662
unsigned mail_8bit_header
Definition: tin.h:1661
int mail_mime_encoding
Definition: tinrc.h:152
t_bool mail_8bit_header
Definition: tinrc.h:230
char mm_charset[LEN]
Definition: tinrc.h:111
char mm_local_charset[LEN]
Definition: tinrc.h:115
Definition: tin.h:1816
struct t_attribute * attribute
Definition: tin.h:1834
#define my_malloc(size)
Definition: tin.h:2245
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define CURR_GROUP
Definition: tin.h:1054
#define _(Text)
Definition: tin.h:94
#define snprintf
Definition: tin.h:2464
#define FreeAndNull(p)
Definition: tin.h:2253
const char constext
Definition: tin.h:2018
void(* BodyPtr)(char *, FILE *, int)
Definition: tin.h:2327
#define my_realloc(ptr, size)
Definition: tin.h:2247
#define EIGHT_BIT(ptr)
Definition: tin.h:2267
#define is_EIGHT_BIT(p)
Definition: tin.h:2268