NeoMutt  2024-10-02-34-g9dd811
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 "header.h"
36#include "muttlib.h"
37
42{
43 char buffer[3];
44 short size;
45 short linelen;
46};
47
53static void b64_flush(struct B64Context *bctx, FILE *fp_out)
54{
55 /* for some reasons, mutt_b64_encode expects the
56 * output buffer to be larger than 10B */
57 char encoded[11] = { 0 };
58 size_t rc;
59
60 if (bctx->size == 0)
61 return;
62
63 if (bctx->linelen >= 72)
64 {
65 fputc('\n', fp_out);
66 bctx->linelen = 0;
67 }
68
69 /* rc should always be equal to 4 here, because bctx->size
70 * is a value between 1 and 3 (included), but let's not hardcode it
71 * and prefer the return value of the function */
72 rc = mutt_b64_encode(bctx->buffer, bctx->size, encoded, sizeof(encoded));
73 for (size_t i = 0; i < rc; i++)
74 {
75 fputc(encoded[i], fp_out);
76 bctx->linelen++;
77 }
78
79 bctx->size = 0;
80}
81
88static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
89{
90 if (bctx->size == 3)
91 b64_flush(bctx, fp_out);
92
93 bctx->buffer[bctx->size++] = c;
94}
95
102static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
103{
104 struct B64Context bctx = { 0 };
105 int ch, ch1 = EOF;
106
107 while ((ch = mutt_ch_fgetconv(fc)) != EOF)
108 {
109 if (SigInt)
110 {
111 SigInt = false;
112 return;
113 }
114 if (istext && (ch == '\n') && (ch1 != '\r'))
115 b64_putc(&bctx, '\r', fp_out);
116 b64_putc(&bctx, ch, fp_out);
117 ch1 = ch;
118 }
119 b64_flush(&bctx, fp_out);
120 fputc('\n', fp_out);
121}
122
128static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
129{
130 int ch;
131
132 while ((ch = mutt_ch_fgetconv(fc)) != EOF)
133 {
134 if (SigInt)
135 {
136 SigInt = false;
137 return;
138 }
139 fputc(ch, fp_out);
140 }
141}
142
149static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
150{
151 int c, linelen = 0;
152 char line[77] = { 0 };
153
154 while ((c = mutt_ch_fgetconv(fc)) != EOF)
155 {
156 /* Wrap the line if needed. */
157 if ((linelen == 76) && ((istext && (c != '\n')) || !istext))
158 {
159 /* If the last character is "quoted", then be sure to move all three
160 * characters to the next line. Otherwise, just move the last
161 * character... */
162 if (line[linelen - 3] == '=')
163 {
164 line[linelen - 3] = 0;
165 fputs(line, fp_out);
166 fputs("=\n", fp_out);
167 line[linelen] = 0;
168 line[0] = '=';
169 line[1] = line[linelen - 2];
170 line[2] = line[linelen - 1];
171 linelen = 3;
172 }
173 else
174 {
175 char savechar = line[linelen - 1];
176 line[linelen - 1] = '=';
177 line[linelen] = 0;
178 fputs(line, fp_out);
179 fputc('\n', fp_out);
180 line[0] = savechar;
181 linelen = 1;
182 }
183 }
184
185 /* Escape lines that begin with/only contain "the message separator". */
186 if ((linelen == 4) && mutt_str_startswith(line, "From"))
187 {
188 mutt_str_copy(line, "=46rom", sizeof(line));
189 linelen = 6;
190 }
191 else if ((linelen == 4) && mutt_str_startswith(line, "from"))
192 {
193 mutt_str_copy(line, "=66rom", sizeof(line));
194 linelen = 6;
195 }
196 else if ((linelen == 1) && (line[0] == '.'))
197 {
198 mutt_str_copy(line, "=2E", sizeof(line));
199 linelen = 3;
200 }
201
202 if ((c == '\n') && istext)
203 {
204 /* Check to make sure there is no trailing space on this line. */
205 if ((linelen > 0) && ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t')))
206 {
207 if (linelen < 74)
208 {
209 sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
210 fputs(line, fp_out);
211 }
212 else
213 {
214 int savechar2 = line[linelen - 1];
215
216 line[linelen - 1] = '=';
217 line[linelen] = 0;
218 fputs(line, fp_out);
219 fprintf(fp_out, "\n=%2.2X", (unsigned char) savechar2);
220 }
221 }
222 else
223 {
224 line[linelen] = 0;
225 fputs(line, fp_out);
226 }
227 fputc('\n', fp_out);
228 linelen = 0;
229 }
230 else if ((c != 9) && ((c < 32) || (c > 126) || (c == '=')))
231 {
232 /* Check to make sure there is enough room for the quoted character.
233 * If not, wrap to the next line. */
234 if (linelen > 73)
235 {
236 line[linelen++] = '=';
237 line[linelen] = 0;
238 fputs(line, fp_out);
239 fputc('\n', fp_out);
240 linelen = 0;
241 }
242 sprintf(line + linelen, "=%2.2X", (unsigned char) c);
243 linelen += 3;
244 }
245 else
246 {
247 /* Don't worry about wrapping the line here. That will happen during
248 * the next iteration when I'll also know what the next character is. */
249 line[linelen++] = c;
250 }
251 }
252
253 /* Take care of anything left in the buffer */
254 if (linelen > 0)
255 {
256 if ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t'))
257 {
258 /* take care of trailing whitespace */
259 if (linelen < 74)
260 {
261 sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
262 }
263 else
264 {
265 char savechar = line[linelen - 1];
266 line[linelen - 1] = '=';
267 line[linelen] = 0;
268 fputs(line, fp_out);
269 fputc('\n', fp_out);
270 snprintf(line, sizeof(line), "=%2.2X", (unsigned char) savechar);
271 }
272 }
273 else
274 {
275 line[linelen] = 0;
276 }
277 fputs(line, fp_out);
278 }
279}
280
286static bool write_as_text_part(struct Body *b)
287{
288 return mutt_is_text_part(b) ||
290}
291
300int mutt_write_mime_body(struct Body *b, FILE *fp, struct ConfigSubset *sub)
301{
302 FILE *fp_in = NULL;
303 struct FgetConv *fc = NULL;
304
305 if (b->type == TYPE_MULTIPART)
306 {
307 /* First, find the boundary to use */
308 const char *p = mutt_param_get(&b->parameter, "boundary");
309 if (!p)
310 {
311 mutt_debug(LL_DEBUG1, "no boundary parameter found\n");
312 mutt_error(_("No boundary parameter found [report this error]"));
313 return -1;
314 }
315 char boundary[128] = { 0 };
316 mutt_str_copy(boundary, p, sizeof(boundary));
317
318 for (struct Body *t = b->parts; t; t = t->next)
319 {
320 fprintf(fp, "\n--%s\n", boundary);
321 if (mutt_write_mime_header(t, fp, sub) == -1)
322 return -1;
323 fputc('\n', fp);
324 if (mutt_write_mime_body(t, fp, sub) == -1)
325 return -1;
326 }
327 fprintf(fp, "\n--%s--\n", boundary);
328 return ferror(fp) ? -1 : 0;
329 }
330
331 /* This is pretty gross, but it's the best solution for now... */
332 if (((WithCrypto & APPLICATION_PGP) != 0) && (b->type == TYPE_APPLICATION) &&
333 mutt_str_equal(b->subtype, "pgp-encrypted") && !b->filename)
334 {
335 fputs("Version: 1\n", fp);
336 return 0;
337 }
338
339 fp_in = mutt_file_fopen(b->filename, "r");
340 if (!fp_in)
341 {
342 mutt_debug(LL_DEBUG1, "%s no longer exists\n", b->filename);
343 mutt_error(_("%s no longer exists"), b->filename);
344 return -1;
345 }
346
347 if ((b->type == TYPE_TEXT) && (!b->noconv))
348 {
349 char send_charset[128] = { 0 };
350 fc = mutt_ch_fgetconv_open(fp_in, b->charset,
351 mutt_body_get_charset(b, send_charset, sizeof(send_charset)),
353 }
354 else
355 {
357 }
358
362 else if (b->encoding == ENC_BASE64)
364 else if ((b->type == TYPE_TEXT) && (!b->noconv))
365 encode_8bit(fc, fp);
366 else
369
371 mutt_file_fclose(&fp_in);
372
373 if (SigInt)
374 {
375 SigInt = false;
376 return -1;
377 }
378 return ferror(fp) ? -1 : 0;
379}
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:87
SecurityFlags mutt_is_application_pgp(const struct Body *b)
Does the message use PGP?
Definition: crypt.c:548
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body's character set.
Definition: body.c:133
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:287
#define mutt_file_fclose(FP)
Definition: file.h:138
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:137
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:756
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:983
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:933
void mutt_ch_fgetconv_close(struct FgetConv **ptr)
Close an fgetconv handle.
Definition: charset.c:965
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:73
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:660
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
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:581
bool mutt_is_text_part(const struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:407
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:85
static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
Encode text as quoted printable.
Definition: body.c:149
static bool write_as_text_part(struct Body *b)
Should the Body be written as a text MIME part.
Definition: body.c:286
static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
Base64-encode some data.
Definition: body.c:102
int mutt_write_mime_body(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:300
static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
Write the data as raw 8-bit data.
Definition: body.c:128
static void b64_flush(struct B64Context *bctx, FILE *fp_out)
Save the bytes to the file.
Definition: body.c:53
static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
Base64-encode one character.
Definition: body.c:88
Convenience wrapper for the send headers.
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition: signal.c:66
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:300
Cursor for the Base64 conversion.
Definition: body.c:42
short linelen
Definition: body.c:45
char buffer[3]
Definition: body.c:43
short size
Definition: body.c:44
The body of an email.
Definition: body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:73
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:79
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:63
struct Body * next
next attachment in the list
Definition: body.h:72
char * subtype
content-type subtype
Definition: body.h:61
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:59
A set of inherited config items.
Definition: subset.h:47
Cursor for converting a file's encoding.
Definition: charset.h:43
FILE * fp
Definition: charset.h:44
char * p
Definition: charset.h:48