Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-header.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 <ctype.h>
28
29 #include "gmime-stream-mem.h"
30 #include "gmime-common.h"
31 #include "gmime-header.h"
32 #include "gmime-events.h"
33 #include "gmime-utils.h"
34
35 #include "list.h"
36
37
38 /**
39  * SECTION: gmime-header
40  * @title: GMimeHeader
41  * @short_description: Message and MIME part headers
42  * @see_also: #GMimeObject
43  *
44  * A #GMimeHeader is a collection of rfc822 header fields and their
45  * values.
46  **/
47
48
49 /**
50  * GMimeHeader:
51  * @next: pointer to the next header
52  * @prev: pointer to the previous header
53  * @offset: file/stream offset
54  * @name: header name
55  * @value: header value
56  *
57  * A message/rfc822 header.
58  **/
59
60 struct _GMimeHeader {
61         GMimeHeader *next;
62         GMimeHeader *prev;
63         /*gint64 offset;*/
64         char *name;
65         char *value;
66 };
67
68 struct _GMimeHeaderList {
69         GMimeStream *stream;
70         GHashTable *writers;
71         GMimeEvent *changed;
72         GHashTable *hash;
73         guint32 version;
74         List list;
75 };
76
77
78 static GMimeHeader *g_mime_header_new (const char *name, const char *value, gint64 offset);
79 static void g_mime_header_free (GMimeHeader *header);
80
81
82 /**
83  * g_mime_header_new:
84  * @name: header name
85  * @value: header value
86  * @offset: file/stream offset for the start of the header (or %-1 if unknown)
87  *
88  * Creates a new #GMimeHeader.
89  *
90  * Returns: a new #GMimeHeader with the specified values.
91  **/
92 static GMimeHeader *
93 g_mime_header_new (const char *name, const char *value, gint64 offset)
94 {
95         GMimeHeader *header;
96         
97         header = g_slice_new (GMimeHeader);
98         header->name = g_strdup (name);
99         header->value = g_strdup (value);
100         /*header->offset = offset;*/
101         header->next = NULL;
102         header->prev = NULL;
103         
104         return header;
105 }
106
107
108 /**
109  * g_mime_header_free:
110  * @header: a #GMimeHeader
111  *
112  * Frees a single #GMimeHeader node.
113  **/
114 static void
115 g_mime_header_free (GMimeHeader *header)
116 {
117         g_free (header->name);
118         g_free (header->value);
119         
120         g_slice_free (GMimeHeader, header);
121 }
122
123
124 /**
125  * g_mime_header_iter_new:
126  *
127  * Dynamically allocates a #GMimeHeaderIter on the heap. This is
128  * intended for use in language bindings but may also be useful in
129  * applications as well. You must free this iter with
130  * g_mime_header_iter_free().
131  *
132  * Returns: a newly-allocated #GMimeHeaderIter.
133  **/
134 GMimeHeaderIter *
135 g_mime_header_iter_new (void)
136 {
137         GMimeHeaderIter *iter;
138         
139         iter = g_slice_new (GMimeHeaderIter);
140         iter->hdrlist = NULL;
141         iter->cursor = NULL;
142         iter->version = 0;
143         
144         return iter;
145 }
146
147
148 /**
149  * g_mime_header_iter_copy:
150  * @iter: a #GMimeHeaderIter
151  *
152  * Creates a dynamically allocated header iterator as a copy of
153  * @iter. You must free this iter with g_mime_header_iter_free().
154  *
155  * Returns: a newly-allocated copy of @iter.
156  **/
157 GMimeHeaderIter *
158 g_mime_header_iter_copy (GMimeHeaderIter *iter)
159 {
160         GMimeHeaderIter *copy;
161         
162         g_return_val_if_fail (iter != NULL, NULL);
163         
164         copy = g_mime_header_iter_new ();
165         memcpy (copy, iter, sizeof (GMimeHeaderIter));
166         
167         return copy;
168 }
169
170
171 /**
172  * g_mime_header_iter_copy_to:
173  * @src: a #GMimeHeaderIter
174  * @dest: a #GMimeHeaderIter
175  *
176  * Copies @src to @dest.
177  **/
178 void
179 g_mime_header_iter_copy_to (GMimeHeaderIter *src, GMimeHeaderIter *dest)
180 {
181         g_return_if_fail (dest != NULL);
182         g_return_if_fail (src != NULL);
183         
184         memcpy (dest, src, sizeof (GMimeHeaderIter));
185 }
186
187
188 /**
189  * g_mime_header_iter_free:
190  * @iter: a #GMimeHeaderIter
191  *
192  * Frees a dynamically-allocated #GMimeHeaderIter as created by
193  * g_mime_header_iter_new() or g_mime_header_iter_copy().
194  **/
195 void
196 g_mime_header_iter_free (GMimeHeaderIter *iter)
197 {
198         g_return_if_fail (iter != NULL);
199         
200         g_slice_free (GMimeHeaderIter, iter);
201 }
202
203
204 /**
205  * g_mime_header_iter_equal:
206  * @iter1: a #GMimeHeaderIter
207  * @iter2: a #GMimeHeaderIter
208  *
209  * Check that @iter1 and @iter2 reference the same header.
210  *
211  * Returns: %TRUE if @iter1 and @iter2 refer to the same header or
212  * %FALSE otherwise.
213  **/
214 gboolean
215 g_mime_header_iter_equal (GMimeHeaderIter *iter1, GMimeHeaderIter *iter2)
216 {
217         g_return_val_if_fail (iter1 != NULL, FALSE);
218         g_return_val_if_fail (iter2 != NULL, FALSE);
219         
220         return iter1->hdrlist == iter2->hdrlist &&
221                 iter1->version == iter2->version &&
222                 iter1->cursor == iter2->cursor;
223 }
224
225
226 /**
227  * g_mime_header_iter_is_valid:
228  * @iter: a #GMimeHeaderIter
229  *
230  * Checks if a #GMimeHeaderIter is valid. An iterator may become
231  * invalid if the #GMimeHeaderList that the iterator refers to
232  * changes.
233  *
234  * Returns: %TRUE if @iter is still valid or %FALSE otherwise.
235  **/
236 gboolean
237 g_mime_header_iter_is_valid (GMimeHeaderIter *iter)
238 {
239         g_return_val_if_fail (iter != NULL, FALSE);
240         
241         if (!iter->hdrlist || iter->version != iter->hdrlist->version)
242                 return FALSE;
243         
244         if (!iter->cursor || !iter->cursor->next)
245                 return FALSE;
246         
247         return TRUE;
248 }
249
250
251 /**
252  * g_mime_header_iter_first:
253  * @iter: a #GMimeHeaderIter
254  *
255  * Updates @iter to point to the first header.
256  *
257  * Returns: %TRUE on success or %FALSE otherwise.
258  **/
259 gboolean
260 g_mime_header_iter_first (GMimeHeaderIter *iter)
261 {
262         GMimeHeader *first;
263         
264         g_return_val_if_fail (iter != NULL, FALSE);
265         
266         /* make sure we can actually do as requested */
267         if (!iter->hdrlist || list_is_empty (&iter->hdrlist->list))
268                 return FALSE;
269         
270         first = (GMimeHeader *) iter->hdrlist->list.head;
271         
272         iter->version = iter->hdrlist->version;
273         iter->cursor = first;
274         
275         return TRUE;
276 }
277
278
279 /**
280  * g_mime_header_iter_last:
281  * @iter: a #GMimeHeaderIter
282  *
283  * Updates @iter to point to the last header.
284  *
285  * Returns: %TRUE on success or %FALSE otherwise.
286  **/
287 gboolean
288 g_mime_header_iter_last (GMimeHeaderIter *iter)
289 {
290         GMimeHeader *last;
291         
292         g_return_val_if_fail (iter != NULL, FALSE);
293         
294         /* make sure we can actually do as requested */
295         if (!iter->hdrlist || list_is_empty (&iter->hdrlist->list))
296                 return FALSE;
297         
298         last = (GMimeHeader *) iter->hdrlist->list.tailpred;
299         
300         iter->version = iter->hdrlist->version;
301         iter->cursor = last;
302         
303         return TRUE;
304 }
305
306
307 /**
308  * g_mime_header_iter_next:
309  * @iter: a #GMimeHeaderIter
310  *
311  * Advances to the next header.
312  *
313  * Returns: %TRUE on success or %FALSE otherwise.
314  **/
315 gboolean
316 g_mime_header_iter_next (GMimeHeaderIter *iter)
317 {
318         GMimeHeader *next;
319         
320         g_return_val_if_fail (iter != NULL, FALSE);
321         
322         /* make sure current cursor is valid */
323         if (!g_mime_header_iter_is_valid (iter))
324                 return FALSE;
325         
326         /* make sure next item is valid */
327         next = iter->cursor->next;
328         if (!next->next)
329                 return FALSE;
330         
331         iter->cursor = next;
332         
333         return TRUE;
334 }
335
336
337 /**
338  * g_mime_header_iter_prev:
339  * @iter: a #GMimeHeaderIter
340  *
341  * Advances to the previous header.
342  *
343  * Returns: %TRUE on success or %FALSE otherwise.
344  **/
345 gboolean
346 g_mime_header_iter_prev (GMimeHeaderIter *iter)
347 {
348         GMimeHeader *prev;
349         
350         g_return_val_if_fail (iter != NULL, FALSE);
351         
352         /* make sure current cursor is valid */
353         if (!g_mime_header_iter_is_valid (iter))
354                 return FALSE;
355         
356         /* make sure prev item is valid */
357         prev = iter->cursor->prev;
358         if (!prev || !prev->prev)
359                 return FALSE;
360         
361         iter->cursor = prev;
362         
363         return TRUE;
364 }
365
366
367 #if 0
368 /**
369  * g_mime_header_iter_get_offset:
370  * @iter: a #GMimeHeaderIter
371  *
372  * Gets the current header's file/stream offset.
373  *
374  * Returns: the file/stream offset or %-1 if unknown or invalid.
375  **/
376 gint64
377 g_mime_header_iter_get_offset (GMimeHeaderIter *iter)
378 {
379         g_return_val_if_fail (iter != NULL, -1);
380         
381         if (!g_mime_header_iter_is_valid (iter))
382                 return -1;
383         
384         return iter->cursor->offset;
385 }
386 #endif
387
388
389 /**
390  * g_mime_header_iter_get_name:
391  * @iter: a #GMimeHeaderIter
392  *
393  * Gets the current header's name.
394  *
395  * Returns: the header name or %NULL if invalid.
396  **/
397 const char *
398 g_mime_header_iter_get_name (GMimeHeaderIter *iter)
399 {
400         g_return_val_if_fail (iter != NULL, NULL);
401         
402         if (!g_mime_header_iter_is_valid (iter))
403                 return NULL;
404         
405         return iter->cursor->name;
406 }
407
408
409 /**
410  * g_mime_header_iter_set_value:
411  * @iter: a #GMimeHeaderIter
412  * @value: new header value
413  *
414  * Sets the current header's value to the new value.
415  *
416  * Returns: %TRUE if the value was set or %FALSE otherwise (indicates
417  * invalid iter).
418  **/
419 gboolean
420 g_mime_header_iter_set_value (GMimeHeaderIter *iter, const char *value)
421 {
422         g_return_val_if_fail (iter != NULL, FALSE);
423         
424         if (!g_mime_header_iter_is_valid (iter))
425                 return FALSE;
426         
427         g_free (iter->cursor->value);
428         iter->cursor->value = g_strdup (value);
429         
430         g_mime_header_list_set_stream (iter->hdrlist, NULL);
431         
432         return TRUE;
433 }
434
435
436 /**
437  * g_mime_header_iter_get_value:
438  * @iter: a #GMimeHeaderIter
439  *
440  * Gets the current header's name.
441  *
442  * Returns: the header name or %NULL if invalid.
443  **/
444 const char *
445 g_mime_header_iter_get_value (GMimeHeaderIter *iter)
446 {
447         g_return_val_if_fail (iter != NULL, NULL);
448         
449         if (!g_mime_header_iter_is_valid (iter))
450                 return NULL;
451         
452         return iter->cursor->value;
453 }
454
455
456 /**
457  * g_mime_header_iter_remove:
458  * @iter: a #GMimeHeaderIter
459  *
460  * Removes the current header and advances to the next header.
461  *
462  * Note: If you remove the last header in the list, then @iter will
463  * become invalid regardless of whether or not other headers remain.
464  *
465  * Returns: %TRUE on success or %FALSE otherwise.
466  **/
467 gboolean
468 g_mime_header_iter_remove (GMimeHeaderIter *iter)
469 {
470         GMimeHeader *cursor, *header, *next;
471         GMimeHeaderList *hdrlist;
472         
473         g_return_val_if_fail (iter != NULL, FALSE);
474         
475         if (!g_mime_header_iter_is_valid (iter))
476                 return FALSE;
477         
478         /* save iter state */
479         hdrlist = iter->hdrlist;
480         cursor = iter->cursor;
481         next = cursor->next;
482         
483         if (!(header = g_hash_table_lookup (hdrlist->hash, cursor->name)))
484                 return FALSE;
485         
486         if (cursor == header) {
487                 /* update the header lookup table */
488                 GMimeHeader *node = next;
489                 
490                 g_hash_table_remove (hdrlist->hash, cursor->name);
491                 
492                 while (node->next) {
493                         if (!g_ascii_strcasecmp (node->name, cursor->name)) {
494                                 /* enter this node into the lookup table */
495                                 g_hash_table_insert (hdrlist->hash, node->name, node);
496                                 break;
497                         }
498                         
499                         node = node->next;
500                 }
501         }
502         
503         /* remove/free the header */
504         list_unlink ((ListNode *) cursor);
505         g_mime_header_free (cursor);
506         hdrlist->version++;
507         
508         /* update iter state */
509         iter->version = hdrlist->version;
510         iter->cursor = next;
511         
512         return TRUE;
513 }
514
515
516 /**
517  * g_mime_header_list_new:
518  *
519  * Creates a new #GMimeHeaderList object.
520  *
521  * Returns: a new header list object.
522  **/
523 GMimeHeaderList *
524 g_mime_header_list_new (void)
525 {
526         GMimeHeaderList *headers;
527         
528         headers = g_slice_new (GMimeHeaderList);
529         headers->writers = g_hash_table_new_full (g_mime_strcase_hash,
530                                                   g_mime_strcase_equal,
531                                                   g_free, NULL);
532         headers->hash = g_hash_table_new (g_mime_strcase_hash,
533                                           g_mime_strcase_equal);
534         list_init (&headers->list);
535         headers->changed = g_mime_event_new (headers);
536         headers->stream = NULL;
537         headers->version = 0;
538         
539         return headers;
540 }
541
542
543 /**
544  * g_mime_header_list_destroy:
545  * @headers: a #GMimeHeaderList
546  *
547  * Destroy the header list.
548  **/
549 void
550 g_mime_header_list_destroy (GMimeHeaderList *headers)
551 {
552         GMimeHeader *header, *next;
553         
554         if (!headers)
555                 return;
556         
557         header = (GMimeHeader *) headers->list.head;
558         while (header->next) {
559                 next = header->next;
560                 g_mime_header_free (header);
561                 header = next;
562         }
563         
564         g_hash_table_destroy (headers->writers);
565         g_hash_table_destroy (headers->hash);
566         
567         if (headers->stream)
568                 g_object_unref (headers->stream);
569         
570         g_mime_event_destroy (headers->changed);
571         
572         g_slice_free (GMimeHeaderList, headers);
573 }
574
575
576 /**
577  * g_mime_header_list_clear:
578  * @headers: a #GMimeHeaderList
579  *
580  * Removes all of the headers from the #GMimeHeaderList.
581  **/
582 void
583 g_mime_header_list_clear (GMimeHeaderList *headers)
584 {
585         GMimeHeader *header, *next;
586         
587         g_return_if_fail (headers != NULL);
588         
589         header = (GMimeHeader *) headers->list.head;
590         while (header->next) {
591                 next = header->next;
592                 g_mime_header_free (header);
593                 header = next;
594         }
595         
596         g_hash_table_remove_all (headers->hash);
597         list_init (&headers->list);
598         
599         g_mime_header_list_set_stream (headers, NULL);
600 }
601
602
603 /**
604  * g_mime_header_list_contains:
605  * @headers: a #GMimeHeaderList
606  * @name: header name
607  *
608  * Checks whether or not a header exists.
609  *
610  * Returns: %TRUE if the specified header exists or %FALSE otherwise.
611  **/
612 gboolean
613 g_mime_header_list_contains (const GMimeHeaderList *headers, const char *name)
614 {
615         const GMimeHeader *header;
616         
617         g_return_val_if_fail (headers != NULL, FALSE);
618         g_return_val_if_fail (name != NULL, FALSE);
619         
620         if (!(header = g_hash_table_lookup (headers->hash, name)))
621                 return FALSE;
622         
623         return TRUE;
624 }
625
626
627 /**
628  * g_mime_header_list_prepend:
629  * @headers: a #GMimeHeaderList
630  * @name: header name
631  * @value: header value
632  *
633  * Prepends a header. If @value is %NULL, a space will be set aside
634  * for it (useful for setting the order of headers before values can
635  * be obtained for them) otherwise the header will be unset.
636  **/
637 void
638 g_mime_header_list_prepend (GMimeHeaderList *headers, const char *name, const char *value)
639 {
640         GMimeHeader *header;
641         
642         g_return_if_fail (headers != NULL);
643         g_return_if_fail (name != NULL);
644         
645         header = g_mime_header_new (name, value, -1);
646         list_prepend (&headers->list, (ListNode *) header);
647         g_hash_table_replace (headers->hash, header->name, header);
648         
649         g_mime_header_list_set_stream (headers, NULL);
650 }
651
652
653 /**
654  * g_mime_header_list_append:
655  * @headers: a #GMimeHeaderList
656  * @name: header name
657  * @value: header value
658  *
659  * Appends a header. If @value is %NULL, a space will be set aside for it
660  * (useful for setting the order of headers before values can be
661  * obtained for them) otherwise the header will be unset.
662  **/
663 void
664 g_mime_header_list_append (GMimeHeaderList *headers, const char *name, const char *value)
665 {
666         GMimeHeader *header;
667         
668         g_return_if_fail (headers != NULL);
669         g_return_if_fail (name != NULL);
670         
671         header = g_mime_header_new (name, value, -1);
672         list_append (&headers->list, (ListNode *) header);
673         
674         if (!g_hash_table_lookup (headers->hash, name))
675                 g_hash_table_insert (headers->hash, header->name, header);
676         
677         g_mime_header_list_set_stream (headers, NULL);
678 }
679
680
681 /**
682  * g_mime_header_list_get:
683  * @headers: a #GMimeHeaderList
684  * @name: header name
685  *
686  * Gets the value of the first header with the name requested.
687  *
688  * Returns: the value of the header requested.
689  **/
690 const char *
691 g_mime_header_list_get (const GMimeHeaderList *headers, const char *name)
692 {
693         const GMimeHeader *header;
694         
695         g_return_val_if_fail (headers != NULL, NULL);
696         g_return_val_if_fail (name != NULL, NULL);
697         
698         if (!(header = g_hash_table_lookup (headers->hash, name)))
699                 return NULL;
700         
701         return header->value;
702 }
703
704
705 /**
706  * g_mime_header_list_set:
707  * @headers: a #GMimeHeaderList
708  * @name: header name
709  * @value: header value
710  *
711  * Set the value of the specified header. If @value is %NULL and the
712  * header, @name, had not been previously set, a space will be set
713  * aside for it (useful for setting the order of headers before values
714  * can be obtained for them) otherwise the header will be unset.
715  *
716  * Note: If there are multiple headers with the specified field name,
717  * the first instance of the header will be replaced and further
718  * instances will be removed.
719  **/
720 void
721 g_mime_header_list_set (GMimeHeaderList *headers, const char *name, const char *value)
722 {
723         GMimeHeader *header, *next;
724         
725         g_return_if_fail (headers != NULL);
726         g_return_if_fail (name != NULL);
727         
728         if ((header = g_hash_table_lookup (headers->hash, name))) {
729                 g_free (header->value);
730                 header->value = g_strdup (value);
731                 
732                 header = header->next;
733                 while (header->next) {
734                         next = header->next;
735                         
736                         if (!g_ascii_strcasecmp (header->name, name)) {
737                                 /* remove/free the header */
738                                 list_unlink ((ListNode *) header);
739                                 g_mime_header_free (header);
740                                 headers->version++;
741                         }
742                         
743                         header = next;
744                 }
745         } else {
746                 header = g_mime_header_new (name, value, -1);
747                 list_append (&headers->list, (ListNode *) header);
748                 g_hash_table_insert (headers->hash, header->name, header);
749         }
750         
751         g_mime_header_list_set_stream (headers, NULL);
752 }
753
754
755 /**
756  * g_mime_header_list_remove:
757  * @headers: a #GMimeHeaderList
758  * @name: header name
759  *
760  * Remove the specified header.
761  *
762  * Returns: %TRUE if the header was successfully removed or %FALSE if
763  * the specified header could not be found.
764  **/
765 gboolean
766 g_mime_header_list_remove (GMimeHeaderList *headers, const char *name)
767 {
768         GMimeHeader *header, *node;
769         
770         g_return_val_if_fail (headers != NULL, FALSE);
771         g_return_val_if_fail (name != NULL, FALSE);
772         
773         if (!(header = g_hash_table_lookup (headers->hash, name)))
774                 return FALSE;
775         
776         /* look for another header with the same name... */
777         node = header->next;
778         while (node->next) {
779                 if (!g_ascii_strcasecmp (node->name, name)) {
780                         /* enter this node into the lookup table */
781                         g_hash_table_replace (headers->hash, node->name, node);
782                         break;
783                 }
784                 
785                 node = node->next;
786         }
787         
788         /* invalidate all our outstanding iterators matching @header */
789         headers->version++;
790         
791         /* remove/free the header */
792         list_unlink ((ListNode *) header);
793         g_mime_header_free (header);
794         
795         g_mime_header_list_set_stream (headers, NULL);
796         
797         return TRUE;
798 }
799
800
801 /**
802  * g_mime_header_list_get_iter:
803  * @headers: a #GMimeHeaderList
804  * @iter: a #GMimeHeaderIter
805  *
806  * Initializes an iterator for traversing @headers.
807  *
808  * Returns: a %TRUE if successful or %FALSE if there are no headers to
809  * traverse.
810  **/
811 gboolean
812 g_mime_header_list_get_iter (GMimeHeaderList *headers, GMimeHeaderIter *iter)
813 {
814         GMimeHeader *cursor;
815         
816         g_return_val_if_fail (headers != NULL, FALSE);
817         
818         cursor = (GMimeHeader *) headers->list.head;
819         if (!cursor->next)
820                 return FALSE;
821         
822         iter->version = headers->version;
823         iter->hdrlist = headers;
824         iter->cursor = cursor;
825         
826         return TRUE;
827 }
828
829
830 /**
831  * g_mime_header_list_foreach:
832  * @headers: A #GMimeHeaderList
833  * @func: function to be called for each header.
834  * @user_data: User data to be passed to the func.
835  *
836  * Calls @func for each header name/value pair.
837  */
838 void
839 g_mime_header_list_foreach (const GMimeHeaderList *headers, GMimeHeaderForeachFunc func, gpointer user_data)
840 {
841         const GMimeHeader *header;
842         
843         g_return_if_fail (headers != NULL);
844         g_return_if_fail (func != NULL);
845         
846         header = (const GMimeHeader *) headers->list.head;
847         
848         while (header->next) {
849                 func (header->name, header->value, user_data);
850                 header = header->next;
851         }
852 }
853
854
855 static ssize_t
856 default_writer (GMimeStream *stream, const char *name, const char *value)
857 {
858         ssize_t nwritten;
859         char *val;
860         
861         val = g_mime_utils_header_printf ("%s: %s\n", name, value);
862         nwritten = g_mime_stream_write_string (stream, val);
863         g_free (val);
864         
865         return nwritten;
866 }
867
868
869 /**
870  * g_mime_header_list_write_to_stream:
871  * @headers: a #GMimeHeaderList
872  * @stream: output stream
873  *
874  * Write the headers to a stream.
875  *
876  * Returns: the number of bytes written or %-1 on fail.
877  **/
878 ssize_t
879 g_mime_header_list_write_to_stream (const GMimeHeaderList *headers, GMimeStream *stream)
880 {
881         ssize_t nwritten, total = 0;
882         GMimeHeaderWriter writer;
883         GHashTable *writers;
884         GMimeHeader *header;
885         
886         g_return_val_if_fail (headers != NULL, -1);
887         g_return_val_if_fail (stream != NULL, -1);
888         
889         if (headers->stream) {
890                 g_mime_stream_reset (headers->stream);
891                 return g_mime_stream_write_to_stream (headers->stream, stream);
892         }
893         
894         header = (GMimeHeader *) headers->list.head;
895         writers = headers->writers;
896         
897         while (header->next) {
898                 if (header->value) {
899                         if (!(writer = g_hash_table_lookup (writers, header->name)))
900                                 writer = default_writer;
901                         
902                         if ((nwritten = writer (stream, header->name, header->value)) == -1)
903                                 return -1;
904                         
905                         total += nwritten;
906                 }
907                 
908                 header = header->next;
909         }
910         
911         return total;
912 }
913
914
915 /**
916  * g_mime_header_list_to_string:
917  * @headers: a #GMimeHeaderList
918  *
919  * Allocates a string buffer containing the raw rfc822 headers
920  * contained in @headers.
921  *
922  * Returns: a string containing the header block.
923  **/
924 char *
925 g_mime_header_list_to_string (const GMimeHeaderList *headers)
926 {
927         GMimeStream *stream;
928         GByteArray *array;
929         char *str;
930         
931         g_return_val_if_fail (headers != NULL, NULL);
932         
933         array = g_byte_array_new ();
934         stream = g_mime_stream_mem_new ();
935         g_mime_stream_mem_set_byte_array (GMIME_STREAM_MEM (stream), array);
936         
937         if (headers->stream) {
938                 g_mime_stream_reset (headers->stream);
939                 g_mime_stream_write_to_stream (headers->stream, stream);
940         } else {
941                 g_mime_header_list_write_to_stream (headers, stream);
942         }
943         
944         g_object_unref (stream);
945         
946         g_byte_array_append (array, (unsigned char *) "", 1);
947         str = (char *) array->data;
948         g_byte_array_free (array, FALSE);
949         
950         return str;
951 }
952
953
954 /**
955  * g_mime_header_list_register_writer:
956  * @headers: a #GMimeHeaderList
957  * @name: header name
958  * @writer: writer function
959  *
960  * Changes the function used to write @name headers to @writer (or the
961  * default if @writer is %NULL). This is useful if you want to change
962  * the default header folding style for a particular header.
963  **/
964 void
965 g_mime_header_list_register_writer (GMimeHeaderList *headers, const char *name, GMimeHeaderWriter writer)
966 {
967         gpointer okey, oval;
968         
969         g_return_if_fail (headers != NULL);
970         g_return_if_fail (name != NULL);
971         
972         if (g_hash_table_lookup (headers->writers, name)) {
973                 g_hash_table_lookup_extended (headers->writers, name, &okey, &oval);
974                 g_hash_table_remove (headers->writers, name);
975                 g_free (okey);
976         }
977         
978         if (writer)
979                 g_hash_table_insert (headers->writers, g_strdup (name), writer);
980 }
981
982
983
984 /**
985  * g_mime_header_list_set_stream:
986  * @headers: a #GMimeHeaderList
987  * @stream: a #GMimeStream
988  *
989  * Set the raw header stream.
990  **/
991 void
992 g_mime_header_list_set_stream (GMimeHeaderList *headers, GMimeStream *stream)
993 {
994         g_return_if_fail (stream == NULL || GMIME_IS_STREAM (stream));
995         g_return_if_fail (headers != NULL);
996         
997         if (headers->stream == stream)
998                 return;
999         
1000         if (stream)
1001                 g_object_ref (stream);
1002         
1003         if (headers->stream)
1004                 g_object_unref (headers->stream);
1005         
1006         headers->stream = stream;
1007         
1008         g_mime_event_emit (headers->changed, NULL);
1009 }
1010
1011
1012 /**
1013  * g_mime_header_list_get_stream:
1014  * @headers: a #GMimeHeaderList
1015  *
1016  * Gets the raw stream representing @headers.
1017  *
1018  * Returns: a #GMimeStream if set or %NULL otherwise.
1019  **/
1020 GMimeStream *
1021 g_mime_header_list_get_stream (GMimeHeaderList *headers)
1022 {
1023         g_return_val_if_fail (headers != NULL, NULL);
1024         
1025         return headers->stream;
1026 }
1027
1028
1029 GMimeEvent *
1030 _g_mime_header_list_get_changed_event (GMimeHeaderList *headers)
1031 {
1032         g_return_val_if_fail (headers != NULL, NULL);
1033         
1034         return headers->changed;
1035 }