NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
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 
64 static 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 
84 static 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 
107 static 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  *d++ = *src;
121  }
122 
123  *d = '\0';
124 }
125 
130 static struct Rfc2231Parameter *parameter_new(void)
131 {
132  return mutt_mem_calloc(1, sizeof(struct Rfc2231Parameter));
133 }
134 
143 static void list_insert(struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
144 {
145  struct Rfc2231Parameter **last = list;
146  struct Rfc2231Parameter *p = *list;
147 
148  while (p)
149  {
150  const int c = strcmp(par->attribute, p->attribute);
151  if ((c < 0) || ((c == 0) && (par->index <= p->index)))
152  break;
153 
154  last = &p->next;
155  p = p->next;
156  }
157 
158  par->next = p;
159  *last = par;
160 }
161 
166 static void parameter_free(struct Rfc2231Parameter **p)
167 {
168  if (!p || !*p)
169  return;
170 
171  FREE(&(*p)->attribute);
172  FREE(&(*p)->value);
173  FREE(p);
174 }
175 
181 static void join_continuations(struct ParameterList *pl, struct Rfc2231Parameter *par)
182 {
183  char attribute[256];
184  char charset[256];
185 
186  while (par)
187  {
188  char *value = NULL;
189  size_t l = 0;
190 
191  mutt_str_copy(attribute, par->attribute, sizeof(attribute));
192 
193  const bool encoded = par->encoded;
194  char *valp = NULL;
195  if (encoded)
196  valp = get_charset(par->value, charset, sizeof(charset));
197  else
198  valp = par->value;
199 
200  do
201  {
202  if (encoded && par->encoded)
203  decode_one(par->value, valp);
204 
205  const size_t vl = strlen(par->value);
206 
207  mutt_mem_realloc(&value, l + vl + 1);
208  strcpy(value + l, par->value);
209  l += vl;
210 
211  struct Rfc2231Parameter *q = par->next;
212  parameter_free(&par);
213  par = q;
214  if (par)
215  valp = par->value;
216  } while (par && (strcmp(par->attribute, attribute) == 0));
217 
218  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
219  if (encoded)
220  mutt_ch_convert_string(&value, charset, c_charset, MUTT_ICONV_HOOK_FROM);
221 
222  struct Parameter *np = mutt_param_new();
223  np->attribute = mutt_str_dup(attribute);
224  np->value = value;
225  TAILQ_INSERT_HEAD(pl, np, entries);
226  }
227 }
228 
233 void rfc2231_decode_parameters(struct ParameterList *pl)
234 {
235  if (!pl)
236  return;
237 
238  struct Rfc2231Parameter *conthead = NULL;
239  struct Rfc2231Parameter *conttmp = NULL;
240 
241  char *s = NULL, *t = NULL;
242  char charset[256];
243 
244  bool encoded;
245  int index;
246  bool dirty = false; /* set when we may have created empty parameters. */
247 
249 
250  struct Parameter *np = NULL, *tmp = NULL;
251  TAILQ_FOREACH_SAFE(np, pl, entries, tmp)
252  {
253  s = strchr(np->attribute, '*');
254  if (!s)
255  {
256  /* Using RFC2047 encoding in MIME parameters is explicitly
257  * forbidden by that document. Nevertheless, it's being
258  * generated by some software, including certain Lotus Notes to
259  * Internet Gateways. So we actually decode it. */
260 
261  const bool c_rfc2047_parameters =
262  cs_subset_bool(NeoMutt->sub, "rfc2047_parameters");
263  const char *const c_assumed_charset =
264  cs_subset_string(NeoMutt->sub, "assumed_charset");
265  if (c_rfc2047_parameters && np->value && strstr(np->value, "=?"))
266  rfc2047_decode(&np->value);
267  else if (c_assumed_charset)
269  }
270  else if (s[1] == '\0')
271  {
272  s[0] = '\0';
273 
274  s = get_charset(np->value, charset, sizeof(charset));
275  decode_one(np->value, s);
276  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
277  mutt_ch_convert_string(&np->value, charset, c_charset, MUTT_ICONV_HOOK_FROM);
279  dirty = true;
280  }
281  else
282  {
283  s[0] = '\0';
284  s++; /* let s point to the first character of index. */
285  for (t = s; (t[0] != '\0') && isdigit((unsigned char) t[0]); t++)
286  ; // do nothing
287 
288  encoded = (t[0] == '*');
289  t[0] = '\0';
290 
291  /* RFC2231 says that the index starts at 0 and increments by 1,
292  * thus an overflow should never occur in a valid message, thus
293  * the value INT_MAX in case of overflow does not really matter
294  * (the goal is just to avoid undefined behaviour). */
295  if (mutt_str_atoi(s, &index) != 0)
296  index = INT_MAX;
297 
298  conttmp = parameter_new();
299  conttmp->attribute = np->attribute;
300  conttmp->value = np->value;
301  conttmp->encoded = encoded;
302  conttmp->index = index;
303 
304  np->attribute = NULL;
305  np->value = NULL;
306  TAILQ_REMOVE(pl, np, entries);
307  FREE(&np);
308 
309  list_insert(&conthead, conttmp);
310  }
311  }
312 
313  if (conthead)
314  {
315  join_continuations(pl, conthead);
316  dirty = true;
317  }
318 
319  if (dirty)
321 }
322 
332 size_t rfc2231_encode_string(struct ParameterList *head, const char *attribute, char *value)
333 {
334  if (!attribute || !value)
335  return 0;
336 
337  size_t count = 0;
338  bool encode = false;
339  bool add_quotes = false;
340  bool free_src_value = false;
341  bool split = false;
342  int continuation_number = 0;
343  size_t dest_value_len = 0, max_value_len = 0, cur_value_len = 0;
344  char *cur = NULL, *charset = NULL, *src_value = NULL;
345  struct Parameter *current = NULL;
346 
347  struct Buffer *cur_attribute = mutt_buffer_pool_get();
348  struct Buffer *cur_value = mutt_buffer_pool_get();
349 
350  // Perform charset conversion
351  for (cur = value; *cur; cur++)
352  {
353  if ((*cur < 0x20) || (*cur >= 0x7f))
354  {
355  encode = true;
356  break;
357  }
358  }
359 
360  if (encode)
361  {
362  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
363  const char *const c_send_charset =
364  cs_subset_string(NeoMutt->sub, "send_charset");
365  if (c_charset && c_send_charset)
366  {
367  charset = mutt_ch_choose(c_charset, c_send_charset, value,
368  mutt_str_len(value), &src_value, NULL);
369  }
370  if (src_value)
371  free_src_value = true;
372  if (!charset)
373  charset = mutt_str_dup(c_charset ? c_charset : "unknown-8bit");
374  }
375  if (!src_value)
376  src_value = value;
377 
378  // Count the size the resultant value will need in total
379  if (encode)
380  dest_value_len = mutt_str_len(charset) + 2; /* charset'' prefix */
381 
382  for (cur = src_value; *cur; cur++)
383  {
384  dest_value_len++;
385 
386  if (encode)
387  {
388  /* These get converted to %xx so need a total of three chars */
389  if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
390  strchr("*'%", *cur))
391  {
392  dest_value_len += 2;
393  }
394  }
395  else
396  {
397  /* rfc822_cat() will add outer quotes if it finds MimeSpecials. */
398  if (!add_quotes && strchr(MimeSpecials, *cur))
399  add_quotes = true;
400  /* rfc822_cat() will add a backslash if it finds '\' or '"'. */
401  if ((*cur == '\\') || (*cur == '"'))
402  dest_value_len++;
403  }
404  }
405 
406  // Determine if need to split into parameter value continuations
407  max_value_len = 78 - // rfc suggested line length
408  1 - // Leading tab on continuation line
409  mutt_str_len(attribute) - // attribute
410  (encode ? 1 : 0) - // '*' encoding marker
411  1 - // '='
412  (add_quotes ? 2 : 0) - // "...."
413  1; // ';'
414 
415  if (max_value_len < 30)
416  max_value_len = 30;
417 
418  if (dest_value_len > max_value_len)
419  {
420  split = true;
421  max_value_len -= 4; /* '*n' continuation number and extra encoding
422  * space to keep loop below simpler */
423  }
424 
425  // Generate list of parameter continuations
426  cur = src_value;
427  if (encode)
428  {
429  mutt_buffer_printf(cur_value, "%s''", charset);
430  cur_value_len = mutt_buffer_len(cur_value);
431  }
432 
433  while (*cur)
434  {
435  current = mutt_param_new();
436  TAILQ_INSERT_TAIL(head, current, entries);
437  count++;
438 
439  mutt_buffer_strcpy(cur_attribute, attribute);
440  if (split)
441  mutt_buffer_add_printf(cur_attribute, "*%d", continuation_number++);
442  if (encode)
443  mutt_buffer_addch(cur_attribute, '*');
444 
445  while (*cur && (!split || (cur_value_len < max_value_len)))
446  {
447  if (encode)
448  {
449  if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
450  strchr("*'%", *cur))
451  {
452  mutt_buffer_add_printf(cur_value, "%%%02X", (unsigned char) *cur);
453  cur_value_len += 3;
454  }
455  else
456  {
457  mutt_buffer_addch(cur_value, *cur);
458  cur_value_len++;
459  }
460  }
461  else
462  {
463  mutt_buffer_addch(cur_value, *cur);
464  cur_value_len++;
465  if ((*cur == '\\') || (*cur == '"'))
466  cur_value_len++;
467  }
468 
469  cur++;
470  }
471 
472  current->attribute = mutt_buffer_strdup(cur_attribute);
473  current->value = mutt_buffer_strdup(cur_value);
474 
475  mutt_buffer_reset(cur_value);
476  cur_value_len = 0;
477  }
478 
479  mutt_buffer_pool_release(&cur_attribute);
480  mutt_buffer_pool_release(&cur_value);
481 
482  FREE(&charset);
483  if (free_src_value)
484  FREE(&src_value);
485 
486  return count;
487 }
char * attribute
Parameter name.
Definition: parameter.h:34
static int encode(const char *d, size_t dlen, int col, const char *fromcode, const char *charsets, char **e, size_t *elen, const char *specials)
RFC2047-encode a string.
Definition: rfc2047.c:416
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
char * mutt_ch_choose(const char *fromcode, const char *charsets, const char *u, size_t ulen, char **d, size_t *dlen)
Figure the best charset to encode a string.
Definition: charset.c:1040
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition: rfc2231.c:233
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
String manipulation buffer.
Definition: buffer.h:33
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:758
static void list_insert(struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
Insert parameter into an ordered list.
Definition: rfc2231.c:143
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
Container for Accounts, Notifications.
Definition: neomutt.h:36
RFC2047 MIME extensions encoding / decoding routines.
Convenience wrapper for the config headers.
RFC2231 MIME Charset routines.
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:641
Convenience wrapper for the core headers.
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
Constants and macros for managing MIME encoding.
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:72
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
static void join_continuations(struct ParameterList *pl, struct Rfc2231Parameter *par)
Process continuation parameters.
Definition: rfc2231.c:181
int mutt_ch_convert_nonmime_string(char **ps)
Try to convert a string using a list of character sets.
Definition: charset.c:308
void mutt_param_free_one(struct Parameter **p)
Free a Parameter.
Definition: parameter.c:48
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
static void decode_one(char *dest, char *src)
Decode one percent-encoded character.
Definition: rfc2231.c:107
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:809
static char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition: rfc2231.c:84
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:332
#define hexval(ch)
Definition: mime.h:80
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:796
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
char * value
Parameter value.
Definition: parameter.h:35
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:749
static void purge_empty_parameters(struct ParameterList *pl)
Remove any ill-formed Parameters from a list.
Definition: rfc2231.c:64
char * attribute
Definition: rfc2231.c:53
Attribute associated with a MIME part.
Definition: parameter.h:32
#define FREE(x)
Definition: memory.h:40
static struct Rfc2231Parameter * parameter_new(void)
Create a new Rfc2231Parameter.
Definition: rfc2231.c:130
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition: mbyte.c:423
static void parameter_free(struct Rfc2231Parameter **p)
Free an Rfc2231Parameter.
Definition: rfc2231.c:166
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
struct Rfc2231Parameter * next
Definition: rfc2231.c:57
Convenience wrapper for the library headers.
MIME section parameter.
Definition: rfc2231.c:51
Store attributes associated with a MIME part.
char * value
Definition: rfc2231.c:54