NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
body.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <stdbool.h>
31#include "mutt/lib.h"
32#include "email/lib.h"
33#include "body.h"
34#include "ncrypt/lib.h"
35#include "globals.h"
36#include "header.h"
37#include "muttlib.h"
38
43{
44 char buffer[3];
45 short size;
46 short linelen;
47};
48
54static void b64_flush(struct B64Context *bctx, FILE *fp_out)
55{
56 /* for some reasons, mutt_b64_encode expects the
57 * output buffer to be larger than 10B */
58 char encoded[11] = { 0 };
59 size_t rc;
60
61 if (bctx->size == 0)
62 return;
63
64 if (bctx->linelen >= 72)
65 {
66 fputc('\n', fp_out);
67 bctx->linelen = 0;
68 }
69
70 /* rc should always be equal to 4 here, because bctx->size
71 * is a value between 1 and 3 (included), but let's not hardcode it
72 * and prefer the return value of the function */
73 rc = mutt_b64_encode(bctx->buffer, bctx->size, encoded, sizeof(encoded));
74 for (size_t i = 0; i < rc; i++)
75 {
76 fputc(encoded[i], fp_out);
77 bctx->linelen++;
78 }
79
80 bctx->size = 0;
81}
82
89static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
90{
91 if (bctx->size == 3)
92 b64_flush(bctx, fp_out);
93
94 bctx->buffer[bctx->size++] = c;
95}
96
103static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
104{
105 struct B64Context bctx = { 0 };
106 int ch, ch1 = EOF;
107
108 while ((ch = mutt_ch_fgetconv(fc)) != EOF)
109 {
110 if (SigInt)
111 {
112 SigInt = false;
113 return;
114 }
115 if (istext && (ch == '\n') && (ch1 != '\r'))
116 b64_putc(&bctx, '\r', fp_out);
117 b64_putc(&bctx, ch, fp_out);
118 ch1 = ch;
119 }
120 b64_flush(&bctx, fp_out);
121 fputc('\n', fp_out);
122}
123
129static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
130{
131 int ch;
132
133 while ((ch = mutt_ch_fgetconv(fc)) != EOF)
134 {
135 if (SigInt)
136 {
137 SigInt = false;
138 return;
139 }
140 fputc(ch, fp_out);
141 }
142}
143
150static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
151{
152 int c, linelen = 0;
153 char line[77], savechar;
154
155 while ((c = mutt_ch_fgetconv(fc)) != EOF)
156 {
157 /* Wrap the line if needed. */
158 if ((linelen == 76) && ((istext && (c != '\n')) || !istext))
159 {
160 /* If the last character is "quoted", then be sure to move all three
161 * characters to the next line. Otherwise, just move the last
162 * character... */
163 if (line[linelen - 3] == '=')
164 {
165 line[linelen - 3] = 0;
166 fputs(line, fp_out);
167 fputs("=\n", fp_out);
168 line[linelen] = 0;
169 line[0] = '=';
170 line[1] = line[linelen - 2];
171 line[2] = line[linelen - 1];
172 linelen = 3;
173 }
174 else
175 {
176 savechar = line[linelen - 1];
177 line[linelen - 1] = '=';
178 line[linelen] = 0;
179 fputs(line, fp_out);
180 fputc('\n', fp_out);
181 line[0] = savechar;
182 linelen = 1;
183 }
184 }
185
186 /* Escape lines that begin with/only contain "the message separator". */
187 if ((linelen == 4) && mutt_str_startswith(line, "From"))
188 {
189 mutt_str_copy(line, "=46rom", sizeof(line));
190 linelen = 6;
191 }
192 else if ((linelen == 4) && mutt_str_startswith(line, "from"))
193 {
194 mutt_str_copy(line, "=66rom", sizeof(line));
195 linelen = 6;
196 }
197 else if ((linelen == 1) && (line[0] == '.'))
198 {
199 mutt_str_copy(line, "=2E", sizeof(line));
200 linelen = 3;
201 }
202
203 if ((c == '\n') && istext)
204 {
205 /* Check to make sure there is no trailing space on this line. */
206 if ((linelen > 0) && ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t')))
207 {
208 if (linelen < 74)
209 {
210 sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
211 fputs(line, fp_out);
212 }
213 else
214 {
215 int savechar2 = line[linelen - 1];
216
217 line[linelen - 1] = '=';
218 line[linelen] = 0;
219 fputs(line, fp_out);
220 fprintf(fp_out, "\n=%2.2X", (unsigned char) savechar2);
221 }
222 }
223 else
224 {
225 line[linelen] = 0;
226 fputs(line, fp_out);
227 }
228 fputc('\n', fp_out);
229 linelen = 0;
230 }
231 else if ((c != 9) && ((c < 32) || (c > 126) || (c == '=')))
232 {
233 /* Check to make sure there is enough room for the quoted character.
234 * If not, wrap to the next line. */
235 if (linelen > 73)
236 {
237 line[linelen++] = '=';
238 line[linelen] = 0;
239 fputs(line, fp_out);
240 fputc('\n', fp_out);
241 linelen = 0;
242 }
243 sprintf(line + linelen, "=%2.2X", (unsigned char) c);
244 linelen += 3;
245 }
246 else
247 {
248 /* Don't worry about wrapping the line here. That will happen during
249 * the next iteration when I'll also know what the next character is. */
250 line[linelen++] = c;
251 }
252 }
253
254 /* Take care of anything left in the buffer */
255 if (linelen > 0)
256 {
257 if ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t'))
258 {
259 /* take care of trailing whitespace */
260 if (linelen < 74)
261 {
262 sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
263 }
264 else
265 {
266 savechar = line[linelen - 1];
267 line[linelen - 1] = '=';
268 line[linelen] = 0;
269 fputs(line, fp_out);
270 fputc('\n', fp_out);
271 snprintf(line, sizeof(line), "=%2.2X", (unsigned char) savechar);
272 }
273 }
274 else
275 {
276 line[linelen] = 0;
277 }
278 fputs(line, fp_out);
279 }
280}
281
287static bool write_as_text_part(struct Body *b)
288{
289 return mutt_is_text_part(b) ||
291}
292
301int mutt_write_mime_body(struct Body *a, FILE *fp, struct ConfigSubset *sub)
302{
303 FILE *fp_in = NULL;
304 struct FgetConv *fc = NULL;
305
306 if (a->type == TYPE_MULTIPART)
307 {
308 /* First, find the boundary to use */
309 const char *p = mutt_param_get(&a->parameter, "boundary");
310 if (!p)
311 {
312 mutt_debug(LL_DEBUG1, "no boundary parameter found\n");
313 mutt_error(_("No boundary parameter found [report this error]"));
314 return -1;
315 }
316 char boundary[128] = { 0 };
317 mutt_str_copy(boundary, p, sizeof(boundary));
318
319 for (struct Body *t = a->parts; t; t = t->next)
320 {
321 fprintf(fp, "\n--%s\n", boundary);
322 if (mutt_write_mime_header(t, fp, sub) == -1)
323 return -1;
324 fputc('\n', fp);
325 if (mutt_write_mime_body(t, fp, sub) == -1)
326 return -1;
327 }
328 fprintf(fp, "\n--%s--\n", boundary);
329 return ferror(fp) ? -1 : 0;
330 }
331
332 /* This is pretty gross, but it's the best solution for now... */
333 if (((WithCrypto & APPLICATION_PGP) != 0) && (a->type == TYPE_APPLICATION) &&
334 mutt_str_equal(a->subtype, "pgp-encrypted") && !a->filename)
335 {
336 fputs("Version: 1\n", fp);
337 return 0;
338 }
339
340 fp_in = fopen(a->filename, "r");
341 if (!fp_in)
342 {
343 mutt_debug(LL_DEBUG1, "%s no longer exists\n", a->filename);
344 mutt_error(_("%s no longer exists"), a->filename);
345 return -1;
346 }
347
348 if ((a->type == TYPE_TEXT) && (!a->noconv))
349 {
350 char send_charset[128] = { 0 };
351 fc = mutt_ch_fgetconv_open(fp_in, a->charset,
352 mutt_body_get_charset(a, send_charset, sizeof(send_charset)),
354 }
355 else
356 {
358 }
359
363 else if (a->encoding == ENC_BASE64)
365 else if ((a->type == TYPE_TEXT) && (!a->noconv))
366 encode_8bit(fc, fp);
367 else
370
372 mutt_file_fclose(&fp_in);
373
374 if (SigInt)
375 {
376 SigInt = false;
377 return -1;
378 }
379 return ferror(fp) ? -1 : 0;
380}
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to null-terminated base64 string.
Definition: base64.c:88
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:537
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body's character set.
Definition: body.c:131
Structs that make up an email.
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:262
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:59
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:758
Convenience wrapper for the send headers.
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition: mime.h:51
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
int mutt_ch_fgetconv(struct FgetConv *fc)
Convert a file's character set.
Definition: charset.c:978
struct FgetConv * mutt_ch_fgetconv_open(FILE *fp, const char *from, const char *to, uint8_t flags)
Prepare a file for charset conversion.
Definition: charset.c:928
void mutt_ch_fgetconv_close(struct FgetConv **ptr)
Close an fgetconv handle.
Definition: charset.c:960
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:450
Some miscellaneous functions.
API for encryption/signing of emails.
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define WithCrypto
Definition: lib.h:116
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
Encode text as quoted printable.
Definition: body.c:150
static bool write_as_text_part(struct Body *b)
Should the Body be written as a text MIME part.
Definition: body.c:287
static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
Base64-encode some data.
Definition: body.c:103
int mutt_write_mime_body(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:301
static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
Write the data as raw 8-bit data.
Definition: body.c:129
static void b64_flush(struct B64Context *bctx, FILE *fp_out)
Save the bytes to the file.
Definition: body.c:54
static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
Base64-encode one character.
Definition: body.c:89
Convenience wrapper for the send headers.
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:251
Cursor for the Base64 conversion.
Definition: body.c:43
short linelen
Definition: body.c:46
char buffer[3]
Definition: body.c:44
short size
Definition: body.c:45
The body of an email.
Definition: body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
bool noconv
Don't do character set conversion.
Definition: body.h:46
char * charset
Send mode: charset of attached file as stored on disk.
Definition: body.h:78
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
struct Body * next
next attachment in the list
Definition: body.h:71
char * subtype
content-type subtype
Definition: body.h:60
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:58
A set of inherited config items.
Definition: subset.h:47
Cursor for converting a file's encoding.
Definition: charset.h:41
FILE * fp
Definition: charset.h:42
char * p
Definition: charset.h:46