NeoMutt  2022-04-29-145-g9b6a0e
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 }
void mutt_param_free_one(struct Parameter **p)
Free a Parameter.
Definition: parameter.c:48
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
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()

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 }
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:629
+ 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:52
+ 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 }
int index
Index number in the list.
Definition: rfc2231.c:55
struct Rfc2231Parameter * next
Linked list.
Definition: rfc2231.c:57
char * attribute
Attribute name.
Definition: rfc2231.c:53
+ 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:43
+ 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  {
221  mutt_ch_convert_string(&value, charset, c_charset, MUTT_ICONV_HOOK_FROM);
223  }
224 
225  struct Parameter *np = mutt_param_new();
227  np->value = value;
228  TAILQ_INSERT_HEAD(pl, np, entries);
229  }
230 }
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition: mbyte.c:421
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:752
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:72
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:39
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:796
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
static void parameter_free(struct Rfc2231Parameter **p)
Free an Rfc2231Parameter.
Definition: rfc2231.c:166
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
bool encoded
Is the value encoded?
Definition: rfc2231.c:56
char * value
Attribute 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 236 of file rfc2231.c.

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

345 {
346  if (!attribute || !value)
347  return 0;
348 
349  size_t count = 0;
350  bool encode = false;
351  bool add_quotes = false;
352  bool free_src_value = false;
353  bool split = false;
354  int continuation_number = 0;
355  size_t dest_value_len = 0, max_value_len = 0, cur_value_len = 0;
356  char *cur = NULL, *charset = NULL, *src_value = NULL;
357  struct Parameter *current = NULL;
358 
359  struct Buffer *cur_attribute = mutt_buffer_pool_get();
360  struct Buffer *cur_value = mutt_buffer_pool_get();
361 
362  // Perform charset conversion
363  for (cur = value; *cur; cur++)
364  {
365  if ((*cur < 0x20) || (*cur >= 0x7f))
366  {
367  encode = true;
368  break;
369  }
370  }
371 
372  if (encode)
373  {
374  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
375  const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
376  if (c_charset && c_send_charset)
377  {
378  charset = mutt_ch_choose(c_charset, c_send_charset, value,
379  mutt_str_len(value), &src_value, NULL);
380  }
381  if (src_value)
382  free_src_value = true;
383  if (!charset)
384  charset = mutt_str_dup(c_charset ? c_charset : "unknown-8bit");
385  }
386  if (!src_value)
387  src_value = value;
388 
389  // Count the size the resultant value will need in total
390  if (encode)
391  dest_value_len = mutt_str_len(charset) + 2; /* charset'' prefix */
392 
393  for (cur = src_value; *cur; cur++)
394  {
395  dest_value_len++;
396 
397  if (encode)
398  {
399  /* These get converted to %xx so need a total of three chars */
400  if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
401  strchr("*'%", *cur))
402  {
403  dest_value_len += 2;
404  }
405  }
406  else
407  {
408  /* rfc822_cat() will add outer quotes if it finds MimeSpecials. */
409  if (!add_quotes && strchr(MimeSpecials, *cur))
410  add_quotes = true;
411  /* rfc822_cat() will add a backslash if it finds '\' or '"'. */
412  if ((*cur == '\\') || (*cur == '"'))
413  dest_value_len++;
414  }
415  }
416 
417  // Determine if need to split into parameter value continuations
418  max_value_len = 78 - // rfc suggested line length
419  1 - // Leading tab on continuation line
420  mutt_str_len(attribute) - // attribute
421  (encode ? 1 : 0) - // '*' encoding marker
422  1 - // '='
423  (add_quotes ? 2 : 0) - // "...."
424  1; // ';'
425 
426  if (max_value_len < 30)
427  max_value_len = 30;
428 
429  if (dest_value_len > max_value_len)
430  {
431  split = true;
432  max_value_len -= 4; /* '*n' continuation number and extra encoding
433  * space to keep loop below simpler */
434  }
435 
436  // Generate list of parameter continuations
437  cur = src_value;
438  if (encode)
439  {
440  mutt_buffer_printf(cur_value, "%s''", charset);
441  cur_value_len = mutt_buffer_len(cur_value);
442  }
443 
444  while (*cur)
445  {
446  current = mutt_param_new();
447  TAILQ_INSERT_TAIL(head, current, entries);
448  count++;
449 
450  mutt_buffer_strcpy(cur_attribute, attribute);
451  if (split)
452  mutt_buffer_add_printf(cur_attribute, "*%d", continuation_number++);
453  if (encode)
454  mutt_buffer_addch(cur_attribute, '*');
455 
456  while (*cur && (!split || (cur_value_len < max_value_len)))
457  {
458  if (encode)
459  {
460  if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
461  strchr("*'%", *cur))
462  {
463  mutt_buffer_add_printf(cur_value, "%%%02X", (unsigned char) *cur);
464  cur_value_len += 3;
465  }
466  else
467  {
468  mutt_buffer_addch(cur_value, *cur);
469  cur_value_len++;
470  }
471  }
472  else
473  {
474  mutt_buffer_addch(cur_value, *cur);
475  cur_value_len++;
476  if ((*cur == '\\') || (*cur == '"'))
477  cur_value_len++;
478  }
479 
480  cur++;
481  }
482 
483  current->attribute = mutt_buffer_strdup(cur_attribute);
484  current->value = mutt_buffer_strdup(cur_value);
485 
486  mutt_buffer_reset(cur_value);
487  cur_value_len = 0;
488  }
489 
490  mutt_buffer_pool_release(&cur_attribute);
491  mutt_buffer_pool_release(&cur_value);
492 
493  FREE(&charset);
494  if (free_src_value)
495  FREE(&src_value);
496 
497  return count;
498 }
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:354
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:238
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:430
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:201
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:158
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:81
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
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:1035
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:809
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:419
String manipulation buffer.
Definition: buffer.h:34
struct ListHead head
List containing values.
Definition: slist.h:48
size_t count
Number of values in list.
Definition: slist.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function: