NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
body.c File Reference

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

#include "config.h"
#include <stdbool.h>
#include <string.h>
#include "mutt/lib.h"
#include "email/lib.h"
#include "body.h"
#include "lib.h"
#include "ncrypt/lib.h"
#include "globals.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 int b64_init (struct B64Context *bctx)
 Set up the base64 conversion. More...
 
static void b64_flush (struct B64Context *bctx, FILE *fp_out)
 Save the bytes to the file. More...
 
static void b64_putc (struct B64Context *bctx, char c, FILE *fp_out)
 Base64-encode one character. More...
 
static void encode_base64 (struct FgetConv *fc, FILE *fp_out, int istext)
 Base64-encode some data. More...
 
static void encode_8bit (struct FgetConv *fc, FILE *fp_out)
 Write the data as raw 8-bit data. More...
 
static void encode_quoted (struct FgetConv *fc, FILE *fp_out, bool istext)
 Encode text as quoted printable. More...
 
static bool write_as_text_part (struct Body *b)
 Should the Body be written as a text MIME part. More...
 
int mutt_write_mime_body (struct Body *a, FILE *fp, struct ConfigSubset *sub)
 Write a MIME part. More...
 

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_init()

static int b64_init ( struct B64Context bctx)
static

Set up the base64 conversion.

Parameters
bctxCursor for the base64 conversion
Return values
0Always

Definition at line 55 of file body.c.

56{
57 memset(bctx->buffer, '\0', sizeof(bctx->buffer));
58 bctx->size = 0;
59 bctx->linelen = 0;
60
61 return 0;
62}
short linelen
Definition: body.c:47
char buffer[3]
Definition: body.c:45
short size
Definition: body.c:46
+ Here is the caller graph for this function:

◆ 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 69 of file body.c.

70{
71 /* for some reasons, mutt_b64_encode expects the
72 * output buffer to be larger than 10B */
73 char encoded[11] = { 0 };
74 size_t rc;
75
76 if (bctx->size == 0)
77 return;
78
79 if (bctx->linelen >= 72)
80 {
81 fputc('\n', fp_out);
82 bctx->linelen = 0;
83 }
84
85 /* rc should always be equal to 4 here, because bctx->size
86 * is a value between 1 and 3 (included), but let's not hardcode it
87 * and prefer the return value of the function */
88 rc = mutt_b64_encode(bctx->buffer, bctx->size, encoded, sizeof(encoded));
89 for (size_t i = 0; i < rc; i++)
90 {
91 fputc(encoded[i], fp_out);
92 bctx->linelen++;
93 }
94
95 bctx->size = 0;
96}
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
+ 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 104 of file body.c.

105{
106 if (bctx->size == 3)
107 b64_flush(bctx, fp_out);
108
109 bctx->buffer[bctx->size++] = c;
110}
static void b64_flush(struct B64Context *bctx, FILE *fp_out)
Save the bytes to the file.
Definition: body.c:69
+ 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 118 of file body.c.

119{
120 struct B64Context bctx;
121 int ch, ch1 = EOF;
122
123 b64_init(&bctx);
124
125 while ((ch = mutt_ch_fgetconv(fc)) != EOF)
126 {
127 if (SigInt)
128 {
129 SigInt = false;
130 return;
131 }
132 if (istext && (ch == '\n') && (ch1 != '\r'))
133 b64_putc(&bctx, '\r', fp_out);
134 b64_putc(&bctx, ch, fp_out);
135 ch1 = ch;
136 }
137 b64_flush(&bctx, fp_out);
138 fputc('\n', fp_out);
139}
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:58
int mutt_ch_fgetconv(struct FgetConv *fc)
Convert a file's character set.
Definition: charset.c:907
static int b64_init(struct B64Context *bctx)
Set up the base64 conversion.
Definition: body.c:55
static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
Base64-encode one character.
Definition: body.c:104
Cursor for the Base64 conversion.
Definition: body.c:44
+ 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 146 of file body.c.

147{
148 int ch;
149
150 while ((ch = mutt_ch_fgetconv(fc)) != EOF)
151 {
152 if (SigInt)
153 {
154 SigInt = false;
155 return;
156 }
157 fputc(ch, fp_out);
158 }
159}
+ 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 167 of file body.c.

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

301{
302 return mutt_is_text_part(b) ||
304}
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:542
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define WithCrypto
Definition: lib.h:116
+ 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 a,
FILE *  fp,
struct ConfigSubset sub 
)

Write a MIME part.

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

Definition at line 314 of file body.c.

315{
316 FILE *fp_in = NULL;
317 struct FgetConv *fc = NULL;
318
319 if (a->type == TYPE_MULTIPART)
320 {
321 /* First, find the boundary to use */
322 const char *p = mutt_param_get(&a->parameter, "boundary");
323 if (!p)
324 {
325 mutt_debug(LL_DEBUG1, "no boundary parameter found\n");
326 mutt_error(_("No boundary parameter found [report this error]"));
327 return -1;
328 }
329 char boundary[128] = { 0 };
330 mutt_str_copy(boundary, p, sizeof(boundary));
331
332 for (struct Body *t = a->parts; t; t = t->next)
333 {
334 fprintf(fp, "\n--%s\n", boundary);
335 if (mutt_write_mime_header(t, fp, sub) == -1)
336 return -1;
337 fputc('\n', fp);
338 if (mutt_write_mime_body(t, fp, sub) == -1)
339 return -1;
340 }
341 fprintf(fp, "\n--%s--\n", boundary);
342 return ferror(fp) ? -1 : 0;
343 }
344
345 /* This is pretty gross, but it's the best solution for now... */
346 if (((WithCrypto & APPLICATION_PGP) != 0) && (a->type == TYPE_APPLICATION) &&
347 mutt_str_equal(a->subtype, "pgp-encrypted") && !a->filename)
348 {
349 fputs("Version: 1\n", fp);
350 return 0;
351 }
352
353 fp_in = fopen(a->filename, "r");
354 if (!fp_in)
355 {
356 mutt_debug(LL_DEBUG1, "%s no longer exists\n", a->filename);
357 mutt_error(_("%s no longer exists"), a->filename);
358 return -1;
359 }
360
361 if ((a->type == TYPE_TEXT) && (!a->noconv))
362 {
363 char send_charset[128] = { 0 };
364 fc = mutt_ch_fgetconv_open(fp_in, a->charset,
365 mutt_body_get_charset(a, send_charset, sizeof(send_charset)),
367 }
368 else
370
374 else if (a->encoding == ENC_BASE64)
376 else if ((a->type == TYPE_TEXT) && (!a->noconv))
377 encode_8bit(fc, fp);
378 else
381
383 mutt_file_fclose(&fp_in);
384
385 if (SigInt)
386 {
387 SigInt = false;
388 return -1;
389 }
390 return ferror(fp) ? -1 : 0;
391}
char * mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
Get a body's character set.
Definition: body.c:131
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:259
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:151
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:748
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
@ 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
void mutt_ch_fgetconv_close(struct FgetConv **fc)
Close an fgetconv handle.
Definition: charset.c:887
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:857
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
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:167
static bool write_as_text_part(struct Body *b)
Should the Body be written as a text MIME part.
Definition: body.c:300
static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
Base64-encode some data.
Definition: body.c:118
int mutt_write_mime_body(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:314
static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
Write the data as raw 8-bit data.
Definition: body.c:146
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
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
Cursor for converting a file's encoding.
Definition: charset.h:41
FILE * fp
Definition: charset.h:42
char * p
Definition: charset.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function: