NeoMutt  2022-04-29-247-gc6aae8
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:652
+ 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] = { 0 };
184 char charset[256] = { 0 };
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 {
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 char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition: rfc2231.c:84
static void decode_one(char *dest, char *src)
Decode one percent-encoded character.
Definition: rfc2231.c:107
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] = { 0 };
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 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
static struct Rfc2231Parameter * parameter_new(void)
Create a new Rfc2231Parameter.
Definition: rfc2231.c:130
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:327
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:371
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:211
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:447
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:567
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: