NeoMutt  2023-03-22-27-g3cb248
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:423
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:751
#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 {
271 rfc2047_decode(&np->value);
272 }
273 else if (c_assumed_charset)
274 {
275 const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
276 mutt_ch_convert_nonmime_string(c_assumed_charset, c_charset, &np->value);
277 }
278 }
279 /* Single value with encoding:
280 * attr*=us-ascii''the%20value
281 */
282 else if (s[1] == '\0')
283 {
284 s[0] = '\0';
285
286 s = get_charset(np->value, charset, sizeof(charset));
287 decode_one(np->value, s);
288 const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
289 mutt_ch_convert_string(&np->value, charset, c_charset, MUTT_ICONV_HOOK_FROM);
291 dirty = true;
292 }
293 /* A parameter continuation, which may or may not be encoded:
294 * attr*0=value
295 * -or-
296 * attr*0*=us-ascii''the%20value
297 */
298 else
299 {
300 s[0] = '\0';
301 s++; /* let s point to the first character of index. */
302 for (t = s; (t[0] != '\0') && isdigit((unsigned char) t[0]); t++)
303 ; // do nothing
304
305 encoded = (t[0] == '*');
306 t[0] = '\0';
307
308 /* RFC2231 says that the index starts at 0 and increments by 1,
309 * thus an overflow should never occur in a valid message, thus
310 * the value INT_MAX in case of overflow does not really matter
311 * (the goal is just to avoid undefined behaviour). */
312 if (!mutt_str_atoi_full(s, &index))
313 index = INT_MAX;
314
315 conttmp = parameter_new();
316 conttmp->attribute = np->attribute;
317 conttmp->value = np->value;
318 conttmp->encoded = encoded;
319 conttmp->index = index;
320
321 np->attribute = NULL;
322 np->value = NULL;
323 TAILQ_REMOVE(pl, np, entries);
324 FREE(&np);
325
326 list_insert(&conthead, conttmp);
327 }
328 }
329
330 if (conthead)
331 {
332 join_continuations(pl, conthead);
333 dirty = true;
334 }
335
336 if (dirty)
338}
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(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:307
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:655
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 349 of file rfc2231.c.

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