NeoMutt  2023-11-03-85-g512e01
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
rfc2231.c
Go to the documentation of this file.
1
35#include "config.h"
36#include <ctype.h>
37#include <limits.h>
38#include <stdbool.h>
39#include <string.h>
40#include "mutt/lib.h"
41#include "config/lib.h"
42#include "core/lib.h"
43#include "rfc2231.h"
44#include "mime.h"
45#include "parameter.h"
46#include "rfc2047.h"
47
52{
53 char *attribute;
54 char *value;
55 int index;
56 bool encoded;
58};
59
64static void purge_empty_parameters(struct ParameterList *pl)
65{
66 struct Parameter *np = NULL, *tmp = NULL;
67 TAILQ_FOREACH_SAFE(np, pl, entries, tmp)
68 {
69 if (!np->attribute || !np->value)
70 {
71 TAILQ_REMOVE(pl, np, entries);
73 }
74 }
75}
76
84static char *get_charset(char *value, char *charset, size_t chslen)
85{
86 char *t = strchr(value, '\'');
87 if (!t)
88 {
89 charset[0] = '\0';
90 return value;
91 }
92
93 *t = '\0';
94 mutt_str_copy(charset, value, chslen);
95
96 char *u = strchr(t + 1, '\'');
97 if (u)
98 return u + 1;
99 return t + 1;
100}
101
107static void decode_one(char *dest, char *src)
108{
109 char *d = NULL;
110
111 for (d = dest; *src; src++)
112 {
113 if ((src[0] == '%') && isxdigit((unsigned char) src[1]) &&
114 isxdigit((unsigned char) src[2]))
115 {
116 *d++ = (hexval(src[1]) << 4) | hexval(src[2]);
117 src += 2;
118 }
119 else
120 {
121 *d++ = *src;
122 }
123 }
124
125 *d = '\0';
126}
127
132static struct Rfc2231Parameter *parameter_new(void)
133{
134 return mutt_mem_calloc(1, sizeof(struct Rfc2231Parameter));
135}
136
145static void list_insert(struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
146{
147 struct Rfc2231Parameter **last = list;
148 struct Rfc2231Parameter *p = *list;
149
150 while (p)
151 {
152 const int c = mutt_str_cmp(par->attribute, p->attribute);
153 if ((c < 0) || ((c == 0) && (par->index <= p->index)))
154 break;
155
156 last = &p->next;
157 p = p->next;
158 }
159
160 par->next = p;
161 *last = par;
162}
163
168static void parameter_free(struct Rfc2231Parameter **ptr)
169{
170 if (!ptr || !*ptr)
171 return;
172
173 struct Rfc2231Parameter *p = *ptr;
174 FREE(&p->attribute);
175 FREE(&p->value);
176
177 FREE(ptr);
178}
179
185static void join_continuations(struct ParameterList *pl, struct Rfc2231Parameter *par)
186{
187 char attribute[256] = { 0 };
188 char charset[256] = { 0 };
189
190 const char *const c_charset = cc_charset();
191 while (par)
192 {
193 char *value = NULL;
194 size_t l = 0;
195
197
198 const bool encoded = par->encoded;
199 char *valp = NULL;
200 if (encoded)
201 valp = get_charset(par->value, charset, sizeof(charset));
202 else
203 valp = par->value;
204
205 do
206 {
207 if (encoded && par->encoded)
208 decode_one(par->value, valp);
209
210 const size_t vl = strlen(par->value);
211
212 mutt_mem_realloc(&value, l + vl + 1);
213 strcpy(value + l, par->value);
214 l += vl;
215
216 struct Rfc2231Parameter *q = par->next;
217 parameter_free(&par);
218 par = q;
219 if (par)
220 valp = par->value;
221 } while (par && (mutt_str_equal(par->attribute, attribute)));
222
223 if (encoded)
224 {
227 }
228
229 struct Parameter *np = mutt_param_new();
231 np->value = value;
232 TAILQ_INSERT_HEAD(pl, np, entries);
233 }
234}
235
240void rfc2231_decode_parameters(struct ParameterList *pl)
241{
242 if (!pl)
243 return;
244
245 struct Rfc2231Parameter *conthead = NULL;
246 struct Rfc2231Parameter *conttmp = NULL;
247
248 char *s = NULL, *t = NULL;
249 char charset[256] = { 0 };
250
251 bool encoded;
252 int index;
253 bool dirty = false; /* set when we may have created empty parameters. */
254
256
257 struct Parameter *np = NULL, *tmp = NULL;
258 const bool c_rfc2047_parameters = cs_subset_bool(NeoMutt->sub, "rfc2047_parameters");
259 const struct Slist *c_assumed_charset = cc_assumed_charset();
260 const char *c_charset = cc_charset();
261
262 TAILQ_FOREACH_SAFE(np, pl, entries, tmp)
263 {
264 s = strchr(np->attribute, '*');
265 if (!s)
266 {
267 /* Single value, non encoded:
268 * attr=value
269 */
270 /* Using RFC2047 encoding in MIME parameters is explicitly
271 * forbidden by that document. Nevertheless, it's being
272 * generated by some software, including certain Lotus Notes to
273 * Internet Gateways. So we actually decode it. */
274
275 if (c_rfc2047_parameters && np->value && strstr(np->value, "=?"))
276 {
277 rfc2047_decode(&np->value);
278 }
279 else if (!slist_is_empty(c_assumed_charset))
280 {
281 mutt_ch_convert_nonmime_string(c_assumed_charset, c_charset, &np->value);
282 }
283 }
284 else if (s[1] == '\0')
285 {
286 /* Single value with encoding:
287 * attr*=us-ascii''the%20value
288 */
289 s[0] = '\0';
290
291 s = get_charset(np->value, charset, sizeof(charset));
292 decode_one(np->value, s);
293 mutt_ch_convert_string(&np->value, charset, c_charset, MUTT_ICONV_HOOK_FROM);
295 dirty = true;
296 }
297 else
298 {
299 /* A parameter continuation, which may or may not be encoded:
300 * attr*0=value
301 * -or-
302 * attr*0*=us-ascii''the%20value
303 */
304 s[0] = '\0';
305 s++; /* let s point to the first character of index. */
306 for (t = s; (t[0] != '\0') && isdigit((unsigned char) t[0]); t++)
307 ; // do nothing
308
309 encoded = (t[0] == '*');
310 t[0] = '\0';
311
312 /* RFC2231 says that the index starts at 0 and increments by 1,
313 * thus an overflow should never occur in a valid message, thus
314 * the value INT_MAX in case of overflow does not really matter
315 * (the goal is just to avoid undefined behaviour). */
316 if (!mutt_str_atoi_full(s, &index))
317 index = INT_MAX;
318
319 conttmp = parameter_new();
320 conttmp->attribute = np->attribute;
321 conttmp->value = np->value;
322 conttmp->encoded = encoded;
323 conttmp->index = index;
324
325 np->attribute = NULL;
326 np->value = NULL;
327 TAILQ_REMOVE(pl, np, entries);
328 FREE(&np);
329
330 list_insert(&conthead, conttmp);
331 }
332 }
333
334 if (conthead)
335 {
336 join_continuations(pl, conthead);
337 dirty = true;
338 }
339
340 if (dirty)
342}
343
353size_t rfc2231_encode_string(struct ParameterList *head, const char *attribute, char *value)
354{
355 if (!attribute || !value)
356 return 0;
357
358 size_t count = 0;
359 bool encode = false;
360 bool add_quotes = false;
361 bool free_src_value = false;
362 bool split = false;
363 int continuation_number = 0;
364 size_t dest_value_len = 0, max_value_len = 0, cur_value_len = 0;
365 char *cur = NULL, *charset = NULL, *src_value = NULL;
366 struct Parameter *current = NULL;
367
368 struct Buffer *cur_attribute = buf_pool_get();
369 struct Buffer *cur_value = buf_pool_get();
370
371 // Perform charset conversion
372 for (cur = value; *cur; cur++)
373 {
374 if ((*cur < 0x20) || (*cur >= 0x7f))
375 {
376 encode = true;
377 break;
378 }
379 }
380
381 if (encode)
382 {
383 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
384 const char *c_charset = cc_charset();
385 if (c_charset && c_send_charset)
386 {
387 charset = mutt_ch_choose(c_charset, c_send_charset, value,
388 mutt_str_len(value), &src_value, NULL);
389 }
390 if (src_value)
391 free_src_value = true;
392 if (!charset)
393 charset = mutt_str_dup(c_charset ? c_charset : "unknown-8bit");
394 }
395 if (!src_value)
396 src_value = value;
397
398 // Count the size the resultant value will need in total
399 if (encode)
400 dest_value_len = mutt_str_len(charset) + 2; /* charset'' prefix */
401
402 for (cur = src_value; *cur; cur++)
403 {
404 dest_value_len++;
405
406 if (encode)
407 {
408 /* These get converted to %xx so need a total of three chars */
409 if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
410 strchr("*'%", *cur))
411 {
412 dest_value_len += 2;
413 }
414 }
415 else
416 {
417 /* rfc822_cat() will add outer quotes if it finds MimeSpecials. */
418 if (!add_quotes && strchr(MimeSpecials, *cur))
419 add_quotes = true;
420 /* rfc822_cat() will add a backslash if it finds '\' or '"'. */
421 if ((*cur == '\\') || (*cur == '"'))
422 dest_value_len++;
423 }
424 }
425
426 // Determine if need to split into parameter value continuations
427 max_value_len = 78 - // rfc suggested line length
428 1 - // Leading tab on continuation line
429 mutt_str_len(attribute) - // attribute
430 (encode ? 1 : 0) - // '*' encoding marker
431 1 - // '='
432 (add_quotes ? 2 : 0) - // "...."
433 1; // ';'
434
435 if (max_value_len < 30)
436 max_value_len = 30;
437
438 if (dest_value_len > max_value_len)
439 {
440 split = true;
441 max_value_len -= 4; /* '*n' continuation number and extra encoding
442 * space to keep loop below simpler */
443 }
444
445 // Generate list of parameter continuations
446 cur = src_value;
447 if (encode)
448 {
449 buf_printf(cur_value, "%s''", charset);
450 cur_value_len = buf_len(cur_value);
451 }
452
453 while (*cur)
454 {
455 current = mutt_param_new();
456 TAILQ_INSERT_TAIL(head, current, entries);
457 count++;
458
459 buf_strcpy(cur_attribute, attribute);
460 if (split)
461 buf_add_printf(cur_attribute, "*%d", continuation_number++);
462 if (encode)
463 buf_addch(cur_attribute, '*');
464
465 while (*cur && (!split || (cur_value_len < max_value_len)))
466 {
467 if (encode)
468 {
469 if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
470 strchr("*'%", *cur))
471 {
472 buf_add_printf(cur_value, "%%%02X", (unsigned char) *cur);
473 cur_value_len += 3;
474 }
475 else
476 {
477 buf_addch(cur_value, *cur);
478 cur_value_len++;
479 }
480 }
481 else
482 {
483 buf_addch(cur_value, *cur);
484 cur_value_len++;
485 if ((*cur == '\\') || (*cur == '"'))
486 cur_value_len++;
487 }
488
489 cur++;
490 }
491
492 current->attribute = buf_strdup(cur_attribute);
493 current->value = buf_strdup(cur_value);
494
495 buf_reset(cur_value);
496 cur_value_len = 0;
497 }
498
499 buf_pool_release(&cur_attribute);
500 buf_pool_release(&cur_value);
501
502 FREE(&charset);
503 if (free_src_value)
504 FREE(&src_value);
505
506 return count;
507}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:216
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:243
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:115
const struct Slist * cc_assumed_charset(void)
Get the cached value of $assumed_charset.
Definition: config_cache.c:100
Convenience wrapper for the core headers.
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition: mbyte.c:419
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:45
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
Constants and macros for managing MIME encoding.
#define hexval(ch)
Definition: mime.h:80
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:1106
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:328
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:826
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:72
Convenience wrapper for the library headers.
bool slist_is_empty(const struct Slist *list)
Is the slist empty?
Definition: slist.c:178
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:471
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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
void mutt_param_free_one(struct Parameter **p)
Free a Parameter.
Definition: parameter.c:48
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
Store attributes associated with a MIME part.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:809
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:796
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:659
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:424
RFC2047 MIME extensions encoding / decoding routines.
static void purge_empty_parameters(struct ParameterList *pl)
Remove any ill-formed Parameters from a list.
Definition: rfc2231.c:64
static void join_continuations(struct ParameterList *pl, struct Rfc2231Parameter *par)
Process continuation parameters.
Definition: rfc2231.c:185
static char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition: rfc2231.c:84
static void parameter_free(struct Rfc2231Parameter **ptr)
Free an Rfc2231Parameter.
Definition: rfc2231.c:168
static void list_insert(struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
Insert parameter into an ordered list.
Definition: rfc2231.c:145
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition: rfc2231.c:240
static struct Rfc2231Parameter * parameter_new(void)
Create a new Rfc2231Parameter.
Definition: rfc2231.c:132
size_t rfc2231_encode_string(struct ParameterList *head, const char *attribute, char *value)
Encode a string to be suitable for an RFC2231 header.
Definition: rfc2231.c:353
static void decode_one(char *dest, char *src)
Decode one percent-encoded character.
Definition: rfc2231.c:107
RFC2231 MIME Charset routines.
String manipulation buffer.
Definition: buffer.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
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
MIME section parameter.
Definition: rfc2231.c:52
bool encoded
Is the value encoded?
Definition: rfc2231.c:56
int index
Index number in the list.
Definition: rfc2231.c:55
char * value
Attribute value.
Definition: rfc2231.c:54
struct Rfc2231Parameter * next
Linked list.
Definition: rfc2231.c:57
char * attribute
Attribute name.
Definition: rfc2231.c:53
String list.
Definition: slist.h:47
struct ListHead head
List containing values.
Definition: slist.h:48
size_t count
Number of values in list.
Definition: slist.h:49