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