NeoMutt  2023-11-03-107-g582dc1
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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.
 
static char * get_charset (char *value, char *charset, size_t chslen)
 Get the charset from an RFC2231 header.
 
static void decode_one (char *dest, char *src)
 Decode one percent-encoded character.
 
static struct Rfc2231Parameterparameter_new (void)
 Create a new Rfc2231Parameter.
 
static void list_insert (struct Rfc2231Parameter **list, struct Rfc2231Parameter *par)
 Insert parameter into an ordered list.
 
static void parameter_free (struct Rfc2231Parameter **ptr)
 Free an Rfc2231Parameter.
 
static void join_continuations (struct ParameterList *pl, struct Rfc2231Parameter *par)
 Process continuation parameters.
 
void rfc2231_decode_parameters (struct ParameterList *pl)
 Decode a Parameter list.
 
size_t rfc2231_encode_string (struct ParameterList *head, const char *attribute, char *value)
 Encode a string to be suitable for an RFC2231 header.
 

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:653
+ 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 {
121 *d++ = *src;
122 }
123 }
124
125 *d = '\0';
126}
#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 132 of file rfc2231.c.

133{
134 return mutt_mem_calloc(1, sizeof(struct Rfc2231Parameter));
135}
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 145 of file rfc2231.c.

146{
147 struct Rfc2231Parameter **last = list;
148 struct Rfc2231Parameter *p = *list;
149
150 while (p)
151 {
152 const int c = mutt_str_cmp(par->attribute, p->attribute);
153 if ((c < 0) || ((c == 0) && (par->index <= p->index)))
154 break;
155
156 last = &p->next;
157 p = p->next;
158 }
159
160 par->next = p;
161 *last = par;
162}
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:471
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 call graph for this function:
+ Here is the caller graph for this function:

◆ parameter_free()

static void parameter_free ( struct Rfc2231Parameter **  ptr)
static

Free an Rfc2231Parameter.

Parameters
[out]ptrRfc2231Parameter to free

Definition at line 168 of file rfc2231.c.

169{
170 if (!ptr || !*ptr)
171 return;
172
173 struct Rfc2231Parameter *p = *ptr;
174 FREE(&p->attribute);
175 FREE(&p->value);
176
177 FREE(ptr);
178}
#define FREE(x)
Definition: memory.h:45
char * value
Attribute value.
Definition: rfc2231.c:54
+ 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 185 of file rfc2231.c.

186{
187 char attribute[256] = { 0 };
188 char charset[256] = { 0 };
189
190 const char *const c_charset = cc_charset();
191 while (par)
192 {
193 char *value = NULL;
194 size_t l = 0;
195
196 mutt_str_copy(attribute, par->attribute, sizeof(attribute));
197
198 const bool encoded = par->encoded;
199 char *valp = NULL;
200 if (encoded)
201 valp = get_charset(par->value, charset, sizeof(charset));
202 else
203 valp = par->value;
204
205 do
206 {
207 if (encoded && par->encoded)
208 decode_one(par->value, valp);
209
210 const size_t vl = strlen(par->value);
211
212 mutt_mem_realloc(&value, l + vl + 1);
213 strcpy(value + l, par->value);
214 l += vl;
215
216 struct Rfc2231Parameter *q = par->next;
217 parameter_free(&par);
218 par = q;
219 if (par)
220 valp = par->value;
221 } while (par && (mutt_str_equal(par->attribute, attribute)));
222
223 if (encoded)
224 {
227 }
228
229 struct Parameter *np = mutt_param_new();
231 np->value = value;
232 TAILQ_INSERT_HEAD(pl, np, entries);
233 }
234}
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:115
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition: mbyte.c:419
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:826
#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:251
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:763
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 parameter_free(struct Rfc2231Parameter **ptr)
Free an Rfc2231Parameter.
Definition: rfc2231.c:168
static void decode_one(char *dest, char *src)
Decode one percent-encoded character.
Definition: rfc2231.c:107
bool encoded
Is the value encoded?
Definition: rfc2231.c:56
+ 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 240 of file rfc2231.c.

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

354{
355 if (!attribute || !value)
356 return 0;
357
358 size_t count = 0;
359 bool encode = false;
360 bool add_quotes = false;
361 bool free_src_value = false;
362 bool split = false;
363 int continuation_number = 0;
364 size_t dest_value_len = 0, max_value_len = 0, cur_value_len = 0;
365 char *cur = NULL, *charset = NULL, *src_value = NULL;
366 struct Parameter *current = NULL;
367
368 struct Buffer *cur_attribute = buf_pool_get();
369 struct Buffer *cur_value = buf_pool_get();
370
371 // Perform charset conversion
372 for (cur = value; *cur; cur++)
373 {
374 if ((*cur < 0x20) || (*cur >= 0x7f))
375 {
376 encode = true;
377 break;
378 }
379 }
380
381 if (encode)
382 {
383 const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
384 const char *c_charset = cc_charset();
385 if (c_charset && c_send_charset)
386 {
387 charset = mutt_ch_choose(c_charset, c_send_charset, value,
388 mutt_str_len(value), &src_value, NULL);
389 }
390 if (src_value)
391 free_src_value = true;
392 if (!charset)
393 charset = mutt_str_dup(c_charset ? c_charset : "unknown-8bit");
394 }
395 if (!src_value)
396 src_value = value;
397
398 // Count the size the resultant value will need in total
399 if (encode)
400 dest_value_len = mutt_str_len(charset) + 2; /* charset'' prefix */
401
402 for (cur = src_value; *cur; cur++)
403 {
404 dest_value_len++;
405
406 if (encode)
407 {
408 /* These get converted to %xx so need a total of three chars */
409 if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
410 strchr("*'%", *cur))
411 {
412 dest_value_len += 2;
413 }
414 }
415 else
416 {
417 /* rfc822_cat() will add outer quotes if it finds MimeSpecials. */
418 if (!add_quotes && strchr(MimeSpecials, *cur))
419 add_quotes = true;
420 /* rfc822_cat() will add a backslash if it finds '\' or '"'. */
421 if ((*cur == '\\') || (*cur == '"'))
422 dest_value_len++;
423 }
424 }
425
426 // Determine if need to split into parameter value continuations
427 max_value_len = 78 - // rfc suggested line length
428 1 - // Leading tab on continuation line
429 mutt_str_len(attribute) - // attribute
430 (encode ? 1 : 0) - // '*' encoding marker
431 1 - // '='
432 (add_quotes ? 2 : 0) - // "...."
433 1; // ';'
434
435 if (max_value_len < 30)
436 max_value_len = 30;
437
438 if (dest_value_len > max_value_len)
439 {
440 split = true;
441 max_value_len -= 4; /* '*n' continuation number and extra encoding
442 * space to keep loop below simpler */
443 }
444
445 // Generate list of parameter continuations
446 cur = src_value;
447 if (encode)
448 {
449 buf_printf(cur_value, "%s''", charset);
450 cur_value_len = buf_len(cur_value);
451 }
452
453 while (*cur)
454 {
455 current = mutt_param_new();
456 TAILQ_INSERT_TAIL(head, current, entries);
457 count++;
458
459 buf_strcpy(cur_attribute, attribute);
460 if (split)
461 buf_add_printf(cur_attribute, "*%d", continuation_number++);
462 if (encode)
463 buf_addch(cur_attribute, '*');
464
465 while (*cur && (!split || (cur_value_len < max_value_len)))
466 {
467 if (encode)
468 {
469 if ((*cur < 0x20) || (*cur >= 0x7f) || strchr(MimeSpecials, *cur) ||
470 strchr("*'%", *cur))
471 {
472 buf_add_printf(cur_value, "%%%02X", (unsigned char) *cur);
473 cur_value_len += 3;
474 }
475 else
476 {
477 buf_addch(cur_value, *cur);
478 cur_value_len++;
479 }
480 }
481 else
482 {
483 buf_addch(cur_value, *cur);
484 cur_value_len++;
485 if ((*cur == '\\') || (*cur == '"'))
486 cur_value_len++;
487 }
488
489 cur++;
490 }
491
492 current->attribute = buf_strdup(cur_attribute);
493 current->value = buf_strdup(cur_value);
494
495 buf_reset(cur_value);
496 cur_value_len = 0;
497 }
498
499 buf_pool_release(&cur_attribute);
500 buf_pool_release(&cur_value);
501
502 FREE(&charset);
503 if (free_src_value)
504 FREE(&src_value);
505
506 return count;
507}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:216
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:243
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:1106
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
#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:424
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: