Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-param.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 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-table-private.h"
34 #include "gmime-parse-utils.h"
35 #include "gmime-iconv-utils.h"
36 #include "gmime-charset.h"
37 #include "gmime-utils.h"
38 #include "gmime-iconv.h"
39
40
41 #ifdef ENABLE_WARNINGS
42 #define w(x) x
43 #else
44 #define w(x)
45 #endif /* ENABLE_WARNINGS */
46
47 #define d(x)
48
49
50 /**
51  * SECTION: gmime-param
52  * @title: GMimeParam
53  * @short_description: Content-Type and Content-Disposition parameters
54  * @see_also: #GMimeContentType
55  *
56  * A #GMimeParam is a parameter name/value pair as found on MIME
57  * header fields such as Content-Type and Content-Disposition.
58  **/
59
60
61 static unsigned char tohex[16] = {
62         '0', '1', '2', '3', '4', '5', '6', '7',
63         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
64 };
65
66
67 /**
68  * g_mime_param_new:
69  * @name: parameter name
70  * @value: parameter value
71  *
72  * Creates a new #GMimeParam node with name @name and value @value.
73  *
74  * Returns: a new paramter structure.
75  **/
76 GMimeParam *
77 g_mime_param_new (const char *name, const char *value)
78 {
79         GMimeParam *param;
80         
81         param = g_new (GMimeParam, 1);
82         
83         param->next = NULL;
84         param->name = g_strdup (name);
85         param->value = g_strdup (value);
86         
87         return param;
88 }
89
90 #define INT_OVERFLOW(x,d) (((x) > (INT_MAX / 10)) || ((x) == (INT_MAX / 10) && (d) > (INT_MAX % 10)))
91
92 static int
93 decode_int (const char **in)
94 {
95         const unsigned char *inptr;
96         int digit, n = 0;
97         
98         decode_lwsp (in);
99         
100         inptr = (const unsigned char *) *in;
101         while (isdigit ((int) *inptr)) {
102                 digit = (*inptr - '0');
103                 if (INT_OVERFLOW (n, digit)) {
104                         while (isdigit ((int) *inptr))
105                                 inptr++;
106                         break;
107                 }
108                 
109                 n = (n * 10) + digit;
110                 
111                 inptr++;
112         }
113         
114         *in = (const char *) inptr;
115         
116         return n;
117 }
118
119 static char *
120 decode_quoted_string (const char **in)
121 {
122         const char *start, *inptr = *in;
123         char *outptr, *out = NULL;
124         gboolean unescape = FALSE;
125         
126         decode_lwsp (&inptr);
127         
128         if (*inptr != '"') {
129                 *in = inptr;
130                 return NULL;
131         }
132         
133         start = inptr++;
134         
135         while (*inptr && *inptr != '"') {
136                 if (*inptr++ == '\\') {
137                         unescape = TRUE;
138                         inptr++;
139                 }
140         }
141         
142         if (*inptr == '"') {
143                 start++;
144                 out = g_strndup (start, (size_t) (inptr - start));
145                 inptr++;
146         } else {
147                 /* string wasn't properly quoted */
148                 out = g_strndup (start, (size_t) (inptr - start));
149         }
150         
151         *in = inptr;
152         
153         if (unescape) {
154                 inptr = outptr = out;
155                 while (*inptr) {
156                         if (*inptr == '\\')
157                                 inptr++;
158                         *outptr++ = *inptr++;
159                 }
160                 
161                 *outptr = '\0';
162         }
163         
164         return out;
165 }
166
167 static char *
168 decode_token (const char **in)
169 {
170         const char *inptr = *in;
171         const char *start;
172         
173         decode_lwsp (&inptr);
174         
175         start = inptr;
176 #ifdef STRICT_PARSER
177         while (is_ttoken (*inptr))
178                 inptr++;
179 #else
180         /* Broken mail clients like to make our lives difficult. Scan
181          * for a ';' instead of trusting that the client followed the
182          * specification. */
183         while (*inptr && *inptr != ';')
184                 inptr++;
185         
186         /* Scan backwards over any trailing lwsp */
187         while (inptr > start && is_lwsp (inptr[-1]))
188                 inptr--;
189 #endif
190         
191         if (inptr > start) {
192                 *in = inptr;
193                 return g_strndup (start, (size_t) (inptr - start));
194         } else {
195                 return NULL;
196         }
197 }
198
199 static char *
200 decode_value (const char **in)
201 {
202         const char *inptr = *in;
203         
204         decode_lwsp (&inptr);
205         *in = inptr;
206         
207         if (*inptr == '"') {
208                 return decode_quoted_string (in);
209         } else if (is_ttoken (*inptr)) {
210                 return decode_token (in);
211         }
212         
213 #ifndef STRICT_PARSER
214         return decode_token (in);
215 #else
216         return NULL;
217 #endif
218 }
219
220 /* This function is basically the same as decode_token()
221  * except that it will not accept *'s which have a special
222  * meaning for rfc2184 params */
223 static char *
224 decode_param_token (const char **in)
225 {
226         const char *inptr = *in;
227         const char *start;
228         
229         decode_lwsp (&inptr);
230         
231         start = inptr;
232         while (is_ttoken (*inptr) && *inptr != '*')
233                 inptr++;
234         if (inptr > start) {
235                 *in = inptr;
236                 return g_strndup (start, (size_t) (inptr - start));
237         } else {
238                 return NULL;
239         }
240 }
241
242 static gboolean
243 decode_rfc2184_param (const char **in, char **paramp, int *part, gboolean *encoded)
244 {
245         gboolean is_rfc2184 = FALSE;
246         const char *inptr = *in;
247         char *param;
248         
249         *encoded = FALSE;
250         *part = -1;
251         
252         param = decode_param_token (&inptr);
253         
254         decode_lwsp (&inptr);
255         
256         if (*inptr == '*') {
257                 is_rfc2184 = TRUE;
258                 inptr++;
259                 
260                 decode_lwsp (&inptr);
261                 if (*inptr == '=') {
262                         /* form := param*=value */
263                         *encoded = TRUE;
264                 } else {
265                         /* form := param*#=value or param*#*=value */
266                         *part = decode_int (&inptr);
267                         
268                         decode_lwsp (&inptr);
269                         if (*inptr == '*') {
270                                 /* form := param*#*=value */
271                                 inptr++;
272                                 *encoded = TRUE;
273                                 decode_lwsp (&inptr);
274                         }
275                 }
276         }
277         
278         if (paramp)
279                 *paramp = param;
280         
281         if (param)
282                 *in = inptr;
283         
284         return is_rfc2184;
285 }
286
287 static gboolean
288 decode_param (const char **in, char **paramp, char **valuep, int *id, gboolean *encoded)
289 {
290         gboolean is_rfc2184 = FALSE;
291         const char *inptr = *in;
292         char *param, *value = NULL;
293         char *val;
294         
295         is_rfc2184 = decode_rfc2184_param (&inptr, &param, id, encoded);
296         
297         if (*inptr == '=') {
298                 inptr++;
299                 value = decode_value (&inptr);
300                 
301                 if (!is_rfc2184 && value) {
302                         if (strstr (value, "=?") != NULL) {
303                                 /* We (may) have a broken param value that is rfc2047
304                                  * encoded. Since both Outlook and Netscape/Mozilla do
305                                  * this, we should handle this case.
306                                  */
307                                 
308                                 if ((val = g_mime_utils_header_decode_text (value))) {
309                                         g_free (value);
310                                         value = val;
311                                 }
312                         }
313                         
314                         if (!g_utf8_validate (value, -1, NULL)) {
315                                 /* A (broken) mailer has sent us an unencoded 8bit value.
316                                  * Attempt to save it by assuming it's in the user's
317                                  * locale and converting to UTF-8 */
318                                 
319                                 if ((val = g_mime_iconv_locale_to_utf8 (value))) {
320                                         g_free (value);
321                                         value = val;
322                                 } else {
323                                         d(g_warning ("Failed to convert %s param value (\"%s\") to UTF-8: %s",
324                                                      param, value, g_strerror (errno)));
325                                 }
326                         }
327                 }
328         }
329         
330         if (param && value) {
331                 *paramp = param;
332                 *valuep = value;
333                 *in = inptr;
334                 return TRUE;
335         } else {
336                 g_free (param);
337                 g_free (value);
338                 return FALSE;
339         }
340 }
341
342
343 struct _rfc2184_part {
344         char *value;
345         int id;
346 };
347
348 struct _rfc2184_param {
349         struct _rfc2184_param *next;
350         const char *charset;
351         GMimeParam *param;
352         GPtrArray *parts;
353         char *lang;
354 };
355
356 static int
357 rfc2184_sort_cb (const void *v0, const void *v1)
358 {
359         const struct _rfc2184_part *p0 = *((struct _rfc2184_part **) v0);
360         const struct _rfc2184_part *p1 = *((struct _rfc2184_part **) v1);
361         
362         return p0->id - p1->id;
363 }
364
365 #define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
366
367 static size_t
368 hex_decode (const char *in, size_t len, char *out)
369 {
370         register const unsigned char *inptr = (const unsigned char *) in;
371         register unsigned char *outptr = (unsigned char *) out;
372         const unsigned char *inend = inptr + len;
373         
374         while (inptr < inend) {
375                 if (*inptr == '%') {
376                         if (isxdigit (inptr[1]) && isxdigit (inptr[2])) {
377                                 *outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
378                                 inptr += 3;
379                         } else
380                                 *outptr++ = *inptr++;
381                 } else
382                         *outptr++ = *inptr++;
383         }
384         
385         *outptr = '\0';
386         
387         return ((char *) outptr) - out;
388 }
389
390 static const char *
391 rfc2184_param_charset (const char **in, char **langp)
392 {
393         const char *lang, *inptr = *in;
394         char *charset;
395         size_t len;
396         
397         if (langp)
398                 *langp = NULL;
399         
400         while (*inptr != '\0' && *inptr != '\'')
401                 inptr++;
402         
403         if (*inptr != '\'')
404                 return NULL;
405         
406         len = inptr - *in;
407         charset = g_alloca (len + 1);
408         memcpy (charset, *in, len);
409         charset[len] = '\0';
410         
411         lang = ++inptr;
412         while (*inptr != '\0' && *inptr != '\'')
413                 inptr++;
414         
415         if (*inptr == '\'') {
416                 if (langp)
417                         *langp = g_strndup (lang, (size_t) (inptr - lang));
418                 
419                 inptr++;
420         }
421         
422         *in = inptr;
423         
424         return g_mime_charset_canon_name (charset);
425 }
426
427 static char *
428 charset_convert (const char *charset, char *in, size_t inlen)
429 {
430         gboolean locale = FALSE;
431         char *result = NULL;
432         iconv_t cd;
433         
434         if (!charset || !g_ascii_strcasecmp (charset, "UTF-8") || !g_ascii_strcasecmp (charset, "us-ascii")) {
435                 /* we shouldn't need any charset conversion here... */
436                 if (g_utf8_validate (in, inlen, NULL))
437                         return in;
438                 
439                 charset = g_mime_locale_charset ();
440                 locale = TRUE;
441         }
442         
443         /* need charset conversion */
444         cd = g_mime_iconv_open ("UTF-8", charset);
445         if (cd == (iconv_t) -1 && !locale) {
446                 charset = g_mime_locale_charset ();
447                 cd = g_mime_iconv_open ("UTF-8", charset);
448         }
449         
450         if (cd != (iconv_t) -1) {
451                 result = g_mime_iconv_strndup (cd, in, inlen);
452                 g_mime_iconv_close (cd);
453         }
454         
455         if (result == NULL)
456                 result = in;
457         else
458                 g_free (in);
459         
460         return result;
461 }
462
463 static char *
464 rfc2184_decode (const char *value)
465 {
466         const char *inptr = value;
467         const char *charset;
468         char *decoded;
469         size_t len;
470         
471         charset = rfc2184_param_charset (&inptr, NULL);
472         
473         len = strlen (inptr);
474         decoded = g_alloca (len + 1);
475         len = hex_decode (inptr, len, decoded);
476         
477         return charset_convert (charset, g_strdup (decoded), len);
478 }
479
480 static void
481 rfc2184_param_add_part (struct _rfc2184_param *rfc2184, char *value, int id, gboolean encoded)
482 {
483         struct _rfc2184_part *part;
484         size_t len;
485         
486         part = g_new (struct _rfc2184_part, 1);
487         g_ptr_array_add (rfc2184->parts, part);
488         part->id = id;
489         
490         if (encoded) {
491                 len = strlen (value);
492                 part->value = g_malloc (len + 1);
493                 hex_decode (value, len, part->value);
494                 g_free (value);
495         } else {
496                 part->value = value;
497         }
498 }
499
500 static struct _rfc2184_param *
501 rfc2184_param_new (char *name, char *value, int id, gboolean encoded)
502 {
503         struct _rfc2184_param *rfc2184;
504         const char *inptr = value;
505         
506         rfc2184 = g_new (struct _rfc2184_param, 1);
507         rfc2184->parts = g_ptr_array_new ();
508         rfc2184->next = NULL;
509         
510         if (encoded) {
511                 rfc2184->charset = rfc2184_param_charset (&inptr, &rfc2184->lang);
512         } else {
513                 rfc2184->charset = NULL;
514                 rfc2184->lang = NULL;
515         }
516         
517         if (inptr == value) {
518                 rfc2184_param_add_part (rfc2184, value, id, encoded);
519         } else {
520                 rfc2184_param_add_part (rfc2184, g_strdup (inptr), id, encoded);
521                 g_free (value);
522         }
523         
524         rfc2184->param = g_new (GMimeParam, 1);
525         rfc2184->param->next = NULL;
526         rfc2184->param->name = name;
527         rfc2184->param->value = NULL;
528         
529         return rfc2184;
530 }
531
532 static GMimeParam *
533 decode_param_list (const char *in)
534 {
535         struct _rfc2184_param *rfc2184, *list, *t;
536         GMimeParam *param, *params, *tail;
537         struct _rfc2184_part *part;
538         GHashTable *rfc2184_hash;
539         const char *inptr = in;
540         char *name, *value;
541         gboolean encoded;
542         GString *gvalue;
543         guint i;
544         int id;
545         
546         params = NULL;
547         tail = (GMimeParam *) &params;
548         
549         list = NULL;
550         t = (struct _rfc2184_param *) &list;
551         rfc2184_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
552         
553         decode_lwsp (&inptr);
554         
555         do {
556                 /* invalid format? */
557                 if (!decode_param (&inptr, &name, &value, &id, &encoded)) {
558                         decode_lwsp (&inptr);
559                         
560                         if (*inptr == ';')
561                                 continue;
562                         
563                         break;
564                 }
565                 
566                 if (id != -1) {
567                         /* we have a multipart rfc2184 param */
568                         if (!(rfc2184 = g_hash_table_lookup (rfc2184_hash, name))) {
569                                 rfc2184 = rfc2184_param_new (name, value, id, encoded);
570                                 param = rfc2184->param;
571                                 t->next = rfc2184;
572                                 t = rfc2184;
573                                 
574                                 g_hash_table_insert (rfc2184_hash, param->name, rfc2184);
575                                 
576                                 tail->next = param;
577                                 tail = param;
578                         } else {
579                                 rfc2184_param_add_part (rfc2184, value, id, encoded);
580                                 g_free (name);
581                         }
582                 } else {
583                         param = g_new (GMimeParam, 1);
584                         param->next = NULL;
585                         param->name = name;
586                         
587                         if (encoded) {
588                                 /* singleton encoded rfc2184 param value */
589                                 param->value = rfc2184_decode (value);
590                                 g_free (value);
591                         } else {
592                                 /* normal parameter value */
593                                 param->value = value;
594                         }
595                         
596                         tail->next = param;
597                         tail = param;
598                 }
599                 
600                 decode_lwsp (&inptr);
601         } while (*inptr++ == ';');
602         
603         g_hash_table_destroy (rfc2184_hash);
604         
605         rfc2184 = list;
606         while (rfc2184 != NULL) {
607                 t = rfc2184->next;
608                 
609                 param = rfc2184->param;
610                 gvalue = g_string_new ("");
611                 
612                 g_ptr_array_sort (rfc2184->parts, rfc2184_sort_cb);
613                 for (i = 0; i < rfc2184->parts->len; i++) {
614                         part = rfc2184->parts->pdata[i];
615                         g_string_append (gvalue, part->value);
616                         g_free (part->value);
617                         g_free (part);
618                 }
619                 
620                 g_ptr_array_free (rfc2184->parts, TRUE);
621                 
622                 param->value = charset_convert (rfc2184->charset, gvalue->str, gvalue->len);
623                 g_string_free (gvalue, FALSE);
624                 
625                 g_free (rfc2184->lang);
626                 g_free (rfc2184);
627                 rfc2184 = t;
628         }
629         
630         return params;
631 }
632
633
634 /**
635  * g_mime_param_new_from_string:
636  * @str: input string
637  *
638  * Creates a parameter list based on the input string.
639  *
640  * Returns: a #GMimeParam structure based on @string.
641  **/
642 GMimeParam *
643 g_mime_param_new_from_string (const char *str)
644 {
645         g_return_val_if_fail (str != NULL, NULL);
646         
647         return decode_param_list (str);
648 }
649
650
651 /**
652  * g_mime_param_destroy:
653  * @param: Mime param list to destroy
654  *
655  * Releases all memory used by this mime param back to the Operating
656  * System.
657  **/
658 void
659 g_mime_param_destroy (GMimeParam *param)
660 {
661         GMimeParam *next;
662         
663         while (param) {
664                 next = param->next;
665                 g_free (param->name);
666                 g_free (param->value);
667                 g_free (param);
668                 param = next;
669         }
670 }
671
672
673 /**
674  * g_mime_param_next:
675  * @param: a #GMimeParam node
676  *
677  * Gets the next #GMimeParam node in the list.
678  *
679  * Returns: the next #GMimeParam node in the list.
680  **/
681 const GMimeParam *
682 g_mime_param_next (const GMimeParam *param)
683 {
684         g_return_val_if_fail (param != NULL, NULL);
685         
686         return param->next;
687 }
688
689
690 /**
691  * g_mime_param_get_name:
692  * @param: a #GMimeParam
693  *
694  * Gets the name of the parameter.
695  *
696  * Returns: the name of the parameter.
697  **/
698 const char *
699 g_mime_param_get_name (const GMimeParam *param)
700 {
701         g_return_val_if_fail (param != NULL, NULL);
702         
703         return param->name;
704 }
705
706
707 /**
708  * g_mime_param_get_value:
709  * @param: a #GMimeParam
710  *
711  * Gets the value of the parameter.
712  *
713  * Returns: the value of the parameter.
714  **/
715 const char *
716 g_mime_param_get_value (const GMimeParam *param)
717 {
718         g_return_val_if_fail (param != NULL, NULL);
719         
720         return param->value;
721 }
722
723
724 /**
725  * g_mime_param_append:
726  * @params: param list
727  * @name: new param name
728  * @value: new param value
729  *
730  * Appends a new parameter with name @name and value @value to the
731  * parameter list @params.
732  *
733  * Returns: a param list with the new param of name @name and value
734  * @value appended to the list of params @params.
735  **/
736 GMimeParam *
737 g_mime_param_append (GMimeParam *params, const char *name, const char *value)
738 {
739         GMimeParam *param, *p;
740         
741         g_return_val_if_fail (name != NULL, params);
742         g_return_val_if_fail (value != NULL, params);
743         
744         param = g_mime_param_new (name, value);
745         if (params) {
746                 p = params;
747                 while (p->next)
748                         p = p->next;
749                 p->next = param;
750         } else
751                 params = param;
752         
753         return params;
754 }
755
756
757 /**
758  * g_mime_param_append_param:
759  * @params: param list
760  * @param: param to append
761  *
762  * Appends @param to the param list @params.
763  *
764  * Returns: a param list with the new param @param appended to the list
765  * of params @params.
766  **/
767 GMimeParam *
768 g_mime_param_append_param (GMimeParam *params, GMimeParam *param)
769 {
770         GMimeParam *p;
771         
772         g_return_val_if_fail (param != NULL, params);
773         
774         if (params) {
775                 p = params;
776                 while (p->next)
777                         p = p->next;
778                 p->next = param;
779         } else
780                 params = param;
781         
782         return params;
783 }
784
785 /* FIXME: I wrote this in a quick & dirty fasion - it may not be 100% correct */
786 static char *
787 encode_param (const char *in, gboolean *encoded)
788 {
789         register const unsigned char *inptr = (const unsigned char *) in;
790         const unsigned char *instart = inptr;
791         iconv_t cd = (iconv_t) -1;
792         const char *charset = NULL;
793         char *outbuf = NULL;
794         unsigned char c;
795         char *outstr;
796         GString *out;
797         
798         *encoded = FALSE;
799         
800         while (*inptr && ((inptr - instart) < GMIME_FOLD_LEN)) {
801                 if (*inptr > 127)
802                         break;
803                 inptr++;
804         }
805         
806         if (*inptr == '\0')
807                 return g_strdup (in);
808         
809         if (*inptr > 127)
810                 charset = g_mime_charset_best (in, strlen (in));
811         
812         if (!charset)
813                 charset = "iso-8859-1";
814         
815         if (g_ascii_strcasecmp (charset, "UTF-8") != 0)
816                 cd = g_mime_iconv_open (charset, "UTF-8");
817         
818         if (cd != (iconv_t) -1) {
819                 outbuf = g_mime_iconv_strdup (cd, in);
820                 g_mime_iconv_close (cd);
821                 if (outbuf == NULL) {
822                         charset = "UTF-8";
823                         inptr = instart;
824                 } else {
825                         inptr = (const unsigned char *) outbuf;
826                 }
827         } else {
828                 charset = "UTF-8";
829                 inptr = instart;
830         }
831         
832         /* FIXME: set the 'language' as well, assuming we can get that info...? */
833         out = g_string_new ("");
834         g_string_append_printf (out, "%s''", charset);
835         
836         while ((c = *inptr++)) {
837                 if (!is_attrchar (c))
838                         g_string_append_printf (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
839                 else
840                         g_string_append_c (out, c);
841         }
842         
843         g_free (outbuf);
844         
845         outstr = out->str;
846         g_string_free (out, FALSE);
847         *encoded = TRUE;
848         
849         return outstr;
850 }
851
852 static void
853 g_string_append_len_quoted (GString *out, const char *in, size_t len)
854 {
855         register const char *inptr;
856         const char *inend;
857         
858         g_string_append_c (out, '"');
859         
860         inptr = in;
861         inend = in + len;
862         
863         while (inptr < inend) {
864                 if ((*inptr == '"') || *inptr == '\\')
865                         g_string_append_c (out, '\\');
866                 
867                 g_string_append_c (out, *inptr);
868                 
869                 inptr++;
870         }
871         
872         g_string_append_c (out, '"');
873 }
874
875 static void
876 param_list_format (GString *out, const GMimeParam *param, gboolean fold)
877 {
878         int used = out->len;
879         
880         while (param) {
881                 gboolean encoded = FALSE;
882                 int here = out->len;
883                 size_t nlen, vlen;
884                 int quote = 0;
885                 char *value;
886                 
887                 if (!param->value) {
888                         param = param->next;
889                         continue;
890                 }
891                 
892                 if (!(value = encode_param (param->value, &encoded))) {
893                         w(g_warning ("appending parameter %s=%s violates rfc2184",
894                                      param->name, param->value));
895                         value = g_strdup (param->value);
896                 }
897                 
898                 if (!encoded) {
899                         char *ch;
900                         
901                         for (ch = value; *ch; ch++) {
902                                 if (!is_attrchar (*ch) || is_lwsp (*ch))
903                                         quote++;
904                         }
905                 }
906                 
907                 nlen = strlen (param->name);
908                 vlen = strlen (value);
909                 
910                 if (fold && (used + nlen + vlen + quote > GMIME_FOLD_LEN - 2)) {
911                         g_string_append (out, ";\n\t");
912                         here = out->len;
913                         used = 1;
914                 } else {
915                         g_string_append (out, "; ");
916                         here = out->len;
917                         used += 2;
918                 }
919                 
920                 if (nlen + vlen + quote > GMIME_FOLD_LEN - 2) {
921                         /* we need to do special rfc2184 parameter wrapping */
922                         size_t maxlen = GMIME_FOLD_LEN - (nlen + 6);
923                         char *inptr, *inend;
924                         int i = 0;
925                         
926                         inptr = value;
927                         inend = value + vlen;
928                         
929                         while (inptr < inend) {
930                                 char *ptr = inptr + MIN ((size_t) (inend - inptr), maxlen);
931                                 
932                                 if (encoded && ptr < inend) {
933                                         /* be careful not to break an encoded char (ie %20) */
934                                         char *q = ptr;
935                                         int j = 2;
936                                         
937                                         for ( ; j > 0 && q > inptr && *q != '%'; j--, q--);
938                                         if (*q == '%')
939                                                 ptr = q;
940                                 }
941                                 
942                                 if (i != 0) {
943                                         if (fold)
944                                                 g_string_append (out, ";\n\t");
945                                         else
946                                                 g_string_append (out, "; ");
947                                         
948                                         here = out->len;
949                                         used = 1;
950                                 }
951                                 
952                                 g_string_append_printf (out, "%s*%d%s=", param->name,
953                                                         i++, encoded ? "*" : "");
954                                 
955                                 if (encoded || !quote)
956                                         g_string_append_len (out, inptr, (size_t) (ptr - inptr));
957                                 else
958                                         g_string_append_len_quoted (out, inptr, (size_t) (ptr - inptr));
959                                 
960                                 used += (out->len - here);
961                                 
962                                 inptr = ptr;
963                         }
964                 } else {
965                         g_string_append_printf (out, "%s%s=", param->name, encoded ? "*" : "");
966                         
967                         if (encoded || !quote)
968                                 g_string_append_len (out, value, vlen);
969                         else
970                                 g_string_append_len_quoted (out, value, vlen);
971                         
972                         used += (out->len - here);
973                 }
974                 
975                 g_free (value);
976                 
977                 param = param->next;
978         }
979         
980         if (fold)
981                 g_string_append_c (out, '\n');
982 }
983
984
985 /**
986  * g_mime_param_write_to_string:
987  * @param: MIME Param list
988  * @fold: specifies whether or not to fold headers
989  * @str: output string
990  *
991  * Assumes the output string contains only the Content-* header and
992  * it's immediate value.
993  *
994  * Writes the params out to the string @string.
995  **/
996 void
997 g_mime_param_write_to_string (const GMimeParam *param, gboolean fold, GString *str)
998 {
999         g_return_if_fail (str != NULL);
1000         
1001         param_list_format (str, param, fold);
1002 }