NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
body.c File Reference

Write a MIME Email Body to a file. More...

#include "config.h"
#include <stdbool.h>
#include "mutt/lib.h"
#include "email/lib.h"
#include "body.h"
#include "ncrypt/lib.h"
#include "header.h"
#include "muttlib.h"
+ Include dependency graph for body.c:

Go to the source code of this file.

Data Structures

struct  B64Context
 Cursor for the Base64 conversion. More...
 

Functions

static void b64_flush (struct B64Context *bctx, FILE *fp_out)
 Save the bytes to the file.
 
static void b64_putc (struct B64Context *bctx, char c, FILE *fp_out)
 Base64-encode one character.
 
static void encode_base64 (struct FgetConv *fc, FILE *fp_out, int istext)
 Base64-encode some data.
 
static void encode_8bit (struct FgetConv *fc, FILE *fp_out)
 Write the data as raw 8-bit data.
 
static void encode_quoted (struct FgetConv *fc, FILE *fp_out, bool istext)
 Encode text as quoted printable.
 
static bool write_as_text_part (struct Body *b)
 Should the Body be written as a text MIME part.
 
int mutt_write_mime_body (struct Body *b, FILE *fp, struct ConfigSubset *sub)
 Write a MIME part.
 

Detailed Description

Write a MIME Email Body to a file.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file body.c.

Function Documentation

◆ b64_flush()

static void b64_flush ( struct B64Context * bctx,
FILE * fp_out )
static

Save the bytes to the file.

Parameters
bctxCursor for the base64 conversion
fp_outFile to save the output

Definition at line 53 of file body.c.

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}
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to a base64 string.
Definition base64.c:148
short linelen
Length of the line so far.
Definition body.c:45
char buffer[3]
Buffer for the conversion.
Definition body.c:43
short size
Number of bytes in the buffer.
Definition body.c:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ b64_putc()

static void b64_putc ( struct B64Context * bctx,
char c,
FILE * fp_out )
static

Base64-encode one character.

Parameters
bctxCursor for the base64 conversion
cCharacter to encode
fp_outFile to save the output

Definition at line 88 of file body.c.

89{
90 if (bctx->size == 3)
91 b64_flush(bctx, fp_out);
92
93 bctx->buffer[bctx->size++] = c;
94}
static void b64_flush(struct B64Context *bctx, FILE *fp_out)
Save the bytes to the file.
Definition body.c:53
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ encode_base64()

static void encode_base64 ( struct FgetConv * fc,
FILE * fp_out,
int istext )
static

Base64-encode some data.

Parameters
fcCursor for converting a file's encoding
fp_outFile to store the result
istextIs the input text?

Definition at line 102 of file body.c.

103{
104 struct B64Context bctx = { 0 };
105 int ch;
106 int 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}
int mutt_ch_fgetconv(struct FgetConv *fc)
Convert a file's character set.
Definition charset.c:968
static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
Base64-encode one character.
Definition body.c:88
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition signal.c:68
Cursor for the Base64 conversion.
Definition body.c:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ encode_8bit()

static void encode_8bit ( struct FgetConv * fc,
FILE * fp_out )
static

Write the data as raw 8-bit data.

Parameters
fcCursor for converting a file's encoding
fp_outFile to store the result

Definition at line 129 of file body.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ encode_quoted()

static void encode_quoted ( struct FgetConv * fc,
FILE * fp_out,
bool istext )
static

Encode text as quoted printable.

Parameters
fcCursor for converting a file's encoding
fp_outFile to store the result
istextIs the input text?

Definition at line 150 of file body.c.

151{
152 int c;
153 int linelen = 0;
154 char line[77] = { 0 };
155
156 while ((c = mutt_ch_fgetconv(fc)) != EOF)
157 {
158 /* Wrap the line if needed. */
159 if ((linelen == 76) && ((istext && (c != '\n')) || !istext))
160 {
161 /* If the last character is "quoted", then be sure to move all three
162 * characters to the next line. Otherwise, just move the last
163 * character... */
164 if (line[linelen - 3] == '=')
165 {
166 line[linelen - 3] = 0;
167 fputs(line, fp_out);
168 fputs("=\n", fp_out);
169 line[linelen] = 0;
170 line[0] = '=';
171 line[1] = line[linelen - 2];
172 line[2] = line[linelen - 1];
173 linelen = 3;
174 }
175 else
176 {
177 char savechar = line[linelen - 1];
178 line[linelen - 1] = '=';
179 line[linelen] = 0;
180 fputs(line, fp_out);
181 fputc('\n', fp_out);
182 line[0] = savechar;
183 linelen = 1;
184 }
185 }
186
187 /* Escape lines that begin with/only contain "the message separator". */
188 if ((linelen == 4) && mutt_str_startswith(line, "From"))
189 {
190 mutt_str_copy(line, "=46rom", sizeof(line));
191 linelen = 6;
192 }
193 else if ((linelen == 4) && mutt_str_startswith(line, "from"))
194 {
195 mutt_str_copy(line, "=66rom", sizeof(line));
196 linelen = 6;
197 }
198 else if ((linelen == 1) && (line[0] == '.'))
199 {
200 mutt_str_copy(line, "=2E", sizeof(line));
201 linelen = 3;
202 }
203
204 if ((c == '\n') && istext)
205 {
206 /* Check to make sure there is no trailing space on this line. */
207 if ((linelen > 0) && ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t')))
208 {
209 if (linelen < 74)
210 {
211 snprintf(line + linelen - 1, sizeof(line) - (linelen - 1), "=%2.2X",
212 (unsigned char) line[linelen - 1]);
213 fputs(line, fp_out);
214 }
215 else
216 {
217 int savechar2 = line[linelen - 1];
218
219 line[linelen - 1] = '=';
220 line[linelen] = 0;
221 fputs(line, fp_out);
222 fprintf(fp_out, "\n=%2.2X", (unsigned char) savechar2);
223 }
224 }
225 else
226 {
227 line[linelen] = 0;
228 fputs(line, fp_out);
229 }
230 fputc('\n', fp_out);
231 linelen = 0;
232 }
233 else if ((c != 9) && ((c < 32) || (c > 126) || (c == '=')))
234 {
235 /* Check to make sure there is enough room for the quoted character.
236 * If not, wrap to the next line. */
237 if (linelen > 73)
238 {
239 line[linelen++] = '=';
240 line[linelen] = 0;
241 fputs(line, fp_out);
242 fputc('\n', fp_out);
243 linelen = 0;
244 }
245 snprintf(line + linelen, sizeof(line) - linelen, "=%2.2X", (unsigned char) c);
246 linelen += 3;
247 }
248 else
249 {
250 /* Don't worry about wrapping the line here. That will happen during
251 * the next iteration when I'll also know what the next character is. */
252 line[linelen++] = c;
253 }
254 }
255
256 /* Take care of anything left in the buffer */
257 if (linelen > 0)
258 {
259 if ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t'))
260 {
261 /* take care of trailing whitespace */
262 if (linelen < 74)
263 {
264 snprintf(line + linelen - 1, sizeof(line) - (linelen - 1), "=%2.2X",
265 (unsigned char) line[linelen - 1]);
266 }
267 else
268 {
269 char savechar = line[linelen - 1];
270 line[linelen - 1] = '=';
271 line[linelen] = 0;
272 fputs(line, fp_out);
273 fputc('\n', fp_out);
274 snprintf(line, sizeof(line), "=%2.2X", (unsigned char) savechar);
275 }
276 }
277 else
278 {
279 line[linelen] = 0;
280 }
281 fputs(line, fp_out);
282 }
283}
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
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:587
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ write_as_text_part()

static bool write_as_text_part ( struct Body * b)
static

Should the Body be written as a text MIME part.

Parameters
bEmail to examine
Return values
trueThe Body should be written as text

Definition at line 290 of file body.c.

291{
292 return mutt_is_text_part(b) ||
294}
SecurityFlags mutt_is_application_pgp(const struct Body *b)
Does the message use PGP?
Definition crypt.c:549
bool mutt_is_text_part(const struct Body *b)
Is this part of an email in plain text?
Definition muttlib.c:396
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition lib.h:106
#define WithCrypto
Definition lib.h:132
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_write_mime_body()

int mutt_write_mime_body ( struct Body * b,
FILE * fp,
struct ConfigSubset * sub )

Write a MIME part.

Parameters
bBody to use
fpFile to write to
subConfig Subset
Return values
0Success
-1Failure

Definition at line 304 of file body.c.

305{
306 FILE *fp_in = NULL;
307 struct FgetConv *fc = NULL;
308
309 if (b->type == TYPE_MULTIPART)
310 {
311 /* First, find the boundary to use */
312 const char *p = mutt_param_get(&b->parameter, "boundary");
313 if (!p)
314 {
315 mutt_debug(LL_DEBUG1, "no boundary parameter found\n");
316 mutt_error(_("No boundary parameter found [report this error]"));
317 return -1;
318 }
319 char boundary[128] = { 0 };
320 mutt_str_copy(boundary, p, sizeof(boundary));
321
322 for (struct Body *t = b->parts; t; t = t->next)
323 {
324 fprintf(fp, "\n--%s\n", boundary);
325 if (mutt_write_mime_header(t, fp, sub) == -1)
326 return -1;
327 fputc('\n', fp);
328 if (mutt_write_mime_body(t, fp, sub) == -1)
329 return -1;
330 }
331 fprintf(fp, "\n--%s--\n", boundary);
332 return ferror(fp) ? -1 : 0;
333 }
334
335 /* This is pretty gross, but it's the best solution for now... */
336 if (((WithCrypto & APPLICATION_PGP) != 0) && (b->type == TYPE_APPLICATION) &&
337 mutt_str_equal(b->subtype, "pgp-encrypted") && !b->filename)
338 {
339 fputs("Version: 1\n", fp);
340 return 0;
341 }
342
343 fp_in = mutt_file_fopen(b->filename, "r");
344 if (!fp_in)
345 {
346 mutt_debug(LL_DEBUG1, "%s no longer exists\n", NONULL(b->filename));
347 mutt_error(_("%s no longer exists"), NONULL(b->filename));
348 return -1;
349 }
350
351 if ((b->type == TYPE_TEXT) && (!b->noconv))
352 {
353 char send_charset[128] = { 0 };
354 fc = mutt_ch_fgetconv_open(fp_in, b->charset,
355 mutt_body_get_charset(b, send_charset, sizeof(send_charset)),
357 }
358 else
359 {
360 fc = mutt_ch_fgetconv_open(fp_in, 0, 0, MUTT_ICONV_NONE);
361 }
362
366 else if (b->encoding == ENC_BASE64)
368 else if ((b->type == TYPE_TEXT) && (!b->noconv))
369 encode_8bit(fc, fp);
370 else
373
375 mutt_file_fclose(&fp_in);
376
377 if (SigInt)
378 {
379 SigInt = false;
380 return -1;
381 }
382 return ferror(fp) ? -1 : 0;
383}
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body's character set.
Definition body.c:134
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:224
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
@ 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
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:921
void mutt_ch_fgetconv_close(struct FgetConv **ptr)
Close an fgetconv handle.
Definition charset.c:950
#define MUTT_ICONV_NONE
No flags are set.
Definition charset.h:66
#define _(a)
Definition message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
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:150
static bool write_as_text_part(struct Body *b)
Should the Body be written as a text MIME part.
Definition body.c:290
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:304
static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
Write the data as raw 8-bit data.
Definition body.c:129
int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition header.c:763
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition signal.c:315
#define NONULL(x)
Definition string2.h: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
Cursor for converting a file's encoding.
Definition charset.h:45
FILE * fp
File to read from.
Definition charset.h:46
char * p
Current position in output buffer.
Definition charset.h:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function: