NeoMutt  2019-12-07-168-gc45f47
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 <stdlib.h>
40 #include <string.h>
41 #include "mutt/lib.h"
42 #include "rfc2231.h"
43 #include "email_globals.h"
44 #include "mime.h"
45 #include "parameter.h"
46 #include "rfc2047.h"
47 
48 /* These Config Variables are only used in rfc2231.c */
50 
55 {
56  char *attribute;
57  char *value;
58  int index;
59  bool encoded;
61 };
62 
67 static void purge_empty_parameters(struct ParameterList *pl)
68 {
69  struct Parameter *np = NULL, *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 }
79 
87 static char *get_charset(char *value, char *charset, size_t chslen)
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_strfcpy(charset, value, chslen);
98 
99  char *u = strchr(t + 1, '\'');
100  if (u)
101  return u + 1;
102  return t + 1;
103 }
104 
110 static void decode_one(char *dest, char *src)
111 {
112  char *d = NULL;
113 
114  for (d = dest; *src; src++)
115  {
116  if ((src[0] == '%') && isxdigit((unsigned char) src[1]) &&
117  isxdigit((unsigned char) src[2]))
118  {
119  *d++ = (hexval(src[1]) << 4) | hexval(src[2]);
120  src += 2;
121  }
122  else
123  *d++ = *src;
124  }
125 
126  *d = '\0';
127 }
128 
133 static struct Rfc2231Parameter *parameter_new(void)
134 {
135  return mutt_mem_calloc(1, sizeof(struct Rfc2231Parameter));
136 }
137 
146 static void list_insert(struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
147 {
148  struct Rfc2231Parameter **last = list;
149  struct Rfc2231Parameter *p = *list;
150 
151  while (p)
152  {
153  const int c = strcmp(par->attribute, p->attribute);
154  if ((c < 0) || ((c == 0) && (par->index <= p->index)))
155  break;
156 
157  last = &p->next;
158  p = p->next;
159  }
160 
161  par->next = p;
162  *last = par;
163 }
164 
169 static void parameter_free(struct Rfc2231Parameter **p)
170 {
171  if (!p || !*p)
172  return;
173 
174  FREE(&(*p)->attribute);
175  FREE(&(*p)->value);
176  FREE(p);
177 }
178 
184 static void join_continuations(struct ParameterList *pl, struct Rfc2231Parameter *par)
185 {
186  char attribute[256];
187  char charset[256];
188 
189  while (par)
190  {
191  char *value = NULL;
192  size_t l = 0;
193 
194  mutt_str_strfcpy(attribute, par->attribute, sizeof(attribute));
195 
196  const bool encoded = par->encoded;
197  char *valp = NULL;
198  if (encoded)
199  valp = get_charset(par->value, charset, sizeof(charset));
200  else
201  valp = par->value;
202 
203  do
204  {
205  if (encoded && par->encoded)
206  decode_one(par->value, valp);
207 
208  const size_t vl = strlen(par->value);
209 
210  mutt_mem_realloc(&value, l + vl + 1);
211  strcpy(value + l, par->value);
212  l += vl;
213 
214  struct Rfc2231Parameter *q = par->next;
215  parameter_free(&par);
216  par = q;
217  if (par)
218  valp = par->value;
219  } while (par && (strcmp(par->attribute, attribute) == 0));
220 
221  if (encoded)
223 
224  struct Parameter *np = mutt_param_new();
225  np->attribute = mutt_str_strdup(attribute);
226  np->value = value;
227  TAILQ_INSERT_HEAD(pl, np, entries);
228  }
229 }
230 
235 void rfc2231_decode_parameters(struct ParameterList *pl)
236 {
237  if (!pl)
238  return;
239 
240  struct Rfc2231Parameter *conthead = NULL;
241  struct Rfc2231Parameter *conttmp = NULL;
242 
243  char *s = NULL, *t = NULL;
244  char charset[256];
245 
246  bool encoded;
247  int index;
248  bool dirty = false; /* set when we may have created empty parameters. */
249 
251 
252  struct Parameter *np = NULL, *tmp = NULL;
253  TAILQ_FOREACH_SAFE(np, pl, entries, tmp)
254  {
255  s = strchr(np->attribute, '*');
256  if (!s)
257  {
258  /* Using RFC2047 encoding in MIME parameters is explicitly
259  * forbidden by that document. Nevertheless, it's being
260  * generated by some software, including certain Lotus Notes to
261  * Internet Gateways. So we actually decode it. */
262 
263  if (C_Rfc2047Parameters && np->value && strstr(np->value, "=?"))
264  rfc2047_decode(&np->value);
265  else if (C_AssumedCharset)
267  }
268  else if (s[1] == '\0')
269  {
270  s[0] = '\0';
271 
272  s = get_charset(np->value, charset, sizeof(charset));
273  decode_one(np->value, s);
276  dirty = true;
277  }
278  else
279  {
280  s[0] = '\0';
281  s++; /* let s point to the first character of index. */
282  for (t = s; (t[0] != '\0') && isdigit((unsigned char) t[0]); t++)
283  ;
284  encoded = (t[0] == '*');
285  t[0] = '\0';
286 
287  /* RFC2231 says that the index starts at 0 and increments by 1,
288  * thus an overflow should never occur in a valid message, thus
289  * the value INT_MAX in case of overflow does not really matter
290  * (the goal is just to avoid undefined behaviour). */
291  if (mutt_str_atoi(s, &index) != 0)
292  index = INT_MAX;
293 
294  conttmp = parameter_new();
295  conttmp->attribute = np->attribute;
296  conttmp->value = np->value;
297  conttmp->encoded = encoded;
298  conttmp->index = index;
299 
300  np->attribute = NULL;
301  np->value = NULL;
302  TAILQ_REMOVE(pl, np, entries);
303  FREE(&np);
304 
305  list_insert(&conthead, conttmp);
306  }
307  }
308 
309  if (conthead)
310  {
311  join_continuations(pl, conthead);
312  dirty = true;
313  }
314 
315  if (dirty)
317 }
318 
328 struct ParameterList rfc2231_encode_string(const char *attribute, char *value)
329 {
330  struct ParameterList pl;
331  TAILQ_INIT(&pl);
332 
333  if (!attribute || !value)
334  return pl;
335 
336  bool encode = false;
337  bool add_quotes = false;
338  bool free_src_value = false;
339  bool split = false;
340  int continuation_number = 0;
341  size_t dest_value_len = 0, max_value_len = 0, cur_value_len = 0;
342  char *cur = NULL, *charset = NULL, *src_value = NULL;
343  struct Parameter *current = NULL;
344 
345  struct Buffer *cur_attribute = mutt_buffer_pool_get();
346  struct Buffer *cur_value = mutt_buffer_pool_get();
347 
348  // Perform charset conversion
349  for (cur = value; *cur; cur++)
350  {
351  if ((*cur < 0x20) || (*cur >= 0x7f))
352  {
353  encode = true;
354  break;
355  }
356  }
357 
358  if (encode)
359  {
360  if (C_Charset && C_SendCharset)
361  {
363  mutt_str_strlen(value), &src_value, NULL);
364  }
365  if (src_value)
366  free_src_value = true;
367  if (!charset)
368  charset = mutt_str_strdup(C_Charset ? C_Charset : "unknown-8bit");
369  }
370  if (!src_value)
371  src_value = value;
372 
373  // Count the size the resultant value will need in total
374  if (encode)
375  dest_value_len = mutt_str_strlen(charset) + 2; /* charset'' prefix */
376 
377  for (cur = src_value; *cur; cur++)
378  {
379  dest_value_len++;
380 
381  if (encode)
382  {
383  /* These get converted to %xx so need a total of three chars */
384  if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
385  strchr("*'%", *cur))
386  {
387  dest_value_len += 2;
388  }
389  }
390  else
391  {
392  /* rfc822_cat() will add outer quotes if it finds MimeSpecials. */
393  if (!add_quotes && strchr(MimeSpecials, *cur))
394  add_quotes = true;
395  /* rfc822_cat() will add a backslash if it finds '\' or '"'. */
396  if ((*cur == '\\') || (*cur == '"'))
397  dest_value_len++;
398  }
399  }
400 
401  // Determine if need to split into parameter value continuations
402  max_value_len = 78 - // rfc suggested line length
403  1 - // Leading tab on continuation line
404  mutt_str_strlen(attribute) - // attribute
405  (encode ? 1 : 0) - // '*' encoding marker
406  1 - // '='
407  (add_quotes ? 2 : 0) - // "...."
408  1; // ';'
409 
410  if (max_value_len < 30)
411  max_value_len = 30;
412 
413  if (dest_value_len > max_value_len)
414  {
415  split = true;
416  max_value_len -= 4; /* '*n' continuation number and extra encoding
417  * space to keep loop below simpler */
418  }
419 
420  // Generate list of parameter continuations
421  cur = src_value;
422  if (encode)
423  {
424  mutt_buffer_printf(cur_value, "%s''", charset);
425  cur_value_len = mutt_buffer_len(cur_value);
426  }
427 
428  while (*cur)
429  {
430  current = mutt_param_new();
431  TAILQ_INSERT_TAIL(&pl, current, entries);
432 
433  mutt_buffer_strcpy(cur_attribute, attribute);
434  if (split)
435  mutt_buffer_add_printf(cur_attribute, "*%d", continuation_number++);
436  if (encode)
437  mutt_buffer_addch(cur_attribute, '*');
438 
439  while (*cur && (!split || (cur_value_len < max_value_len)))
440  {
441  if (encode)
442  {
443  if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
444  strchr("*'%", *cur))
445  {
446  mutt_buffer_add_printf(cur_value, "%%%02X", (unsigned char) *cur);
447  cur_value_len += 3;
448  }
449  else
450  {
451  mutt_buffer_addch(cur_value, *cur);
452  cur_value_len++;
453  }
454  }
455  else
456  {
457  mutt_buffer_addch(cur_value, *cur);
458  cur_value_len++;
459  if ((*cur == '\\') || (*cur == '"'))
460  cur_value_len++;
461  }
462 
463  cur++;
464  }
465 
466  current->attribute = mutt_buffer_strdup(cur_attribute);
467  current->value = mutt_buffer_strdup(cur_value);
468 
469  mutt_buffer_reset(cur_value);
470  cur_value_len = 0;
471  }
472 
473  mutt_buffer_pool_release(&cur_attribute);
474  mutt_buffer_pool_release(&cur_value);
475 
476  FREE(&charset);
477  if (free_src_value)
478  FREE(&src_value);
479 
480  return pl;
481 }
char * attribute
Parameter name.
Definition: parameter.h:34
char * C_AssumedCharset
Config: If a message is missing a character set, assume this character set.
Definition: charset.c:53
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:428
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int mutt_ch_convert_string(char **ps, const char *from, const char *to, int flags)
Convert a string between encodings.
Definition: charset.c:748
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:262
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
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:1030
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
void rfc2231_decode_parameters(struct ParameterList *pl)
Decode a Parameter list.
Definition: rfc2231.c:235
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
char * mutt_buffer_strdup(struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
String manipulation buffer.
Definition: buffer.h:33
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:728
static void list_insert(struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
Insert parameter into an ordered list.
Definition: rfc2231.c:146
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:689
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
RFC2047 MIME extensions encoding / decoding routines.
RFC2231 MIME Charset routines.
char * C_SendCharset
Config: Character sets for outgoing mail.
Definition: email_globals.c:38
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:651
#define TAILQ_INIT(head)
Definition: queue.h:758
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
Email Global Variables.
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:834
Constants and macros for managing MIME encoding.
bool C_Rfc2047Parameters
Config: Decode RFC2047-encoded MIME parameters.
Definition: rfc2231.c:49
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
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:184
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:773
void mutt_param_free_one(struct Parameter **p)
Free a Parameter.
Definition: parameter.c:48
static void decode_one(char *dest, char *src)
Decode one percent-encoded character.
Definition: rfc2231.c:110
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:802
static char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition: rfc2231.c:87
#define hexval(ch)
Definition: mime.h:75
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:789
char * value
Parameter value.
Definition: parameter.h:35
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
struct ParameterList rfc2231_encode_string(const char *attribute, char *value)
Encode a string to be suitable for an RFC2231 header.
Definition: rfc2231.c:328
int mutt_ch_convert_nonmime_string(char **ps)
Try to convert a string using a list of character sets.
Definition: charset.c:301
static void purge_empty_parameters(struct ParameterList *pl)
Remove any ill-formed Parameters from a list.
Definition: rfc2231.c:67
char * attribute
Definition: rfc2231.c:56
Attribute associated with a MIME part.
Definition: parameter.h:32
#define FREE(x)
Definition: memory.h:40
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:54
static struct Rfc2231Parameter * parameter_new(void)
Create a new Rfc2231Parameter.
Definition: rfc2231.c:133
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition: mbyte.c:424
static void parameter_free(struct Rfc2231Parameter **p)
Free an Rfc2231Parameter.
Definition: rfc2231.c:169
struct Rfc2231Parameter * next
Definition: rfc2231.c:60
Convenience wrapper for the library headers.
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:81
MIME section parameter.
Definition: rfc2231.c:54
Store attributes associated with a MIME part.
char * value
Definition: rfc2231.c:57