NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
rfc2231.c File Reference

RFC2231 MIME Charset routines. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "rfc2231.h"
#include "mime.h"
#include "parameter.h"
#include "rfc2047.h"
Include dependency graph for rfc2231.c:

Go to the source code of this file.

Data Structures

struct  Rfc2231Parameter
 MIME section parameter. More...

Functions

static void purge_empty_parameters (struct ParameterList *pl)
 Remove any ill-formed Parameters from a list.
static char * get_charset (char *value, char *charset, size_t chslen)
 Get the charset from an RFC2231 header.
static void decode_one (char *dest, char *src)
 Decode one percent-encoded character.
static struct Rfc2231Parameterparameter_new (void)
 Create a new Rfc2231Parameter.
static void list_insert (struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
 Insert parameter into an ordered list.
static void parameter_free (struct Rfc2231Parameter **ptr)
 Free an Rfc2231Parameter.
static void join_continuations (struct ParameterList *pl, struct Rfc2231Parameter *par)
 Process continuation parameters.
void rfc2231_decode_parameters (struct ParameterList *pl)
 Decode a Parameter list.
size_t rfc2231_encode_string (struct ParameterList *head, const char *attribute, char *value)
 Encode a string to be suitable for an RFC2231 header.

Detailed Description

RFC2231 MIME Charset routines.

Authors
  • Richard Russon
  • Federico Kircheis
  • Pietro Cerutti
  • Thomas Klausner

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 rfc2231.c.

Function Documentation

◆ purge_empty_parameters()

void purge_empty_parameters ( struct ParameterList * pl)
static

Remove any ill-formed Parameters from a list.

Parameters
plParameter List to check

Definition at line 66 of file rfc2231.c.

67{
68 struct Parameter *np = NULL;
69 struct Parameter *tmp = NULL;
70 TAILQ_FOREACH_SAFE(np, pl, entries, tmp)
71 {
72 if (!np->attribute || !np->value)
73 {
74 TAILQ_REMOVE(pl, np, entries);
76 }
77 }
78}
void mutt_param_free_one(struct Parameter **p)
Free a Parameter.
Definition parameter.c:49
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
Attribute associated with a MIME part.
Definition parameter.h:33
char * attribute
Parameter name.
Definition parameter.h:34
char * value
Parameter value.
Definition parameter.h:35
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_charset()

char * get_charset ( char * value,
char * charset,
size_t chslen )
static

Get the charset from an RFC2231 header.

Parameters
valueHeader string
charsetBuffer for the result
chslenLength of buffer
Return values
ptrFirst character after charset

Definition at line 87 of file rfc2231.c.

88{
89 char *t = strchr(value, '\'');
90 if (!t)
91 {
92 charset[0] = '\0';
93 return value;
94 }
95
96 *t = '\0';
97 mutt_str_copy(charset, value, chslen);
98
99 char *u = strchr(t + 1, '\'');
100 if (u)
101 return u + 1;
102 return t + 1;
103}
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:

◆ decode_one()

void decode_one ( char * dest,
char * src )
static

Decode one percent-encoded character.

Parameters
[out]destWhere to save the result
[in]srcSource string

Definition at line 110 of file rfc2231.c.

111{
112 char *d = NULL;
113
114 for (d = dest; *src; src++)
115 {
116 if ((src[0] == '%') && mutt_isxdigit(src[1]) && mutt_isxdigit(src[2]))
117 {
118 *d++ = (hexval(src[1]) << 4) | hexval(src[2]);
119 src += 2;
120 }
121 else
122 {
123 *d++ = *src;
124 }
125 }
126
127 *d = '\0';
128}
bool mutt_isxdigit(int arg)
Wrapper for isxdigit(3).
Definition ctype.c:111
#define hexval(ch)
Convert hexadecimal character to its integer value.
Definition mime.h:81
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parameter_new()

struct Rfc2231Parameter * parameter_new ( void )
static

Create a new Rfc2231Parameter.

Return values
ptrNewly allocated Rfc2231Parameter

Definition at line 134 of file rfc2231.c.

135{
136 return MUTT_MEM_CALLOC(1, struct Rfc2231Parameter);
137}
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
MIME section parameter.
Definition rfc2231.c:54
Here is the caller graph for this function:

◆ list_insert()

void list_insert ( struct Rfc2231Parameter ** list,
struct Rfc2231Parameter * par )
static

Insert parameter into an ordered list.

Parameters
[out]listList to insert into
[in]parParameter to insert

Primary sorting key: attribute Secondary sorting key: index

Definition at line 147 of file rfc2231.c.

148{
149 struct Rfc2231Parameter **last = list;
150 struct Rfc2231Parameter *p = *list;
151
152 while (p)
153 {
154 const int c = mutt_str_cmp(par->attribute, p->attribute);
155 if ((c < 0) || ((c == 0) && (par->index <= p->index)))
156 break;
157
158 last = &p->next;
159 p = p->next;
160 }
161
162 par->next = p;
163 *last = par;
164}
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition string.c:403
int index
Index number in the list.
Definition rfc2231.c:57
struct Rfc2231Parameter * next
Linked list.
Definition rfc2231.c:59
char * attribute
Attribute name.
Definition rfc2231.c:55
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parameter_free()

void parameter_free ( struct Rfc2231Parameter ** ptr)
static

Free an Rfc2231Parameter.

Parameters
[out]ptrRfc2231Parameter to free

Definition at line 170 of file rfc2231.c.

171{
172 if (!ptr || !*ptr)
173 return;
174
175 struct Rfc2231Parameter *p = *ptr;
176 FREE(&p->attribute);
177 FREE(&p->value);
178
179 FREE(ptr);
180}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * value
Attribute value.
Definition rfc2231.c:56
Here is the caller graph for this function:

◆ join_continuations()

void join_continuations ( struct ParameterList * pl,
struct Rfc2231Parameter * par )
static

Process continuation parameters.

Parameters
plParameter List for the results
parContinuation Parameter

Definition at line 187 of file rfc2231.c.

188{
189 char attribute[256] = { 0 };
190 char charset[256] = { 0 };
191
192 const char *const c_charset = cc_charset();
193 while (par)
194 {
195 char *value = NULL;
196 size_t l = 0;
197
198 mutt_str_copy(attribute, par->attribute, sizeof(attribute));
199
200 const bool encoded = par->encoded;
201 char *valp = NULL;
202 if (encoded)
203 valp = get_charset(par->value, charset, sizeof(charset));
204 else
205 valp = par->value;
206
207 do
208 {
209 if (encoded && par->encoded)
210 decode_one(par->value, valp);
211
212 const size_t vl = strlen(par->value);
213
214 MUTT_MEM_REALLOC(&value, l + vl + 1, char);
215 strcpy(value + l, par->value);
216 l += vl;
217
218 struct Rfc2231Parameter *q = par->next;
219 parameter_free(&par);
220 par = q;
221 if (par)
222 valp = par->value;
223 } while (par && (mutt_str_equal(par->attribute, attribute)));
224
225 if (encoded)
226 {
229 }
230
231 struct Parameter *np = mutt_param_new();
233 np->value = value;
234 TAILQ_INSERT_HEAD(pl, np, entries);
235 }
236}
const char * cc_charset(void)
Get the cached value of $charset.
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition mbyte.c:424
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition charset.c:819
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition charset.h:67
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition parameter.c:40
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition queue.h:853
static char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition rfc2231.c:87
static void parameter_free(struct Rfc2231Parameter **ptr)
Free an Rfc2231Parameter.
Definition rfc2231.c:170
static void decode_one(char *dest, char *src)
Decode one percent-encoded character.
Definition rfc2231.c:110
bool encoded
Is the value encoded?
Definition rfc2231.c:58
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2231_decode_parameters()

void rfc2231_decode_parameters ( struct ParameterList * pl)

Decode a Parameter list.

Parameters
plList to decode

Definition at line 242 of file rfc2231.c.

243{
244 if (!pl)
245 return;
246
247 struct Rfc2231Parameter *conthead = NULL;
248 struct Rfc2231Parameter *conttmp = NULL;
249
250 char *s = NULL;
251 char *t = NULL;
252 char charset[256] = { 0 };
253
254 bool encoded;
255 int index = 0;
256 bool dirty = false; /* set when we may have created empty parameters. */
257
259
260 struct Parameter *np = NULL;
261 struct Parameter *tmp = NULL;
262 const bool c_rfc2047_parameters = cs_subset_bool(NeoMutt->sub, "rfc2047_parameters");
263 const struct Slist *c_assumed_charset = cc_assumed_charset();
264 const char *c_charset = cc_charset();
265
266 TAILQ_FOREACH_SAFE(np, pl, entries, tmp)
267 {
268 s = strchr(np->attribute, '*');
269 if (!s)
270 {
271 /* Single value, non encoded:
272 * attr=value
273 */
274 /* Using RFC2047 encoding in MIME parameters is explicitly
275 * forbidden by that document. Nevertheless, it's being
276 * generated by some software, including certain Lotus Notes to
277 * Internet Gateways. So we actually decode it. */
278
279 if (c_rfc2047_parameters && np->value && strstr(np->value, "=?"))
280 {
281 rfc2047_decode(&np->value);
282 }
283 else if (!slist_is_empty(c_assumed_charset))
284 {
285 mutt_ch_convert_nonmime_string(c_assumed_charset, c_charset, &np->value);
286 }
287 }
288 else if (s[1] == '\0')
289 {
290 /* Single value with encoding:
291 * attr*=us-ascii''the%20value
292 */
293 s[0] = '\0';
294
295 s = get_charset(np->value, charset, sizeof(charset));
296 decode_one(np->value, s);
297 mutt_ch_convert_string(&np->value, charset, c_charset, MUTT_ICONV_HOOK_FROM);
299 dirty = true;
300 }
301 else
302 {
303 /* A parameter continuation, which may or may not be encoded:
304 * attr*0=value
305 * -or-
306 * attr*0*=us-ascii''the%20value
307 */
308 s[0] = '\0';
309 s++; /* let s point to the first character of index. */
310 for (t = s; (t[0] != '\0') && mutt_isdigit(t[0]); t++)
311 ; // do nothing
312
313 encoded = (t[0] == '*');
314 t[0] = '\0';
315
316 /* RFC2231 says that the index starts at 0 and increments by 1,
317 * thus an overflow should never occur in a valid message, thus
318 * the value INT_MAX in case of overflow does not really matter
319 * (the goal is just to avoid undefined behaviour). */
320 if (!mutt_str_atoi_full(s, &index))
321 index = INT_MAX;
322
323 conttmp = parameter_new();
324 conttmp->attribute = np->attribute;
325 conttmp->value = np->value;
326 conttmp->encoded = encoded;
327 conttmp->index = index;
328
329 np->attribute = NULL;
330 np->value = NULL;
331 TAILQ_REMOVE(pl, np, entries);
332 FREE(&np);
333
334 list_insert(&conthead, conttmp);
335 }
336 }
337
338 if (conthead)
339 {
340 join_continuations(pl, conthead);
341 dirty = true;
342 }
343
344 if (dirty)
346}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
bool mutt_isdigit(int arg)
Wrapper for isdigit(3).
Definition ctype.c:66
int mutt_ch_convert_nonmime_string(const struct Slist *const assumed_charset, const char *charset, char **ps)
Try to convert a string using a list of character sets.
Definition charset.c:318
bool slist_is_empty(const struct Slist *list)
Is the slist empty?
Definition slist.c:140
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition rfc2047.c:679
static void purge_empty_parameters(struct ParameterList *pl)
Remove any ill-formed Parameters from a list.
Definition rfc2231.c:66
static void join_continuations(struct ParameterList *pl, struct Rfc2231Parameter *par)
Process continuation parameters.
Definition rfc2231.c:187
static void list_insert(struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
Insert parameter into an ordered list.
Definition rfc2231.c:147
static struct Rfc2231Parameter * parameter_new(void)
Create a new Rfc2231Parameter.
Definition rfc2231.c:134
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
String list.
Definition slist.h:37
Here is the call graph for this function:
Here is the caller graph for this function:

◆ rfc2231_encode_string()

size_t rfc2231_encode_string ( struct ParameterList * head,
const char * attribute,
char * value )

Encode a string to be suitable for an RFC2231 header.

Parameters
headString encoded as a ParameterList, empty on error
attributeName of attribute to encode
valueValue of attribute to encode
Return values
numNumber of Parameters in the List

If the value is large, the list will contain continuation lines.

Definition at line 357 of file rfc2231.c.

358{
359 if (!attribute || !value)
360 return 0;
361
362 size_t count = 0;
363 bool encode = false;
364 bool add_quotes = false;
365 bool free_src_value = false;
366 bool split = false;
367 int continuation_number = 0;
368 size_t dest_value_len = 0;
369 size_t max_value_len = 0;
370 size_t cur_value_len = 0;
371 char *cur = NULL;
372 char *charset = NULL;
373 char *src_value = NULL;
374 struct Parameter *current = NULL;
375
376 struct Buffer *cur_attribute = buf_pool_get();
377 struct Buffer *cur_value = buf_pool_get();
378
379 // Perform charset conversion
380 for (cur = value; *cur; cur++)
381 {
382 if ((*cur < 0x20) || (*cur >= 0x7f))
383 {
384 encode = true;
385 break;
386 }
387 }
388
389 if (encode)
390 {
391 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
392 const char *c_charset = cc_charset();
393 if (c_charset && c_send_charset)
394 {
395 charset = mutt_ch_choose(c_charset, c_send_charset, value,
396 mutt_str_len(value), &src_value, NULL);
397 }
398 if (src_value)
399 free_src_value = true;
400 if (!charset)
401 charset = mutt_str_dup(c_charset ? c_charset : "unknown-8bit");
402 }
403 if (!src_value)
404 src_value = value;
405
406 // Count the size the resultant value will need in total
407 if (encode)
408 dest_value_len = mutt_str_len(charset) + 2; /* charset'' prefix */
409
410 for (cur = src_value; *cur; cur++)
411 {
412 dest_value_len++;
413
414 if (encode)
415 {
416 /* These get converted to %xx so need a total of three chars */
417 if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
418 strchr("*'%", *cur))
419 {
420 dest_value_len += 2;
421 }
422 }
423 else
424 {
425 /* rfc822_cat() will add outer quotes if it finds MimeSpecials. */
426 if (!add_quotes && strchr(MimeSpecials, *cur))
427 add_quotes = true;
428 /* rfc822_cat() will add a backslash if it finds '\' or '"'. */
429 if ((*cur == '\\') || (*cur == '"'))
430 dest_value_len++;
431 }
432 }
433
434 // Determine if need to split into parameter value continuations
435 max_value_len = 78 - // rfc suggested line length
436 1 - // Leading tab on continuation line
437 mutt_str_len(attribute) - // attribute
438 (encode ? 1 : 0) - // '*' encoding marker
439 1 - // '='
440 (add_quotes ? 2 : 0) - // "...."
441 1; // ';'
442
443 if (max_value_len < 30)
444 max_value_len = 30;
445
446 if (dest_value_len > max_value_len)
447 {
448 split = true;
449 max_value_len -= 4; /* '*n' continuation number and extra encoding
450 * space to keep loop below simpler */
451 }
452
453 // Generate list of parameter continuations
454 cur = src_value;
455 if (encode)
456 {
457 buf_printf(cur_value, "%s''", charset);
458 cur_value_len = buf_len(cur_value);
459 }
460
461 while (*cur)
462 {
463 current = mutt_param_new();
464 TAILQ_INSERT_TAIL(head, current, entries);
465 count++;
466
467 buf_strcpy(cur_attribute, attribute);
468 if (split)
469 buf_add_printf(cur_attribute, "*%d", continuation_number++);
470 if (encode)
471 buf_addch(cur_attribute, '*');
472
473 while (*cur && (!split || (cur_value_len < max_value_len)))
474 {
475 if (encode)
476 {
477 if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
478 strchr("*'%", *cur))
479 {
480 buf_add_printf(cur_value, "%%%02X", (unsigned char) *cur);
481 cur_value_len += 3;
482 }
483 else
484 {
485 buf_addch(cur_value, *cur);
486 cur_value_len++;
487 }
488 }
489 else
490 {
491 buf_addch(cur_value, *cur);
492 cur_value_len++;
493 if ((*cur == '\\') || (*cur == '"'))
494 cur_value_len++;
495 }
496
497 cur++;
498 }
499
500 current->attribute = buf_strdup(cur_attribute);
501 current->value = buf_strdup(cur_value);
502
503 buf_reset(cur_value);
504 cur_value_len = 0;
505 }
506
507 buf_pool_release(&cur_attribute);
508 buf_pool_release(&cur_value);
509
510 FREE(&charset);
511 if (free_src_value)
512 FREE(&src_value);
513
514 return count;
515}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:211
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:497
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition helpers.c:242
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition mime.c:69
char * mutt_ch_choose(const char *fromcode, const struct Slist *charsets, const char *u, size_t ulen, char **d, size_t *dlen)
Figure the best charset to encode a string.
Definition charset.c:1096
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:866
static int encode(const char *d, size_t dlen, int col, const char *fromcode, const struct Slist *charsets, char **e, size_t *elen, const char *specials)
RFC2047-encode a string.
Definition rfc2047.c:438
String manipulation buffer.
Definition buffer.h:36
struct ListHead head
List containing values.
Definition slist.h:38
size_t count
Number of values in list.
Definition slist.h:39
Here is the call graph for this function:
Here is the caller graph for this function: