1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2020 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20 
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <string.h>
27 #include <limits.h>
28 #include <ctype.h>
29 #include <errno.h>
30 
31 #include "gmime-param.h"
32 #include "gmime-common.h"
33 #include "gmime-events.h"
34 #include "gmime-internal.h"
35 #include "gmime-table-private.h"
36 #include "gmime-parse-utils.h"
37 #include "gmime-iconv-utils.h"
38 #include "gmime-charset.h"
39 #include "gmime-utils.h"
40 #include "gmime-iconv.h"
41 
42 
43 #ifdef ENABLE_WARNINGS
44 #define w(x) x
45 #else
46 #define w(x)
47 #endif /* ENABLE_WARNINGS */
48 
49 #define d(x)
50 
51 /**
52  * SECTION: gmime-param
53  * @title: GMimeParamList
54  * @short_description: Content-Type and Content-Disposition parameters
55  * @see_also: #GMimeContentType
56  *
57  * A #GMimeParam is a parameter name/value pair as found on MIME
58  * header fields such as Content-Type and Content-Disposition.
59  **/
60 
61 
62 static unsigned char tohex[16] = {
63 	'0', '1', '2', '3', '4', '5', '6', '7',
64 	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
65 };
66 
67 
68 static void g_mime_param_class_init (GMimeParamClass *klass);
69 static void g_mime_param_init (GMimeParam *cert, GMimeParamClass *klass);
70 static void g_mime_param_finalize (GObject *object);
71 
72 static GObjectClass *parent_class = NULL;
73 
74 
75 GType
g_mime_param_get_type(void)76 g_mime_param_get_type (void)
77 {
78 	static GType type = 0;
79 
80 	if (!type) {
81 		static const GTypeInfo info = {
82 			sizeof (GMimeParamClass),
83 			NULL, /* base_class_init */
84 			NULL, /* base_class_finalize */
85 			(GClassInitFunc) g_mime_param_class_init,
86 			NULL, /* class_finalize */
87 			NULL, /* class_data */
88 			sizeof (GMimeParam),
89 			0,    /* n_preallocs */
90 			(GInstanceInitFunc) g_mime_param_init,
91 		};
92 
93 		type = g_type_register_static (G_TYPE_OBJECT, "GMimeParam", &info, 0);
94 	}
95 
96 	return type;
97 }
98 
99 static void
g_mime_param_class_init(GMimeParamClass * klass)100 g_mime_param_class_init (GMimeParamClass *klass)
101 {
102 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
103 
104 	parent_class = g_type_class_ref (G_TYPE_OBJECT);
105 
106 	object_class->finalize = g_mime_param_finalize;
107 }
108 
109 static void
g_mime_param_init(GMimeParam * param,GMimeParamClass * klass)110 g_mime_param_init (GMimeParam *param, GMimeParamClass *klass)
111 {
112 	param->method = GMIME_PARAM_ENCODING_METHOD_DEFAULT;
113 	param->changed = g_mime_event_new (param);
114 	param->charset = NULL;
115 	param->value = NULL;
116 	param->name = NULL;
117 	param->lang = NULL;
118 }
119 
120 static void
g_mime_param_finalize(GObject * object)121 g_mime_param_finalize (GObject *object)
122 {
123 	GMimeParam *param = (GMimeParam *) object;
124 
125 	g_mime_event_free (param->changed);
126 	g_free (param->charset);
127 	g_free (param->value);
128 	g_free (param->name);
129 	g_free (param->lang);
130 
131 	G_OBJECT_CLASS (parent_class)->finalize (object);
132 }
133 
134 
135 /**
136  * g_mime_param_new:
137  *
138  * Creates a new #GMimeParam.
139  *
140  * Returns: a new #GMimeParam.
141  **/
142 static GMimeParam *
g_mime_param_new(void)143 g_mime_param_new (void)
144 {
145 	return g_object_new (GMIME_TYPE_PARAM, NULL);
146 }
147 
148 
149 /**
150  * g_mime_param_get_name:
151  * @param: a #GMimeParam
152  *
153  * Gets the name of the parameter.
154  *
155  * Returns: the name of the parameter.
156  **/
157 const char *
g_mime_param_get_name(GMimeParam * param)158 g_mime_param_get_name (GMimeParam *param)
159 {
160 	g_return_val_if_fail (GMIME_IS_PARAM (param), NULL);
161 
162 	return param->name;
163 }
164 
165 
166 /**
167  * g_mime_param_get_value:
168  * @param: a #GMimeParam
169  *
170  * Gets the value of the parameter.
171  *
172  * Returns: the value of the parameter.
173  **/
174 const char *
g_mime_param_get_value(GMimeParam * param)175 g_mime_param_get_value (GMimeParam *param)
176 {
177 	g_return_val_if_fail (GMIME_IS_PARAM (param), NULL);
178 
179 	return param->value;
180 }
181 
182 
183 /**
184  * g_mime_param_set_value:
185  * @param: a #GMimeParam
186  * @value: the new parameter value
187  *
188  * Sets the parameter value to @value.
189  **/
190 void
g_mime_param_set_value(GMimeParam * param,const char * value)191 g_mime_param_set_value (GMimeParam *param, const char *value)
192 {
193 	g_return_if_fail (GMIME_IS_PARAM (param));
194 	g_return_if_fail (value != NULL);
195 
196 	g_free (param->value);
197 	param->value = g_strdup (value);
198 
199 	g_mime_event_emit (param->changed, NULL);
200 }
201 
202 
203 /**
204  * g_mime_param_get_charset:
205  * @param: a #GMimeParam
206  *
207  * Gets the charset used for encoding the parameter.
208  *
209  * Returns: the charset used for encoding the parameter.
210  **/
211 const char *
g_mime_param_get_charset(GMimeParam * param)212 g_mime_param_get_charset (GMimeParam *param)
213 {
214 	g_return_val_if_fail (GMIME_IS_PARAM (param), NULL);
215 
216 	return param->charset;
217 }
218 
219 
220 /**
221  * g_mime_param_set_charset:
222  * @param: a #GMimeParam
223  * @charset: the charset or %NULL to use the default
224  *
225  * Sets the parameter charset used for encoding the value.
226  **/
227 void
g_mime_param_set_charset(GMimeParam * param,const char * charset)228 g_mime_param_set_charset (GMimeParam *param, const char *charset)
229 {
230 	g_return_if_fail (GMIME_IS_PARAM (param));
231 
232 	g_free (param->charset);
233 	param->charset = charset ? g_strdup (charset) : NULL;
234 
235 	g_mime_event_emit (param->changed, NULL);
236 }
237 
238 
239 /**
240  * g_mime_param_get_lang:
241  * @param: a #GMimeParam
242  *
243  * Gets the language specifier used for encoding the parameter.
244  *
245  * Returns: the language specifier used for encoding the parameter.
246  **/
247 const char *
g_mime_param_get_lang(GMimeParam * param)248 g_mime_param_get_lang (GMimeParam *param)
249 {
250 	g_return_val_if_fail (GMIME_IS_PARAM (param), NULL);
251 
252 	return param->lang;
253 }
254 
255 
256 /**
257  * g_mime_param_set_lang:
258  * @param: a #GMimeParam
259  * @lang: the language specifier
260  *
261  * Sets the parameter language specifier used for encoding the value.
262  **/
263 void
g_mime_param_set_lang(GMimeParam * param,const char * lang)264 g_mime_param_set_lang (GMimeParam *param, const char *lang)
265 {
266 	g_return_if_fail (GMIME_IS_PARAM (param));
267 
268 	g_free (param->lang);
269 	param->lang = lang ? g_strdup (lang) : NULL;
270 
271 	g_mime_event_emit (param->changed, NULL);
272 }
273 
274 
275 /**
276  * g_mime_param_get_encoding_method:
277  * @param: a #GMimeParam
278  *
279  * Gets the encoding method used for encoding the parameter.
280  *
281  * Returns: the encoding method used for encoding the parameter.
282  **/
283 GMimeParamEncodingMethod
g_mime_param_get_encoding_method(GMimeParam * param)284 g_mime_param_get_encoding_method (GMimeParam *param)
285 {
286 	g_return_val_if_fail (GMIME_IS_PARAM (param), GMIME_PARAM_ENCODING_METHOD_DEFAULT);
287 
288 	return param->method;
289 }
290 
291 
292 /**
293  * g_mime_param_set_encoding_method:
294  * @param: a #GMimeParam
295  * @method: a #GMimeParamEncodingMethod
296  *
297  * Sets the encoding method used for encoding the value.
298  **/
299 void
g_mime_param_set_encoding_method(GMimeParam * param,GMimeParamEncodingMethod method)300 g_mime_param_set_encoding_method (GMimeParam *param, GMimeParamEncodingMethod method)
301 {
302 	g_return_if_fail (GMIME_IS_PARAM (param));
303 
304 	param->method = method;
305 
306 	g_mime_event_emit (param->changed, NULL);
307 }
308 
309 
310 static void
param_changed(GMimeParam * param,gpointer args,GMimeParamList * list)311 param_changed (GMimeParam *param, gpointer args, GMimeParamList *list)
312 {
313 	g_mime_event_emit (list->changed, NULL);
314 }
315 
316 
317 static void g_mime_param_list_class_init (GMimeParamListClass *klass);
318 static void g_mime_param_list_init (GMimeParamList *list, GMimeParamListClass *klass);
319 static void g_mime_param_list_finalize (GObject *object);
320 
321 
322 static GObjectClass *list_parent_class = NULL;
323 
324 
325 GType
g_mime_param_list_get_type(void)326 g_mime_param_list_get_type (void)
327 {
328 	static GType type = 0;
329 
330 	if (!type) {
331 		static const GTypeInfo info = {
332 			sizeof (GMimeParamListClass),
333 			NULL, /* base_class_init */
334 			NULL, /* base_class_finalize */
335 			(GClassInitFunc) g_mime_param_list_class_init,
336 			NULL, /* class_finalize */
337 			NULL, /* class_data */
338 			sizeof (GMimeParamList),
339 			0,    /* n_preallocs */
340 			(GInstanceInitFunc) g_mime_param_list_init,
341 		};
342 
343 		type = g_type_register_static (G_TYPE_OBJECT, "GMimeParamList", &info, 0);
344 	}
345 
346 	return type;
347 }
348 
349 
350 static void
g_mime_param_list_class_init(GMimeParamListClass * klass)351 g_mime_param_list_class_init (GMimeParamListClass *klass)
352 {
353 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
354 
355 	list_parent_class = g_type_class_ref (G_TYPE_OBJECT);
356 
357 	object_class->finalize = g_mime_param_list_finalize;
358 }
359 
360 static void
g_mime_param_list_init(GMimeParamList * list,GMimeParamListClass * klass)361 g_mime_param_list_init (GMimeParamList *list, GMimeParamListClass *klass)
362 {
363 	list->changed = g_mime_event_new (list);
364 	list->array = g_ptr_array_new ();
365 }
366 
367 static void
g_mime_param_list_finalize(GObject * object)368 g_mime_param_list_finalize (GObject *object)
369 {
370 	GMimeParamList *list = (GMimeParamList *) object;
371 	GMimeParam *param;
372 	guint i;
373 
374 	for (i = 0; i < list->array->len; i++) {
375 		param = (GMimeParam *) list->array->pdata[i];
376 		g_mime_event_remove (param->changed, (GMimeEventCallback) param_changed, list);
377 		g_object_unref (param);
378 	}
379 
380 	g_ptr_array_free (list->array, TRUE);
381 	g_mime_event_free (list->changed);
382 
383 	G_OBJECT_CLASS (list_parent_class)->finalize (object);
384 }
385 
386 
387 /**
388  * g_mime_param_list_new:
389  *
390  * Creates a new Content-Type or Content-Disposition parameter list.
391  *
392  * Returns: a new #GMimeParamList.
393  **/
394 GMimeParamList *
g_mime_param_list_new(void)395 g_mime_param_list_new (void)
396 {
397 	return g_object_new (GMIME_TYPE_PARAM_LIST, NULL);
398 }
399 
400 
401 /**
402  * g_mime_param_list_length:
403  * @list: a #GMimeParamList
404  *
405  * Gets the length of the list.
406  *
407  * Returns: the number of #GMimeParam items in the list.
408  **/
409 int
g_mime_param_list_length(GMimeParamList * list)410 g_mime_param_list_length (GMimeParamList *list)
411 {
412 	g_return_val_if_fail (GMIME_IS_PARAM_LIST (list), -1);
413 
414 	return list->array->len;
415 }
416 
417 
418 /**
419  * g_mime_param_list_clear:
420  * @list: a #GMimeParamList
421  *
422  * Clears the list of parameters.
423  **/
424 void
g_mime_param_list_clear(GMimeParamList * list)425 g_mime_param_list_clear (GMimeParamList *list)
426 {
427 	GMimeParam *param;
428 	guint i;
429 
430 	g_return_if_fail (GMIME_IS_PARAM_LIST (list));
431 
432 	for (i = 0; i < list->array->len; i++) {
433 		param = (GMimeParam *) list->array->pdata[i];
434 		g_mime_event_remove (param->changed, (GMimeEventCallback) param_changed, list);
435 		g_object_unref (param);
436 	}
437 
438 	g_ptr_array_set_size (list->array, 0);
439 
440 	g_mime_event_emit (list->changed, NULL);
441 }
442 
443 
444 /**
445  * g_mime_param_list_add:
446  * @list: a #GMimeParamList
447  * @param: a #GMimeParam
448  *
449  * Adds a #GMimeParam to the #GMimeParamList.
450  *
451  * Returns: the index of the added #GMimeParam.
452  **/
453 static void
g_mime_param_list_add(GMimeParamList * list,GMimeParam * param)454 g_mime_param_list_add (GMimeParamList *list, GMimeParam *param)
455 {
456 	g_mime_event_add (param->changed, (GMimeEventCallback) param_changed, list);
457 	g_ptr_array_add (list->array, param);
458 }
459 
460 
461 /**
462  * g_mime_param_list_set_parameter:
463  * @list: a #GMimeParamList
464  * @name: The name of the parameter
465  * @value: The parameter value
466  *
467  * Sets the specified parameter to @value.
468  **/
469 void
g_mime_param_list_set_parameter(GMimeParamList * list,const char * name,const char * value)470 g_mime_param_list_set_parameter (GMimeParamList *list, const char *name, const char *value)
471 {
472 	GMimeParam *param;
473 	guint i;
474 
475 	g_return_if_fail (GMIME_IS_PARAM_LIST (list));
476 	g_return_if_fail (name != NULL);
477 	g_return_if_fail (value != NULL);
478 
479 	for (i = 0; i < list->array->len; i++) {
480 		param = list->array->pdata[i];
481 
482 		if (!g_ascii_strcasecmp (param->name, name)) {
483 			g_mime_param_set_value (param, value);
484 			return;
485 		}
486 	}
487 
488 	param = g_mime_param_new ();
489 	param->value = g_strdup (value);
490 	param->name = g_strdup (name);
491 
492 	g_mime_param_list_add (list, param);
493 
494 	g_mime_event_emit (list->changed, NULL);
495 }
496 
497 
498 /**
499  * g_mime_param_list_get_parameter:
500  * @list: list: a #GMimeParamList
501  * @name: the name of the parameter
502  *
503  * Gets the #GMimeParam with the given @name.
504  *
505  * Returns: (transfer none): the requested #GMimeParam.
506  **/
507 GMimeParam *
g_mime_param_list_get_parameter(GMimeParamList * list,const char * name)508 g_mime_param_list_get_parameter (GMimeParamList *list, const char *name)
509 {
510 	GMimeParam *param;
511 	guint i;
512 
513 	g_return_val_if_fail (GMIME_IS_PARAM_LIST (list), NULL);
514 	g_return_val_if_fail (name != NULL, NULL);
515 
516 	for (i = 0; i < list->array->len; i++) {
517 		param = list->array->pdata[i];
518 
519 		if (!g_ascii_strcasecmp (param->name, name))
520 			return param;
521 	}
522 
523 	return NULL;
524 }
525 
526 
527 /**
528  * g_mime_param_list_get_parameter_at:
529  * @list: a #GMimeParamList
530  * @index: the index of the requested parameter
531  *
532  * Gets the #GMimeParam at the specified @index.
533  *
534  * Returns: (transfer none): the #GMimeParam at the specified index.
535  **/
536 GMimeParam *
g_mime_param_list_get_parameter_at(GMimeParamList * list,int index)537 g_mime_param_list_get_parameter_at (GMimeParamList *list, int index)
538 {
539 	g_return_val_if_fail (GMIME_IS_PARAM_LIST (list), NULL);
540 	g_return_val_if_fail (index >= 0, NULL);
541 
542 	if ((guint) index >= list->array->len)
543 		return NULL;
544 
545 	return list->array->pdata[index];
546 }
547 
548 
549 /**
550  * g_mime_param_list_remove:
551  * @list: a #GMimeParamList
552  * @name: the name of the parameter
553  *
554  * Removes a parameter from the #GMimeParamList.
555  *
556  * Returns: %TRUE if the specified parameter was removed or %FALSE otherwise.
557  **/
558 gboolean
g_mime_param_list_remove(GMimeParamList * list,const char * name)559 g_mime_param_list_remove (GMimeParamList *list, const char *name)
560 {
561 	GMimeParam *param;
562 	guint i;
563 
564 	g_return_val_if_fail (GMIME_IS_PARAM_LIST (list), FALSE);
565 	g_return_val_if_fail (name != NULL, FALSE);
566 
567 	for (i = 0; i < list->array->len; i++) {
568 		param = list->array->pdata[i];
569 
570 		if (!g_ascii_strcasecmp (param->name, name)) {
571 			g_mime_event_remove (param->changed, (GMimeEventCallback) param_changed, list);
572 			g_ptr_array_remove_index (list->array, i);
573 			g_object_unref (param);
574 			return TRUE;
575 		}
576 	}
577 
578 	return FALSE;
579 }
580 
581 
582 /**
583  * g_mime_param_list_remove_at:
584  * @list: a #GMimeParamList
585  * @index: index of the param to remove
586  *
587  * Removes a #GMimeParam from the #GMimeParamList at the specified index.
588  *
589  * Returns: %TRUE if a #GMimeParam was removed or %FALSE otherwise.
590  **/
591 gboolean
g_mime_param_list_remove_at(GMimeParamList * list,int index)592 g_mime_param_list_remove_at (GMimeParamList *list, int index)
593 {
594 	GMimeParam *param;
595 
596 	g_return_val_if_fail (GMIME_IS_PARAM_LIST (list), FALSE);
597 	g_return_val_if_fail (index >= 0, FALSE);
598 
599 	if ((guint) index >= list->array->len)
600 		return FALSE;
601 
602 	param = list->array->pdata[index];
603 	g_mime_event_remove (param->changed, (GMimeEventCallback) param_changed, list);
604 	g_ptr_array_remove_index (list->array, index);
605 	g_object_unref (param);
606 
607 	return TRUE;
608 }
609 
610 
611 /* FIXME: I wrote this in a quick & dirty fashion - it may not be 100% correct */
612 static char *
encode_param(GMimeParam * param,GMimeFormatOptions * options,GMimeParamEncodingMethod * method)613 encode_param (GMimeParam *param, GMimeFormatOptions *options, GMimeParamEncodingMethod *method)
614 {
615 	register const unsigned char *inptr = (const unsigned char *) param->value;
616 	GMimeParamEncodingMethod requested;
617 	const unsigned char *start = inptr;
618 	const char *charset = NULL;
619 	iconv_t cd = (iconv_t) -1;
620 	char *outbuf = NULL;
621 	unsigned char c;
622 	char *outstr;
623 	GString *str;
624 
625 	while (*inptr && ((inptr - start) < GMIME_FOLD_LEN)) {
626 		if (*inptr > 127)
627 			break;
628 		inptr++;
629 	}
630 
631 	if (*inptr == '\0') {
632 		*method = GMIME_PARAM_ENCODING_METHOD_DEFAULT;
633 
634 		return g_strdup (param->value);
635 	}
636 
637 	if (param->method == GMIME_PARAM_ENCODING_METHOD_DEFAULT)
638 		requested = g_mime_format_options_get_param_encoding_method (options);
639 	else
640 		requested = param->method;
641 
642 	if (requested == GMIME_PARAM_ENCODING_METHOD_RFC2047) {
643 		*method = GMIME_PARAM_ENCODING_METHOD_RFC2047;
644 
645 		return g_mime_utils_header_encode_text (options, param->value, param->charset);
646 	}
647 
648 	*method = GMIME_PARAM_ENCODING_METHOD_RFC2231;
649 
650 	if (!param->charset) {
651 		if (*inptr > 127)
652 			charset = g_mime_charset_best (param->value, strlen (param->value));
653 
654 		if (!charset)
655 			charset = "us-ascii";
656 	} else {
657 		charset = param->charset;
658 	}
659 
660 	if (g_ascii_strcasecmp (charset, "UTF-8") != 0)
661 		cd = g_mime_iconv_open (charset, "UTF-8");
662 
663 	if (cd != (iconv_t) -1) {
664 		outbuf = g_mime_iconv_strdup (cd, param->value);
665 		g_mime_iconv_close (cd);
666 		if (outbuf == NULL) {
667 			charset = "UTF-8";
668 			inptr = start;
669 		} else {
670 			inptr = (const unsigned char *) outbuf;
671 		}
672 	} else {
673 		charset = "UTF-8";
674 		inptr = start;
675 	}
676 
677 	str = g_string_new (charset);
678 	g_string_append_c (str, '\'');
679 	if (param->lang)
680 		g_string_append (str, param->lang);
681 	g_string_append_c (str, '\'');
682 
683 	while ((c = *inptr++)) {
684 		if (!is_attrchar (c))
685 			g_string_append_printf (str, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
686 		else
687 			g_string_append_c (str, c);
688 	}
689 
690 	g_free (outbuf);
691 
692 	outstr = g_string_free (str, FALSE);
693 
694 	return outstr;
695 }
696 
697 static void
g_string_append_len_quoted(GString * str,const char * text,size_t len)698 g_string_append_len_quoted (GString *str, const char *text, size_t len)
699 {
700 	register const char *inptr = text;
701 	const char *inend = text + len;
702 
703 	g_string_append_c (str, '"');
704 
705 	while (inptr < inend) {
706 		if ((*inptr == '"') || *inptr == '\\')
707 			g_string_append_c (str, '\\');
708 
709 		g_string_append_c (str, *inptr);
710 
711 		inptr++;
712 	}
713 
714 	g_string_append_c (str, '"');
715 }
716 
717 
718 /**
719  * g_mime_param_list_encode:
720  * @list: a #GMimeParamList
721  * @options: a #GMimeFormatOptions or %NULL
722  * @fold: %TRUE if the parameter list should be folded; otherwise, %FALSE
723  * @str: the output string buffer
724  *
725  * Encodes the parameter list into @str, folding lines if required.
726  **/
727 void
g_mime_param_list_encode(GMimeParamList * list,GMimeFormatOptions * options,gboolean fold,GString * str)728 g_mime_param_list_encode (GMimeParamList *list, GMimeFormatOptions *options, gboolean fold, GString *str)
729 {
730 	const char *newline;
731 	guint count, i;
732 	int used;
733 
734 	g_return_if_fail (GMIME_IS_PARAM_LIST (list));
735 	g_return_if_fail (str != NULL);
736 
737 	newline = g_mime_format_options_get_newline (options);
738 	count = list->array->len;
739 	used = str->len;
740 
741 	for (i = 0; i < count; i++) {
742 		GMimeParamEncodingMethod method;
743 		gboolean encoded, toolong;
744 		int here = str->len;
745 		char *value, *inptr;
746 		size_t nlen, vlen;
747 		GMimeParam *param;
748 		int quote = 0;
749 
750 		param = (GMimeParam *) list->array->pdata[i];
751 
752 		if (!param->value)
753 			continue;
754 
755 		if (!(value = encode_param (param, options, &method))) {
756 			w(g_warning ("appending parameter %s=%s violates rfc2184",
757 				     param->name, param->value));
758 			value = g_strdup (param->value);
759 		}
760 
761 		if (method == GMIME_PARAM_ENCODING_METHOD_DEFAULT) {
762 			for (inptr = value; *inptr; inptr++) {
763 				if (!is_attrchar (*inptr) || is_lwsp (*inptr))
764 					quote++;
765 			}
766 
767 			vlen = (size_t) (inptr - value);
768 		} else if (method == GMIME_PARAM_ENCODING_METHOD_RFC2047) {
769 			vlen = strlen (value);
770 			quote = 2;
771 		} else {
772 			vlen = strlen (value);
773 		}
774 
775 		nlen = strlen (param->name);
776 
777 		g_string_append_c (str, ';');
778 		used++;
779 
780 		if (fold && (used + nlen + vlen + quote > GMIME_FOLD_LEN - 1)) {
781 			g_string_append (str, newline);
782 			g_string_append_c (str, '\t');
783 			here = str->len;
784 			used = 1;
785 		} else {
786 			g_string_append_c (str, ' ');
787 			here = str->len;
788 			used++;
789 		}
790 
791 		toolong = nlen + vlen + quote > GMIME_FOLD_LEN - 2;
792 
793 		if (toolong && method == GMIME_PARAM_ENCODING_METHOD_RFC2231) {
794 			/* we need to do special rfc2184 parameter wrapping */
795 			size_t maxlen = GMIME_FOLD_LEN - (nlen + 6);
796 			char *inend;
797 			int n = 0;
798 
799 			inptr = value;
800 			inend = value + vlen;
801 
802 			while (inptr < inend) {
803 				char *ptr = inptr + MIN ((size_t) (inend - inptr), maxlen);
804 
805 				if (ptr < inend) {
806 					/* be careful not to break an encoded char (ie %20) */
807 					char *q = ptr;
808 					int j = 2;
809 
810 					while (j > 0 && q > inptr && *q != '%') {
811 						j--;
812 						q--;
813 					}
814 
815 					if (*q == '%')
816 						ptr = q;
817 				}
818 
819 				if (n != 0) {
820 					g_string_append_c (str, ';');
821 
822 					if (fold) {
823 						g_string_append (str, newline);
824 						g_string_append_c (str, '\t');
825 					} else {
826 						g_string_append_c (str, ' ');
827 					}
828 
829 					here = str->len;
830 					used = 1;
831 				}
832 
833 				g_string_append_printf (str, "%s*%d*=", param->name, n++);
834 				g_string_append_len (str, inptr, (size_t) (ptr - inptr));
835 				used += (str->len - here);
836 
837 				inptr = ptr;
838 			}
839 		} else {
840 			encoded = method == GMIME_PARAM_ENCODING_METHOD_RFC2231;
841 
842 			g_string_append_printf (str, "%s%s=", param->name, encoded ? "*" : "");
843 
844 			if (quote)
845 				g_string_append_len_quoted (str, value, vlen);
846 			else
847 				g_string_append_len (str, value, vlen);
848 
849 			used += (str->len - here);
850 		}
851 
852 		g_free (value);
853 	}
854 
855 	if (fold)
856 		g_string_append (str, newline);
857 }
858 
859 
860 #define INT_OVERFLOW(x,d) (((x) > (INT_MAX / 10)) || ((x) == (INT_MAX / 10) && (d) > (INT_MAX % 10)))
861 
862 static int
decode_int(const char ** in)863 decode_int (const char **in)
864 {
865 	const unsigned char *inptr;
866 	int digit, n = 0;
867 
868 	skip_cfws (in);
869 
870 	inptr = (const unsigned char *) *in;
871 	while (isdigit ((int) *inptr)) {
872 		digit = (*inptr - '0');
873 		if (INT_OVERFLOW (n, digit)) {
874 			while (isdigit ((int) *inptr))
875 				inptr++;
876 			break;
877 		}
878 
879 		n = (n * 10) + digit;
880 
881 		inptr++;
882 	}
883 
884 	*in = (const char *) inptr;
885 
886 	return n;
887 }
888 
889 static char *
decode_quoted_string(const char ** in)890 decode_quoted_string (const char **in)
891 {
892 	const char *start, *inptr = *in;
893 	char *outptr, *out = NULL;
894 	gboolean unescape = FALSE;
895 
896 	skip_cfws (&inptr);
897 
898 	if (*inptr != '"') {
899 		*in = inptr;
900 		return NULL;
901 	}
902 
903 	start = inptr++;
904 
905 	while (*inptr && *inptr != '"') {
906 		if (*inptr++ == '\\' && *inptr) {
907 			unescape = TRUE;
908 			inptr++;
909 		}
910 	}
911 
912 	if (*inptr == '"') {
913 		start++;
914 		out = g_strndup (start, (size_t) (inptr - start));
915 		inptr++;
916 	} else {
917 		/* string wasn't properly quoted */
918 		out = g_strndup (start, (size_t) (inptr - start));
919 	}
920 
921 	*in = inptr;
922 
923 	if (unescape) {
924 		inptr = outptr = out;
925 		while (*inptr) {
926 			if (*inptr == '\\')
927 				inptr++;
928 			if (*inptr)
929 				*outptr++ = *inptr++;
930 		}
931 
932 		*outptr = '\0';
933 	}
934 
935 	return out;
936 }
937 
938 static char *
decode_token(GMimeRfcComplianceMode mode,const char ** in)939 decode_token (GMimeRfcComplianceMode mode, const char **in)
940 {
941 	const char *inptr = *in;
942 	const char *start;
943 
944 	skip_cfws (&inptr);
945 
946 	start = inptr;
947 	if (mode != GMIME_RFC_COMPLIANCE_LOOSE) {
948 		while (is_ttoken (*inptr))
949 			inptr++;
950 	} else {
951 		/* Broken mail clients like to make our lives difficult. Scan
952 		 * for a ';' instead of trusting that the client followed the
953 		 * specification. */
954 		while (*inptr && *inptr != ';')
955 			inptr++;
956 
957 		/* Scan backwards over any trailing lwsp */
958 		while (inptr > start && is_lwsp (inptr[-1]))
959 			inptr--;
960 	}
961 
962 	if (inptr > start) {
963 		*in = inptr;
964 		return g_strndup (start, (size_t) (inptr - start));
965 	} else {
966 		return NULL;
967 	}
968 }
969 
970 static char *
decode_value(GMimeRfcComplianceMode mode,const char ** in)971 decode_value (GMimeRfcComplianceMode mode, const char **in)
972 {
973 	const char *inptr = *in;
974 
975 	skip_cfws (&inptr);
976 	*in = inptr;
977 
978 	if (*inptr == '"') {
979 		return decode_quoted_string (in);
980 	} else if (is_ttoken (*inptr)) {
981 		return decode_token (mode, in);
982 	}
983 
984 	if (mode == GMIME_RFC_COMPLIANCE_LOOSE)
985 		return decode_token (mode, in);
986 
987 	return NULL;
988 }
989 
990 /* This function is basically the same as decode_token()
991  * except that it will not accept *'s which have a special
992  * meaning for rfc2184 params */
993 static char *
decode_param_token(const char ** in)994 decode_param_token (const char **in)
995 {
996 	const char *inptr = *in;
997 	const char *start;
998 
999 	skip_cfws (&inptr);
1000 
1001 	start = inptr;
1002 	while (is_ttoken (*inptr) && *inptr != '*')
1003 		inptr++;
1004 	if (inptr > start) {
1005 		*in = inptr;
1006 		return g_strndup (start, (size_t) (inptr - start));
1007 	} else {
1008 		return NULL;
1009 	}
1010 }
1011 
1012 static gboolean
decode_rfc2184_param(const char ** in,char ** namep,int * part,gboolean * encoded)1013 decode_rfc2184_param (const char **in, char **namep, int *part, gboolean *encoded)
1014 {
1015 	gboolean is_rfc2184 = FALSE;
1016 	const char *inptr = *in;
1017 	char *name;
1018 
1019 	*encoded = FALSE;
1020 	*part = -1;
1021 
1022 	name = decode_param_token (&inptr);
1023 
1024 	skip_cfws (&inptr);
1025 
1026 	if (*inptr == '*') {
1027 		is_rfc2184 = TRUE;
1028 		inptr++;
1029 
1030 		skip_cfws (&inptr);
1031 		if (*inptr == '=') {
1032 			/* form := param*=value */
1033 			*encoded = TRUE;
1034 		} else {
1035 			/* form := param*#=value or param*#*=value */
1036 			*part = decode_int (&inptr);
1037 
1038 			skip_cfws (&inptr);
1039 			if (*inptr == '*') {
1040 				/* form := param*#*=value */
1041 				inptr++;
1042 				*encoded = TRUE;
1043 				skip_cfws (&inptr);
1044 			}
1045 		}
1046 	}
1047 
1048 	*namep = name;
1049 
1050 	if (name)
1051 		*in = inptr;
1052 
1053 	return is_rfc2184;
1054 }
1055 
1056 static gboolean
decode_param(GMimeParserOptions * options,const char ** in,char ** namep,char ** valuep,int * id,const char ** rfc2047_charset,gboolean * encoded,GMimeParamEncodingMethod * method,gint64 offset)1057 decode_param (GMimeParserOptions *options, const char **in, char **namep, char **valuep, int *id,
1058 	      const char **rfc2047_charset, gboolean *encoded, GMimeParamEncodingMethod *method, gint64 offset)
1059 {
1060 	GMimeRfcComplianceMode mode = g_mime_parser_options_get_parameter_compliance_mode (options);
1061 	gboolean is_rfc2184 = FALSE;
1062 	const char *inptr = *in;
1063 	char *name, *value = NULL;
1064 	char *val;
1065 
1066 	*method = GMIME_PARAM_ENCODING_METHOD_DEFAULT;
1067 	*rfc2047_charset = NULL;
1068 
1069 	if ((is_rfc2184 = decode_rfc2184_param (&inptr, &name, id, encoded)))
1070 		*method = GMIME_PARAM_ENCODING_METHOD_RFC2231;
1071 
1072 	if (*inptr == '=') {
1073 		inptr++;
1074 		value = decode_value (mode, &inptr);
1075 
1076 		if (!is_rfc2184 && value) {
1077 			if (strstr (value, "=?") != NULL) {
1078 				/* We (may) have a broken param value that is rfc2047
1079 				 * encoded. Since both Outlook and Netscape/Mozilla do
1080 				 * this, we should handle this case.
1081 				 */
1082 
1083 				if ((val = _g_mime_utils_header_decode_text (options, value, rfc2047_charset, offset))) {
1084 					*method = GMIME_PARAM_ENCODING_METHOD_RFC2047;
1085 					g_free (value);
1086 					value = val;
1087 				}
1088 			}
1089 
1090 			if (!g_utf8_validate (value, -1, NULL)) {
1091 				/* A (broken) mailer has sent us an unencoded 8bit value.
1092 				 * Attempt to save it by assuming it's in the user's
1093 				 * locale and converting to UTF-8 */
1094 
1095 				if ((val = g_mime_iconv_locale_to_utf8 (value))) {
1096 					g_free (value);
1097 					value = val;
1098 				} else {
1099 					d(g_warning ("Failed to convert %s param value (\"%s\") to UTF-8: %s",
1100 						     name, value, g_strerror (errno)));
1101 				}
1102 			}
1103 		}
1104 	}
1105 
1106 	if (name && value) {
1107 		*valuep = value;
1108 		*namep = name;
1109 		*in = inptr;
1110 		return TRUE;
1111 	}
1112 
1113 	_g_mime_parser_options_warn (options, offset, GMIME_WARN_INVALID_PARAMETER, name);
1114 	g_free (value);
1115 	g_free (name);
1116 
1117 	return FALSE;
1118 }
1119 
1120 
1121 struct _rfc2184_part {
1122 	char *value;
1123 	int id;
1124 };
1125 
1126 struct _rfc2184_param {
1127 	struct _rfc2184_param *next;
1128 	const char *charset;
1129 	GMimeParam *param;
1130 	GPtrArray *parts;
1131 	char *lang;
1132 };
1133 
1134 static int
rfc2184_sort_cb(const void * v0,const void * v1)1135 rfc2184_sort_cb (const void *v0, const void *v1)
1136 {
1137 	const struct _rfc2184_part *p0 = *((struct _rfc2184_part **) v0);
1138 	const struct _rfc2184_part *p1 = *((struct _rfc2184_part **) v1);
1139 
1140 	return p0->id - p1->id;
1141 }
1142 
1143 #define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
1144 
1145 static size_t
hex_decode(const char * in,size_t len,char * out)1146 hex_decode (const char *in, size_t len, char *out)
1147 {
1148 	register const unsigned char *inptr = (const unsigned char *) in;
1149 	register unsigned char *outptr = (unsigned char *) out;
1150 	const unsigned char *inend = inptr + len;
1151 
1152 	while (inptr < inend) {
1153 		if (*inptr == '%') {
1154 			if (isxdigit (inptr[1]) && isxdigit (inptr[2])) {
1155 				*outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
1156 				inptr += 3;
1157 			} else
1158 				*outptr++ = *inptr++;
1159 		} else
1160 			*outptr++ = *inptr++;
1161 	}
1162 
1163 	*outptr = '\0';
1164 
1165 	return ((char *) outptr) - out;
1166 }
1167 
1168 static const char *
rfc2184_param_charset(const char ** in,char ** langp)1169 rfc2184_param_charset (const char **in, char **langp)
1170 {
1171 	const char *inptr = *in;
1172 	const char *start = *in;
1173 	const char *lang;
1174 	char *charset;
1175 	size_t len;
1176 
1177 	while (*inptr && *inptr != '\'')
1178 		inptr++;
1179 
1180 	if (*inptr != '\'') {
1181 		*langp = NULL;
1182 		return NULL;
1183 	}
1184 
1185 	len = (size_t) (inptr - start);
1186 	charset = g_alloca (len + 1);
1187 	memcpy (charset, start, len);
1188 	charset[len] = '\0';
1189 
1190 	lang = ++inptr;
1191 	while (*inptr && *inptr != '\'')
1192 		inptr++;
1193 
1194 	if (*inptr == '\'') {
1195 		if (inptr > lang)
1196 			*langp = g_strndup (lang, (size_t) (inptr - lang));
1197 		else
1198 			*langp = NULL;
1199 		inptr++;
1200 	} else {
1201 		*langp = NULL;
1202 	}
1203 
1204 	*in = inptr;
1205 
1206 	return g_mime_charset_canon_name (charset);
1207 }
1208 
1209 static char *
charset_convert(const char * charset,char * in,size_t inlen)1210 charset_convert (const char *charset, char *in, size_t inlen)
1211 {
1212 	gboolean locale = FALSE;
1213 	char *result = NULL;
1214 	iconv_t cd;
1215 
1216 	if (!charset || !g_ascii_strcasecmp (charset, "UTF-8") || !g_ascii_strcasecmp (charset, "us-ascii")) {
1217 		/* we shouldn't need any charset conversion here... */
1218 		if (g_utf8_validate (in, inlen, NULL))
1219 			return in;
1220 
1221 		charset = g_mime_locale_charset ();
1222 		locale = TRUE;
1223 	}
1224 
1225 	/* need charset conversion */
1226 	cd = g_mime_iconv_open ("UTF-8", charset);
1227 	if (cd == (iconv_t) -1 && !locale) {
1228 		charset = g_mime_locale_charset ();
1229 		cd = g_mime_iconv_open ("UTF-8", charset);
1230 	}
1231 
1232 	if (cd != (iconv_t) -1) {
1233 		result = g_mime_iconv_strndup (cd, in, inlen);
1234 		g_mime_iconv_close (cd);
1235 	}
1236 
1237 	if (result == NULL)
1238 		result = in;
1239 	else
1240 		g_free (in);
1241 
1242 	return result;
1243 }
1244 
1245 static char *
rfc2184_decode(const char * value,char ** charsetp,char ** langp)1246 rfc2184_decode (const char *value, char **charsetp, char **langp)
1247 {
1248 	const char *inptr = value;
1249 	const char *charset;
1250 	char *decoded;
1251 	size_t len;
1252 
1253 	charset = rfc2184_param_charset (&inptr, langp);
1254 	*charsetp = charset ? g_strdup (charset) : NULL;
1255 
1256 	len = strlen (inptr);
1257 	decoded = g_malloc (len + 1);
1258 	len = hex_decode (inptr, len, decoded);
1259 
1260 	return charset_convert (charset, decoded, len);
1261 }
1262 
1263 static void
rfc2184_param_add_part(struct _rfc2184_param * rfc2184,char * value,int id,gboolean encoded)1264 rfc2184_param_add_part (struct _rfc2184_param *rfc2184, char *value, int id, gboolean encoded)
1265 {
1266 	struct _rfc2184_part *part;
1267 	size_t len;
1268 
1269 	part = g_new (struct _rfc2184_part, 1);
1270 	g_ptr_array_add (rfc2184->parts, part);
1271 	part->id = id;
1272 
1273 	if (encoded) {
1274 		len = strlen (value);
1275 		part->value = g_malloc (len + 1);
1276 		hex_decode (value, len, part->value);
1277 		g_free (value);
1278 	} else {
1279 		part->value = value;
1280 	}
1281 }
1282 
1283 static struct _rfc2184_param *
rfc2184_param_new(char * name,char * value,int id,gboolean encoded)1284 rfc2184_param_new (char *name, char *value, int id, gboolean encoded)
1285 {
1286 	struct _rfc2184_param *rfc2184;
1287 	const char *inptr = value;
1288 
1289 	rfc2184 = g_new (struct _rfc2184_param, 1);
1290 	rfc2184->parts = g_ptr_array_new ();
1291 	rfc2184->next = NULL;
1292 
1293 	if (encoded) {
1294 		rfc2184->charset = rfc2184_param_charset (&inptr, &rfc2184->lang);
1295 	} else {
1296 		rfc2184->charset = NULL;
1297 		rfc2184->lang = NULL;
1298 	}
1299 
1300 	if (inptr == value) {
1301 		rfc2184_param_add_part (rfc2184, value, id, encoded);
1302 	} else {
1303 		rfc2184_param_add_part (rfc2184, g_strdup (inptr), id, encoded);
1304 		g_free (value);
1305 	}
1306 
1307 	rfc2184->param = g_mime_param_new ();
1308 	rfc2184->param->method = GMIME_PARAM_ENCODING_METHOD_RFC2231;
1309 	rfc2184->param->name = name;
1310 
1311 	return rfc2184;
1312 }
1313 
1314 static GMimeParamList *
decode_param_list(GMimeParserOptions * options,const char * in,gint64 offset)1315 decode_param_list (GMimeParserOptions *options, const char *in, gint64 offset)
1316 {
1317 	gboolean can_warn = g_mime_parser_options_get_warning_callback (options) != NULL;
1318 	struct _rfc2184_param *rfc2184, *list, *t;
1319 	char *name, *value, *charset, *lang;
1320 	GMimeParamEncodingMethod method;
1321 	const char *rfc2047_charset;
1322 	struct _rfc2184_part *part;
1323 	GHashTable *rfc2184_hash;
1324 	const char *inptr = in;
1325 	GMimeParamList *params;
1326 	GMimeParam *param;
1327 	gboolean encoded;
1328 	GString *buf;
1329 	guint i;
1330 	int id;
1331 
1332 	params = g_mime_param_list_new ();
1333 
1334 	list = NULL;
1335 	t = (struct _rfc2184_param *) &list;
1336 	rfc2184_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
1337 
1338 	skip_cfws (&inptr);
1339 
1340 	do {
1341 		/* invalid format? */
1342 		if (!decode_param (options, &inptr, &name, &value, &id, &rfc2047_charset, &encoded, &method, offset)) {
1343 			skip_cfws (&inptr);
1344 
1345 			if (*inptr == ';')
1346 				continue;
1347 
1348 			break;
1349 		}
1350 
1351 		if (id != -1) {
1352 			/* we have a multipart rfc2184 param */
1353 			if (!(rfc2184 = g_hash_table_lookup (rfc2184_hash, name))) {
1354 				rfc2184 = rfc2184_param_new (name, value, id, encoded);
1355 				param = rfc2184->param;
1356 				t->next = rfc2184;
1357 				t = rfc2184;
1358 
1359 				g_hash_table_insert (rfc2184_hash, param->name, rfc2184);
1360 				g_mime_param_list_add (params, param);
1361 			} else {
1362 				rfc2184_param_add_part (rfc2184, value, id, encoded);
1363 				g_free (name);
1364 			}
1365 		} else {
1366 			param = g_mime_param_new ();
1367 			param->name = name;
1368 
1369 			if (encoded) {
1370 				/* singleton encoded rfc2184 param value */
1371 				param->value = rfc2184_decode (value, &charset, &lang);
1372 				param->charset = charset;
1373 				param->method = method;
1374 				param->lang = lang;
1375 				g_free (value);
1376 			} else {
1377 				/* normal parameter value */
1378 				param->charset = rfc2047_charset ? g_strdup (rfc2047_charset) : NULL;
1379 				param->method = method;
1380 				param->value = value;
1381 			}
1382 
1383 			g_mime_param_list_add (params, param);
1384 		}
1385 
1386 		skip_cfws (&inptr);
1387 	} while (*inptr++ == ';');
1388 
1389 	g_hash_table_destroy (rfc2184_hash);
1390 
1391 	rfc2184 = list;
1392 	while (rfc2184 != NULL) {
1393 		t = rfc2184->next;
1394 
1395 		param = rfc2184->param;
1396 		buf = g_string_new ("");
1397 
1398 		g_ptr_array_sort (rfc2184->parts, rfc2184_sort_cb);
1399 		for (i = 0; i < rfc2184->parts->len; i++) {
1400 			part = rfc2184->parts->pdata[i];
1401 			g_string_append (buf, part->value);
1402 			g_free (part->value);
1403 			g_free (part);
1404 		}
1405 
1406 		g_ptr_array_free (rfc2184->parts, TRUE);
1407 
1408 		param->value = charset_convert (rfc2184->charset, buf->str, buf->len);
1409 		param->charset = rfc2184->charset ? g_strdup (rfc2184->charset) : NULL;
1410 		param->lang = rfc2184->lang;
1411 
1412 		g_string_free (buf, FALSE);
1413 		g_free (rfc2184);
1414 		rfc2184 = t;
1415 	}
1416 
1417 	if (can_warn) {
1418 		GMimeParam *p;
1419 		guint j;
1420 
1421 		for (i = 0; i < params->array->len; i++) {
1422 			param = params->array->pdata[i];
1423 
1424 			for (j = i + 1; j < params->array->len; j++) {
1425 				p = params->array->pdata[j];
1426 
1427 				if (g_ascii_strcasecmp (param->name, p->name) != 0)
1428 					continue;
1429 
1430 				if (strcmp (param->value, p->value) != 0)
1431 					_g_mime_parser_options_warn (options, offset, GMIME_CRIT_CONFLICTING_PARAMETER, param->name);
1432 				else
1433 					_g_mime_parser_options_warn (options, offset, GMIME_WARN_DUPLICATED_PARAMETER, param->name);
1434 
1435 				break;
1436 			}
1437 		}
1438 	}
1439 
1440 	return params;
1441 }
1442 
1443 
1444 /**
1445  * g_mime_param_list_parse:
1446  * @options: a #GMimeParserOptions or %NULL
1447  * @str: a string to parse
1448  *
1449  * Parses the input string into a parameter list.
1450  *
1451  * Returns: (transfer full): a new #GMimeParamList.
1452  **/
1453 GMimeParamList *
g_mime_param_list_parse(GMimeParserOptions * options,const char * str)1454 g_mime_param_list_parse (GMimeParserOptions *options, const char *str)
1455 {
1456 	return _g_mime_param_list_parse (options, str, -1);
1457 }
1458 
1459 GMimeParamList *
_g_mime_param_list_parse(GMimeParserOptions * options,const char * str,gint64 offset)1460 _g_mime_param_list_parse (GMimeParserOptions *options, const char *str, gint64 offset)
1461 {
1462 	g_return_val_if_fail (str != NULL, NULL);
1463 
1464 	return decode_param_list (options, str, offset);
1465 }
1466