1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
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.
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.
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
27 #include <sys/types.h>
30 #include "gmime-part.h"
31 #include "gmime-utils.h"
32 #include "gmime-common.h"
33 #include "gmime-stream-mem.h"
34 #include "gmime-stream-null.h"
35 #include "gmime-stream-filter.h"
36 #include "gmime-filter-basic.h"
37 #include "gmime-filter-best.h"
38 #include "gmime-filter-crlf.h"
39 #include "gmime-filter-md5.h"
40 #include "gmime-table-private.h"
48 * @short_description: MIME parts
51 * A #GMimePart represents any MIME leaf part (meaning it has no
55 /* GObject class methods */
56 static void g_mime_part_class_init (GMimePartClass *klass);
57 static void g_mime_part_init (GMimePart *mime_part, GMimePartClass *klass);
58 static void g_mime_part_finalize (GObject *object);
60 /* GMimeObject class methods */
61 static void mime_part_prepend_header (GMimeObject *object, const char *header, const char *value);
62 static void mime_part_append_header (GMimeObject *object, const char *header, const char *value);
63 static void mime_part_set_header (GMimeObject *object, const char *header, const char *value);
64 static gboolean mime_part_remove_header (GMimeObject *object, const char *header);
65 static ssize_t mime_part_write_to_stream (GMimeObject *object, GMimeStream *stream);
66 static void mime_part_encode (GMimeObject *object, GMimeEncodingConstraint constraint);
68 /* GMimePart class methods */
69 static void set_content_object (GMimePart *mime_part, GMimeDataWrapper *content);
72 static GMimeObjectClass *parent_class = NULL;
76 g_mime_part_get_type (void)
78 static GType type = 0;
81 static const GTypeInfo info = {
82 sizeof (GMimePartClass),
83 NULL, /* base_class_init */
84 NULL, /* base_class_finalize */
85 (GClassInitFunc) g_mime_part_class_init,
86 NULL, /* class_finalize */
87 NULL, /* class_data */
90 (GInstanceInitFunc) g_mime_part_init,
93 type = g_type_register_static (GMIME_TYPE_OBJECT, "GMimePart", &info, 0);
101 g_mime_part_class_init (GMimePartClass *klass)
103 GMimeObjectClass *object_class = GMIME_OBJECT_CLASS (klass);
104 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
106 parent_class = g_type_class_ref (GMIME_TYPE_OBJECT);
108 gobject_class->finalize = g_mime_part_finalize;
110 object_class->prepend_header = mime_part_prepend_header;
111 object_class->append_header = mime_part_append_header;
112 object_class->remove_header = mime_part_remove_header;
113 object_class->set_header = mime_part_set_header;
114 object_class->write_to_stream = mime_part_write_to_stream;
115 object_class->encode = mime_part_encode;
117 klass->set_content_object = set_content_object;
121 g_mime_part_init (GMimePart *mime_part, GMimePartClass *klass)
123 mime_part->encoding = GMIME_CONTENT_ENCODING_DEFAULT;
124 mime_part->content_description = NULL;
125 mime_part->content_location = NULL;
126 mime_part->content_md5 = NULL;
127 mime_part->content = NULL;
131 g_mime_part_finalize (GObject *object)
133 GMimePart *mime_part = (GMimePart *) object;
135 g_free (mime_part->content_description);
136 g_free (mime_part->content_location);
137 g_free (mime_part->content_md5);
139 if (mime_part->content)
140 g_object_unref (mime_part->content);
142 G_OBJECT_CLASS (parent_class)->finalize (object);
147 HEADER_CONTENT_TRANSFER_ENCODING,
148 HEADER_CONTENT_DESCRIPTION,
149 HEADER_CONTENT_LOCATION,
154 static const char *content_headers[] = {
155 "Content-Transfer-Encoding",
156 "Content-Description",
163 copy_atom (const char *src, char *dest, size_t n)
165 register const char *inptr = src;
166 register char *outptr = dest;
167 char *outend = dest + n;
169 while (is_lwsp (*inptr))
172 while (is_atom (*inptr) && outptr < outend)
173 *outptr++ = *inptr++;
179 process_header (GMimeObject *object, const char *header, const char *value)
181 GMimePart *mime_part = (GMimePart *) object;
185 if (g_ascii_strncasecmp (header, "Content-", 8) != 0)
188 for (i = 0; i < G_N_ELEMENTS (content_headers); i++) {
189 if (!g_ascii_strcasecmp (content_headers[i] + 8, header + 8))
194 case HEADER_CONTENT_TRANSFER_ENCODING:
195 copy_atom (value, encoding, sizeof (encoding) - 1);
196 mime_part->encoding = g_mime_content_encoding_from_string (encoding);
198 case HEADER_CONTENT_DESCRIPTION:
199 /* FIXME: we should decode this */
200 g_free (mime_part->content_description);
201 mime_part->content_description = g_mime_strdup_trim (value);
203 case HEADER_CONTENT_LOCATION:
204 g_free (mime_part->content_location);
205 mime_part->content_location = g_mime_strdup_trim (value);
207 case HEADER_CONTENT_MD5:
208 g_free (mime_part->content_md5);
209 mime_part->content_md5 = g_mime_strdup_trim (value);
219 mime_part_prepend_header (GMimeObject *object, const char *header, const char *value)
221 if (!process_header (object, header, value))
222 GMIME_OBJECT_CLASS (parent_class)->prepend_header (object, header, value);
224 g_mime_header_list_prepend (object->headers, header, value);
228 mime_part_append_header (GMimeObject *object, const char *header, const char *value)
230 if (!process_header (object, header, value))
231 GMIME_OBJECT_CLASS (parent_class)->append_header (object, header, value);
233 g_mime_header_list_append (object->headers, header, value);
237 mime_part_set_header (GMimeObject *object, const char *header, const char *value)
239 if (!process_header (object, header, value))
240 GMIME_OBJECT_CLASS (parent_class)->set_header (object, header, value);
242 g_mime_header_list_set (object->headers, header, value);
246 mime_part_remove_header (GMimeObject *object, const char *header)
248 GMimePart *mime_part = (GMimePart *) object;
251 if (!g_ascii_strncasecmp (header, "Content-", 8)) {
252 for (i = 0; i < G_N_ELEMENTS (content_headers); i++) {
253 if (!g_ascii_strcasecmp (content_headers[i] + 8, header + 8))
258 case HEADER_CONTENT_TRANSFER_ENCODING:
259 mime_part->encoding = GMIME_CONTENT_ENCODING_DEFAULT;
261 case HEADER_CONTENT_DESCRIPTION:
262 g_free (mime_part->content_description);
263 mime_part->content_description = NULL;
265 case HEADER_CONTENT_LOCATION:
266 g_free (mime_part->content_location);
267 mime_part->content_location = NULL;
269 case HEADER_CONTENT_MD5:
270 g_free (mime_part->content_md5);
271 mime_part->content_md5 = NULL;
278 return GMIME_OBJECT_CLASS (parent_class)->remove_header (object, header);
282 write_content (GMimePart *part, GMimeStream *stream)
284 ssize_t nwritten, total = 0;
289 /* Evil Genius's "slight" optimization: Since GMimeDataWrapper::write_to_stream()
290 * decodes its content stream to the raw format, we can cheat by requesting its
291 * content stream and not doing any encoding on the data if the source and
292 * destination encodings are identical.
295 if (part->encoding != g_mime_data_wrapper_get_encoding (part->content)) {
296 GMimeStream *filtered_stream;
297 const char *filename;
300 switch (part->encoding) {
301 case GMIME_CONTENT_ENCODING_UUENCODE:
302 filename = g_mime_part_get_filename (part);
303 nwritten = g_mime_stream_printf (stream, "begin 0644 %s\n",
304 filename ? filename : "unknown");
311 case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
312 case GMIME_CONTENT_ENCODING_BASE64:
313 filtered_stream = g_mime_stream_filter_new (stream);
314 filter = g_mime_filter_basic_new (part->encoding, TRUE);
315 g_mime_stream_filter_add (GMIME_STREAM_FILTER (filtered_stream), filter);
316 g_object_unref (filter);
319 filtered_stream = stream;
320 g_object_ref (stream);
324 nwritten = g_mime_data_wrapper_write_to_stream (part->content, filtered_stream);
325 g_mime_stream_flush (filtered_stream);
326 g_object_unref (filtered_stream);
333 if (part->encoding == GMIME_CONTENT_ENCODING_UUENCODE) {
334 /* FIXME: get rid of this special-case x-uuencode crap */
335 nwritten = g_mime_stream_write (stream, "end\n", 4);
342 GMimeStream *content_stream;
344 content_stream = g_mime_data_wrapper_get_stream (part->content);
345 g_mime_stream_reset (content_stream);
346 nwritten = g_mime_stream_write_to_stream (content_stream, stream);
358 mime_part_write_to_stream (GMimeObject *object, GMimeStream *stream)
360 GMimePart *mime_part = (GMimePart *) object;
361 ssize_t nwritten, total = 0;
363 /* write the content headers */
364 if ((nwritten = g_mime_header_list_write_to_stream (object->headers, stream)) == -1)
369 /* terminate the headers */
370 if (g_mime_stream_write (stream, "\n", 1) == -1)
375 if ((nwritten = write_content (mime_part, stream)) == -1)
384 mime_part_encode (GMimeObject *object, GMimeEncodingConstraint constraint)
386 GMimePart *part = (GMimePart *) object;
387 GMimeContentEncoding encoding;
388 GMimeStream *stream, *null;
391 switch (part->encoding) {
392 case GMIME_CONTENT_ENCODING_DEFAULT:
393 /* Unspecified encoding, we need to figure out the
394 * best encoding no matter what */
396 case GMIME_CONTENT_ENCODING_7BIT:
397 /* This encoding is always safe. */
399 case GMIME_CONTENT_ENCODING_8BIT:
400 /* This encoding is safe unless the constraint is 7bit. */
401 if (constraint != GMIME_ENCODING_CONSTRAINT_7BIT)
404 case GMIME_CONTENT_ENCODING_BINARY:
405 /* This encoding is only safe if the constraint is binary. */
406 if (constraint == GMIME_ENCODING_CONSTRAINT_BINARY)
409 case GMIME_CONTENT_ENCODING_BASE64:
410 case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
411 case GMIME_CONTENT_ENCODING_UUENCODE:
412 /* These encodings are always safe. */
416 filter = g_mime_filter_best_new (GMIME_FILTER_BEST_ENCODING);
418 null = g_mime_stream_null_new ();
419 stream = g_mime_stream_filter_new (null);
420 g_mime_stream_filter_add ((GMimeStreamFilter *) stream, filter);
421 g_object_unref (null);
423 g_mime_object_write_to_stream (object, stream);
424 g_object_unref (stream);
426 encoding = g_mime_filter_best_encoding ((GMimeFilterBest *) filter, constraint);
427 g_mime_part_set_content_encoding (part, encoding);
428 g_object_unref (filter);
435 * Creates a new MIME Part object with a default content-type of
438 * Returns: an empty MIME Part object with a default content-type of
442 g_mime_part_new (void)
444 GMimeContentType *content_type;
445 GMimePart *mime_part;
447 mime_part = g_object_newv (GMIME_TYPE_PART, 0, NULL);
449 content_type = g_mime_content_type_new ("text", "plain");
450 g_mime_object_set_content_type (GMIME_OBJECT (mime_part), content_type);
451 g_object_unref (content_type);
458 * g_mime_part_new_with_type:
459 * @type: content-type string
460 * @subtype: content-subtype string
462 * Creates a new MIME Part with a sepcified type.
464 * Returns: an empty MIME Part object with the specified content-type.
467 g_mime_part_new_with_type (const char *type, const char *subtype)
469 GMimeContentType *content_type;
470 GMimePart *mime_part;
472 mime_part = g_object_newv (GMIME_TYPE_PART, 0, NULL);
474 content_type = g_mime_content_type_new (type, subtype);
475 g_mime_object_set_content_type (GMIME_OBJECT (mime_part), content_type);
476 g_object_unref (content_type);
483 * g_mime_part_set_content_description:
484 * @mime_part: a #GMimePart object
485 * @description: content description
487 * Set the content description for the specified mime part.
490 g_mime_part_set_content_description (GMimePart *mime_part, const char *description)
492 g_return_if_fail (GMIME_IS_PART (mime_part));
494 if (mime_part->content_description == description)
497 g_free (mime_part->content_description);
498 mime_part->content_description = g_strdup (description);
499 g_mime_header_list_set (GMIME_OBJECT (mime_part)->headers, "Content-Description", description);
504 * g_mime_part_get_content_description:
505 * @mime_part: a #GMimePart object
507 * Gets the value of the Content-Description for the specified mime
508 * part if it exists or %NULL otherwise.
510 * Returns: the content description for the specified mime part.
513 g_mime_part_get_content_description (GMimePart *mime_part)
515 g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
517 return mime_part->content_description;
522 * g_mime_part_set_content_id:
523 * @mime_part: a #GMimePart object
524 * @content_id: content id
526 * Set the content id for the specified mime part.
529 g_mime_part_set_content_id (GMimePart *mime_part, const char *content_id)
531 g_return_if_fail (GMIME_IS_PART (mime_part));
533 g_mime_object_set_content_id (GMIME_OBJECT (mime_part), content_id);
538 * g_mime_part_get_content_id:
539 * @mime_part: a #GMimePart object
541 * Gets the content-id of the specified mime part if it exists, or
544 * Returns: the content id for the specified mime part.
547 g_mime_part_get_content_id (GMimePart *mime_part)
549 g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
551 return g_mime_object_get_content_id (GMIME_OBJECT (mime_part));
556 * g_mime_part_set_content_md5:
557 * @mime_part: a #GMimePart object
558 * @content_md5: content md5 or %NULL to generate the md5 digest.
560 * Set the content md5 for the specified mime part.
563 g_mime_part_set_content_md5 (GMimePart *mime_part, const char *content_md5)
565 unsigned char digest[16], b64digest[32];
566 GMimeStreamFilter *filtered_stream;
567 GMimeContentType *content_type;
568 GMimeFilter *md5_filter;
574 g_return_if_fail (GMIME_IS_PART (mime_part));
576 g_free (mime_part->content_md5);
579 /* compute a md5sum */
580 stream = g_mime_stream_null_new ();
581 filtered_stream = (GMimeStreamFilter *) g_mime_stream_filter_new (stream);
582 g_object_unref (stream);
584 content_type = g_mime_object_get_content_type ((GMimeObject *) mime_part);
585 if (g_mime_content_type_is_type (content_type, "text", "*")) {
586 GMimeFilter *crlf_filter;
588 crlf_filter = g_mime_filter_crlf_new (TRUE, FALSE);
589 g_mime_stream_filter_add (filtered_stream, crlf_filter);
590 g_object_unref (crlf_filter);
593 md5_filter = g_mime_filter_md5_new ();
594 g_mime_stream_filter_add (filtered_stream, md5_filter);
596 stream = (GMimeStream *) filtered_stream;
597 g_mime_data_wrapper_write_to_stream (mime_part->content, stream);
598 g_object_unref (stream);
600 memset (digest, 0, 16);
601 g_mime_filter_md5_get_digest ((GMimeFilterMd5 *) md5_filter, digest);
602 g_object_unref (md5_filter);
604 len = g_mime_encoding_base64_encode_close (digest, 16, b64digest, &state, &save);
605 b64digest[len] = '\0';
606 g_strstrip ((char *) b64digest);
608 content_md5 = (const char *) b64digest;
611 mime_part->content_md5 = g_strdup (content_md5);
612 g_mime_header_list_set (GMIME_OBJECT (mime_part)->headers, "Content-Md5", content_md5);
617 * g_mime_part_verify_content_md5:
618 * @mime_part: a #GMimePart object
620 * Verify the content md5 for the specified mime part.
622 * Returns: %TRUE if the md5 is valid or %FALSE otherwise. Note: will
623 * return %FALSE if the mime part does not contain a Content-MD5.
626 g_mime_part_verify_content_md5 (GMimePart *mime_part)
628 unsigned char digest[16], b64digest[32];
629 GMimeStreamFilter *filtered_stream;
630 GMimeContentType *content_type;
631 GMimeFilter *md5_filter;
637 g_return_val_if_fail (GMIME_IS_PART (mime_part), FALSE);
638 g_return_val_if_fail (mime_part->content != NULL, FALSE);
640 if (!mime_part->content_md5)
643 stream = g_mime_stream_null_new ();
644 filtered_stream = (GMimeStreamFilter *) g_mime_stream_filter_new (stream);
645 g_object_unref (stream);
647 content_type = g_mime_object_get_content_type ((GMimeObject *) mime_part);
648 if (g_mime_content_type_is_type (content_type, "text", "*")) {
649 GMimeFilter *crlf_filter;
651 crlf_filter = g_mime_filter_crlf_new (TRUE, FALSE);
652 g_mime_stream_filter_add (filtered_stream, crlf_filter);
653 g_object_unref (crlf_filter);
656 md5_filter = g_mime_filter_md5_new ();
657 g_mime_stream_filter_add (filtered_stream, md5_filter);
659 stream = (GMimeStream *) filtered_stream;
660 g_mime_data_wrapper_write_to_stream (mime_part->content, stream);
661 g_object_unref (stream);
663 memset (digest, 0, 16);
664 g_mime_filter_md5_get_digest ((GMimeFilterMd5 *) md5_filter, digest);
665 g_object_unref (md5_filter);
667 len = g_mime_encoding_base64_encode_close (digest, 16, b64digest, &state, &save);
668 b64digest[len] = '\0';
669 g_strstrip ((char *) b64digest);
671 return !strcmp ((char *) b64digest, mime_part->content_md5);
676 * g_mime_part_get_content_md5:
677 * @mime_part: a #GMimePart object
679 * Gets the md5sum contained in the Content-Md5 header of the
680 * specified mime part if it exists, or %NULL otherwise.
682 * Returns: the content md5 for the specified mime part.
685 g_mime_part_get_content_md5 (GMimePart *mime_part)
687 g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
689 return mime_part->content_md5;
694 * g_mime_part_set_content_location:
695 * @mime_part: a #GMimePart object
696 * @content_location: content location
698 * Set the content location for the specified mime part.
701 g_mime_part_set_content_location (GMimePart *mime_part, const char *content_location)
703 g_return_if_fail (GMIME_IS_PART (mime_part));
705 if (mime_part->content_location == content_location)
708 g_free (mime_part->content_location);
709 mime_part->content_location = g_strdup (content_location);
710 g_mime_header_list_set (GMIME_OBJECT (mime_part)->headers, "Content-Location", content_location);
715 * g_mime_part_get_content_location:
716 * @mime_part: a #GMimePart object
718 * Gets the value of the Content-Location header if it exists, or
721 * Returns: the content location for the specified mime part.
724 g_mime_part_get_content_location (GMimePart *mime_part)
726 g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
728 return mime_part->content_location;
733 * g_mime_part_set_content_encoding:
734 * @mime_part: a #GMimePart object
735 * @encoding: a #GMimeContentEncoding
737 * Set the content encoding for the specified mime part.
740 g_mime_part_set_content_encoding (GMimePart *mime_part, GMimeContentEncoding encoding)
742 g_return_if_fail (GMIME_IS_PART (mime_part));
744 mime_part->encoding = encoding;
745 g_mime_header_list_set (GMIME_OBJECT (mime_part)->headers, "Content-Transfer-Encoding",
746 g_mime_content_encoding_to_string (encoding));
751 * g_mime_part_get_content_encoding:
752 * @mime_part: a #GMimePart object
754 * Gets the content encoding of the mime part.
756 * Returns: the content encoding for the specified mime part.
759 g_mime_part_get_content_encoding (GMimePart *mime_part)
761 g_return_val_if_fail (GMIME_IS_PART (mime_part), GMIME_CONTENT_ENCODING_DEFAULT);
763 return mime_part->encoding;
768 * g_mime_part_get_best_content_encoding:
769 * @mime_part: a #GMimePart object
770 * @constraint: a #GMimeEncodingConstraint
772 * Calculates the most efficient content encoding for the @mime_part
773 * given the @constraint.
775 * Returns: the best content encoding for the specified mime part.
778 g_mime_part_get_best_content_encoding (GMimePart *mime_part, GMimeEncodingConstraint constraint)
780 GMimeStream *filtered, *stream;
781 GMimeContentEncoding encoding;
782 GMimeFilterBest *best;
785 g_return_val_if_fail (GMIME_IS_PART (mime_part), GMIME_CONTENT_ENCODING_DEFAULT);
787 stream = g_mime_stream_null_new ();
788 filtered = g_mime_stream_filter_new (stream);
789 g_object_unref (stream);
791 filter = g_mime_filter_best_new (GMIME_FILTER_BEST_ENCODING);
792 g_mime_stream_filter_add ((GMimeStreamFilter *) filtered, filter);
793 best = (GMimeFilterBest *) filter;
795 g_mime_data_wrapper_write_to_stream (mime_part->content, filtered);
796 g_mime_stream_flush (filtered);
797 g_object_unref (filtered);
799 encoding = g_mime_filter_best_encoding (best, constraint);
800 g_object_unref (best);
807 * g_mime_part_set_filename:
808 * @mime_part: a #GMimePart object
809 * @filename: the filename of the Mime Part's content
811 * Sets the "filename" parameter on the Content-Disposition and also sets the
812 * "name" parameter on the Content-Type.
815 g_mime_part_set_filename (GMimePart *mime_part, const char *filename)
817 GMimeObject *object = (GMimeObject *) mime_part;
819 g_return_if_fail (GMIME_IS_PART (mime_part));
821 g_mime_object_set_content_disposition_parameter (object, "filename", filename);
822 g_mime_object_set_content_type_parameter (object, "name", filename);
827 * g_mime_part_get_filename:
828 * @mime_part: a #GMimePart object
830 * Gets the filename of the specificed mime part, or %NULL if the mime
831 * part does not have the filename or name parameter set.
833 * Returns: the filename of the specified MIME Part. It first checks to
834 * see if the "filename" parameter was set on the Content-Disposition
835 * and if not then checks the "name" parameter in the Content-Type.
838 g_mime_part_get_filename (GMimePart *mime_part)
840 GMimeObject *object = (GMimeObject *) mime_part;
841 const char *filename = NULL;
843 g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
845 if ((filename = g_mime_object_get_content_disposition_parameter (object, "filename")))
848 /* check the "name" param in the content-type */
849 return g_mime_object_get_content_type_parameter (object, "name");
854 set_content_object (GMimePart *mime_part, GMimeDataWrapper *content)
856 if (mime_part->content)
857 g_object_unref (mime_part->content);
859 mime_part->content = content;
860 g_object_ref (content);
865 * g_mime_part_set_content_object:
866 * @mime_part: a #GMimePart object
867 * @content: a #GMimeDataWrapper content object
869 * Sets the content object on the mime part.
872 g_mime_part_set_content_object (GMimePart *mime_part, GMimeDataWrapper *content)
874 g_return_if_fail (GMIME_IS_PART (mime_part));
876 if (mime_part->content == content)
879 GMIME_PART_GET_CLASS (mime_part)->set_content_object (mime_part, content);
884 * g_mime_part_get_content_object:
885 * @mime_part: a #GMimePart object
887 * Gets the internal data-wrapper of the specified mime part, or %NULL
890 * Returns: the data-wrapper for the mime part's contents.
893 g_mime_part_get_content_object (GMimePart *mime_part)
895 g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
897 return mime_part->content;