NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
rfc2231.c File Reference

RFC2231 MIME Charset routines. More...

#include "config.h"
#include <ctype.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. More...
 
static char * get_charset (char *value, char *charset, size_t chslen)
 Get the charset from an RFC2231 header. More...
 
static void decode_one (char *dest, char *src)
 Decode one percent-encoded character. More...
 
static struct Rfc2231Parameterparameter_new (void)
 Create a new Rfc2231Parameter. More...
 
static void list_insert (struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
 Insert parameter into an ordered list. More...
 
static void parameter_free (struct Rfc2231Parameter **p)
 Free an Rfc2231Parameter. More...
 
static void join_continuations (struct ParameterList *pl, struct Rfc2231Parameter *par)
 Process continuation parameters. More...
 
void rfc2231_decode_parameters (struct ParameterList *pl)
 Decode a Parameter list. More...
 
size_t rfc2231_encode_string (struct ParameterList *head, const char *attribute, char *value)
 Encode a string to be suitable for an RFC2231 header. More...
 

Detailed Description

RFC2231 MIME Charset routines.

Authors
  • Thomas Roessler

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

static void purge_empty_parameters ( struct ParameterList *  pl)
static

Remove any ill-formed Parameters from a list.

Parameters
plParameter List to check

Definition at line 64 of file rfc2231.c.

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 }
char * attribute
Parameter name.
Definition: parameter.h:34
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
void mutt_param_free_one(struct Parameter **p)
Free a Parameter.
Definition: parameter.c:48
char * value
Parameter value.
Definition: parameter.h:35
Attribute associated with a MIME part.
Definition: parameter.h:32
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_charset()

static 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 84 of file rfc2231.c.

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 }
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ decode_one()

static 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 107 of file rfc2231.c.

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 }
#define hexval(ch)
Definition: mime.h:80
+ Here is the caller graph for this function:

◆ parameter_new()

static struct Rfc2231Parameter* parameter_new ( void  )
static

Create a new Rfc2231Parameter.

Return values
ptrNewly allocated Rfc2231Parameter

Definition at line 130 of file rfc2231.c.

131 {
132  return mutt_mem_calloc(1, sizeof(struct Rfc2231Parameter));
133 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
MIME section parameter.
Definition: rfc2231.c:51
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ list_insert()

static 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 143 of file rfc2231.c.

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 }
char * attribute
Definition: rfc2231.c:53
struct Rfc2231Parameter * next
Definition: rfc2231.c:57
MIME section parameter.
Definition: rfc2231.c:51
+ Here is the caller graph for this function:

◆ parameter_free()

static void parameter_free ( struct Rfc2231Parameter **  p)
static

Free an Rfc2231Parameter.

Parameters
[out]pRfc2231Parameter to free

Definition at line 166 of file rfc2231.c.

167 {
168  if (!p || !*p)
169  return;
170 
171  FREE(&(*p)->attribute);
172  FREE(&(*p)->value);
173  FREE(p);
174 }
#define FREE(x)
Definition: memory.h:40
+ Here is the caller graph for this function:

◆ join_continuations()

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

Process continuation parameters.

Parameters
plParameter List for the results
parContinuation Parameter

Definition at line 181 of file rfc2231.c.

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 }
char * attribute
Parameter name.
Definition: parameter.h:34
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
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
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:72
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
static char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition: rfc2231.c:84
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:796
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
char * attribute
Definition: rfc2231.c:53
Attribute associated with a MIME part.
Definition: parameter.h:32
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
MIME section parameter.
Definition: rfc2231.c:51
char * value
Definition: rfc2231.c:54
+ 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 233 of file rfc2231.c.

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 }
char * attribute
Parameter name.
Definition: parameter.h:34
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
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
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
Container for Accounts, Notifications.
Definition: neomutt.h:36
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:641
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:72
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
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
static char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition: rfc2231.c:84
char * value
Parameter value.
Definition: parameter.h:35
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
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
MIME section parameter.
Definition: rfc2231.c:51
char * value
Definition: rfc2231.c:54
+ 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 332 of file rfc2231.c.

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_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
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 mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
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
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
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
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:809
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
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
Attribute associated with a MIME part.
Definition: parameter.h:32
#define FREE(x)
Definition: memory.h:40
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function: