NeoMutt  2024-11-14-138-ge5ca67
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
  • Richard Russon
  • Federico Kircheis
  • Pietro Cerutti

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

67{
68 struct Parameter *np = NULL, *tmp = NULL;
69 TAILQ_FOREACH_SAFE(np, pl, entries, tmp)
70 {
71 if (!np->attribute || !np->value)
72 {
73 TAILQ_REMOVE(pl, np, entries);
75 }
76 }
77}
void mutt_param_free_one(struct Parameter **p)
Free a Parameter.
Definition: parameter.c:49
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:753
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:862
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 86 of file rfc2231.c.

87{
88 char *t = strchr(value, '\'');
89 if (!t)
90 {
91 charset[0] = '\0';
92 return value;
93 }
94
95 *t = '\0';
96 mutt_str_copy(charset, value, chslen);
97
98 char *u = strchr(t + 1, '\'');
99 if (u)
100 return u + 1;
101 return t + 1;
102}
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:581
+ 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 109 of file rfc2231.c.

110{
111 char *d = NULL;
112
113 for (d = dest; *src; src++)
114 {
115 if ((src[0] == '%') && isxdigit((unsigned char) src[1]) &&
116 isxdigit((unsigned char) src[2]))
117 {
118 *d++ = (hexval(src[1]) << 4) | hexval(src[2]);
119 src += 2;
120 }
121 else
122 {
123 *d++ = *src;
124 }
125 }
126
127 *d = '\0';
128}
#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 134 of file rfc2231.c.

135{
136 return MUTT_MEM_CALLOC(1, struct Rfc2231Parameter);
137}
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
MIME section parameter.
Definition: rfc2231.c:54
+ 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 147 of file rfc2231.c.

148{
149 struct Rfc2231Parameter **last = list;
150 struct Rfc2231Parameter *p = *list;
151
152 while (p)
153 {
154 const int c = mutt_str_cmp(par->attribute, p->attribute);
155 if ((c < 0) || ((c == 0) && (par->index <= p->index)))
156 break;
157
158 last = &p->next;
159 p = p->next;
160 }
161
162 par->next = p;
163 *last = par;
164}
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:399
int index
Index number in the list.
Definition: rfc2231.c:57
struct Rfc2231Parameter * next
Linked list.
Definition: rfc2231.c:59
char * attribute
Attribute name.
Definition: rfc2231.c:55
+ 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 170 of file rfc2231.c.

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

188{
189 char attribute[256] = { 0 };
190 char charset[256] = { 0 };
191
192 const char *const c_charset = cc_charset();
193 while (par)
194 {
195 char *value = NULL;
196 size_t l = 0;
197
198 mutt_str_copy(attribute, par->attribute, sizeof(attribute));
199
200 const bool encoded = par->encoded;
201 char *valp = NULL;
202 if (encoded)
203 valp = get_charset(par->value, charset, sizeof(charset));
204 else
205 valp = par->value;
206
207 do
208 {
209 if (encoded && par->encoded)
210 decode_one(par->value, valp);
211
212 const size_t vl = strlen(par->value);
213
214 MUTT_MEM_REALLOC(&value, l + vl + 1, char);
215 strcpy(value + l, par->value);
216 l += vl;
217
218 struct Rfc2231Parameter *q = par->next;
219 parameter_free(&par);
220 par = q;
221 if (par)
222 valp = par->value;
223 } while (par && (mutt_str_equal(par->attribute, attribute)));
224
225 if (encoded)
226 {
229 }
230
231 struct Parameter *np = mutt_param_new();
233 np->value = value;
234 TAILQ_INSERT_HEAD(pl, np, entries);
235 }
236}
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:116
int mutt_mb_filter_unprintable(char **s)
Replace unprintable characters.
Definition: mbyte.c:423
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:43
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:831
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition: charset.h:65
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
struct Parameter * mutt_param_new(void)
Create a new Parameter.
Definition: parameter.c:40
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:814
static char * get_charset(char *value, char *charset, size_t chslen)
Get the charset from an RFC2231 header.
Definition: rfc2231.c:86
static void parameter_free(struct Rfc2231Parameter **ptr)
Free an Rfc2231Parameter.
Definition: rfc2231.c:170
static void decode_one(char *dest, char *src)
Decode one percent-encoded character.
Definition: rfc2231.c:109
bool encoded
Is the value encoded?
Definition: rfc2231.c:58
+ 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 242 of file rfc2231.c.

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

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