[geary/wip/743676-cid] Candidate fix.
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/743676-cid] Candidate fix.
- Date: Fri, 13 Feb 2015 01:14:12 +0000 (UTC)
commit 58a713e22ef8d1a7ee332cd18ec1cb57eb8e8e82
Author: Jim Nelson <jim yorba org>
Date: Thu Feb 12 17:13:59 2015 -0800
Candidate fix.
src/CMakeLists.txt | 1 +
.../conversation-viewer/conversation-viewer.vala | 17 +++--
src/engine/mime/mime-content-type.vala | 4 +-
src/engine/mime/mime-multipart-subtype.vala | 73 ++++++++++++++++++++
src/engine/rfc822/rfc822-message.vala | 51 +++++++++-----
5 files changed, 121 insertions(+), 25 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6ee272d..78151fc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -243,6 +243,7 @@ engine/mime/mime-content-type.vala
engine/mime/mime-data-format.vala
engine/mime/mime-disposition-type.vala
engine/mime/mime-error.vala
+engine/mime/mime-multipart-subtype.vala
engine/nonblocking/nonblocking-abstract-semaphore.vala
engine/nonblocking/nonblocking-batch.vala
diff --git a/src/client/conversation-viewer/conversation-viewer.vala
b/src/client/conversation-viewer/conversation-viewer.vala
index d00e5a7..ce7ae0f 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -917,7 +917,13 @@ public class ConversationViewer : Gtk.Box {
private string? inline_image_replacer(string filename, Geary.Mime.ContentType? content_type,
Geary.Mime.ContentDisposition? disposition, string? content_id, Geary.Memory.Buffer buffer) {
- if (content_type == null || !is_content_type_supported_inline(content_type)) {
+ if (content_type == null) {
+ debug("Not displaying inline: no Content-Type");
+
+ return null;
+ }
+
+ if (!is_content_type_supported_inline(content_type)) {
debug("Not displaying %s inline: unsupported Content-Type", content_type.to_string());
return null;
@@ -1958,7 +1964,7 @@ public class ConversationViewer : Gtk.Box {
if (Geary.String.is_empty(src))
continue;
- // if no Content-ID, then leave as-is, but note if a data: URI is being used for
+ // if no Content-ID, then leave as-is, but note if a non-data: URI is being used for
// purposes of detecting remote images
string? content_id = src.has_prefix("cid:") ? src.substring(4) : null;
if (Geary.String.is_empty(content_id)) {
@@ -1981,10 +1987,10 @@ public class ConversationViewer : Gtk.Box {
guess = ContentType.guess(null, unowned_buffer.to_unowned_uint8_array(), null);
else
guess = ContentType.guess(null, image_content.get_uint8_array(), null);
-
+
string mimetype = ContentType.get_mime_type(guess);
- // Replace the SRC to a data URIm the class to a known label for the popup menu,
+ // Replace the SRC to a data URI, the class to a known label for the popup menu,
// and the ALT to its filename, if supplied
img.set_attribute("src", assemble_data_uri(mimetype, image_content));
img.set_attribute("class", DATA_IMAGE_CLASS);
@@ -1995,6 +2001,7 @@ public class ConversationViewer : Gtk.Box {
// Content-Disposition)
inlined_content_ids.add(content_id);
} else {
+ // replaced by data: URI, remove this tag and let the inserted one shine through
img.parent_element.remove_child(img);
}
}
@@ -2010,7 +2017,7 @@ public class ConversationViewer : Gtk.Box {
debug("Error removing inlined image: %s", error.message);
}
}
-
+
// Now return the whole message.
return container.get_inner_html();
} catch (Error e) {
diff --git a/src/engine/mime/mime-content-type.vala b/src/engine/mime/mime-content-type.vala
index 724535c..c4dc791 100644
--- a/src/engine/mime/mime-content-type.vala
+++ b/src/engine/mime/mime-content-type.vala
@@ -90,7 +90,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
* @see is_type
*/
public bool has_media_type(string media_type) {
- return (media_type != WILDCARD) ? String.stri_equal(this.media_type, media_type) : true;
+ return (media_type != WILDCARD) ? Ascii.stri_equal(this.media_type, media_type) : true;
}
/**
@@ -101,7 +101,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
* @see is_type
*/
public bool has_media_subtype(string media_subtype) {
- return (media_subtype != WILDCARD) ? String.stri_equal(this.media_subtype, media_subtype) : true;
+ return (media_subtype != WILDCARD) ? Ascii.stri_equal(this.media_subtype, media_subtype) : true;
}
/**
diff --git a/src/engine/mime/mime-multipart-subtype.vala b/src/engine/mime/mime-multipart-subtype.vala
new file mode 100644
index 0000000..9f216c9
--- /dev/null
+++ b/src/engine/mime/mime-multipart-subtype.vala
@@ -0,0 +1,73 @@
+/* Copyright 2015 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * A represenation of a MIME multipart Content-Type subtype.
+ *
+ * See [[https://tools.ietf.org/html/rfc2046#section-5.1]]
+ */
+
+public enum Geary.Mime.MultipartSubtype {
+ /**
+ * Used as a placeholder for no or unknown multipart subtype.
+ *
+ * Technically an unknown or unspecified subtype should be treated as { link MIXED}, but there
+ * are situations in code where this is useful.
+ */
+ UNSPECIFIED,
+ /**
+ * A multipart structure of mixed media.
+ *
+ * "Any 'multipart' subtypes that an implementation does not recognize must be treated as
+ * being of subtype 'mixed'."
+ *
+ * See [[https://tools.ietf.org/html/rfc2046#section-5.1.3]]
+ */
+ MIXED,
+ /**
+ * A multipart structure of alternative media.
+ *
+ * See [[https://tools.ietf.org/html/rfc2046#section-5.1.4]]
+ */
+ ALTERNATIVE,
+ /**
+ * A multipart structure of related media.
+ *
+ * See [[http://tools.ietf.org/html/rfc2387]]
+ */
+ RELATED;
+
+ /**
+ * Converts a { link ContentType} into a { link MultipartSubtype}.
+ *
+ * If unknown, { link MIXED} is returned but is_unknown will be true.
+ */
+ public static MultipartSubtype from_content_type(ContentType? content_type, out bool is_unknown) {
+ if (content_type == null || !content_type.has_media_type("multipart")) {
+ is_unknown = true;
+
+ return MIXED;
+ }
+
+ is_unknown = false;
+ switch (Ascii.strdown(content_type.media_subtype)) {
+ case "mixed":
+ return MIXED;
+
+ case "alternative":
+ return ALTERNATIVE;
+
+ case "related":
+ return RELATED;
+
+ default:
+ is_unknown = true;
+
+ return MIXED;
+ }
+ }
+}
+
diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala
index 0e2dd09..87df7b9 100644
--- a/src/engine/rfc822/rfc822-message.vala
+++ b/src/engine/rfc822/rfc822-message.vala
@@ -8,6 +8,10 @@ public class Geary.RFC822.Message : BaseObject {
/**
* This delegate is an optional parameter to the body constructers that allows callers
* to process arbitrary non-text, inline MIME parts.
+ *
+ * This is only called for non-text MIME parts in mixed multipart sections. Inline parts
+ * referred to by rich text in alternative or related documents must be located by the caller
+ * and appropriately presented.
*/
public delegate string? InlinePartReplacer(string filename, Mime.ContentType? content_type,
Mime.ContentDisposition? disposition, string? content_id, Geary.Memory.Buffer buffer);
@@ -453,29 +457,39 @@ public class Geary.RFC822.Message : BaseObject {
* Returns: a bool indicating whether a text part with the desired text_subtype was found
*/
private bool construct_body_from_mime_parts(ref string? body, InlinePartReplacer? replacer,
- string text_subtype, bool allow_only_replaced, bool to_html, GMime.Object? node)
+ string text_subtype, bool to_html, GMime.Object? node, Mime.MultipartSubtype container_subtype)
throws RFC822Error {
if (node == null) {
node = message.get_mime_part();
}
+ Mime.ContentType? this_content_type = null;
+ if (node.get_content_type() != null)
+ this_content_type = new Mime.ContentType.from_gmime(node.get_content_type());
+
+ Mime.MultipartSubtype this_subtype = Mime.MultipartSubtype.from_content_type(this_content_type,
+ null);
+
// If this is a multipart, call ourselves recursively on the children
GMime.Multipart? multipart = node as GMime.Multipart;
if (multipart != null) {
bool found_text_subtype = false;
+
StringBuilder builder = new StringBuilder();
int count = multipart.get_count();
for (int i = 0; i < count; ++i) {
GMime.Object child = multipart.get_part(i);
+
string? child_body = null;
found_text_subtype |= construct_body_from_mime_parts(ref child_body, replacer,
- text_subtype, allow_only_replaced, to_html, child);
+ text_subtype, to_html, child, this_subtype);
if (child_body != null)
builder.append(child_body);
}
if (!Geary.String.is_empty_or_whitespace(builder.str))
body = builder.str;
+
return found_text_subtype;
}
@@ -495,34 +509,35 @@ public class Geary.RFC822.Message : BaseObject {
/* Handle text parts that are not attachments
* They may have inline disposition, or they may have no disposition specified
*/
- Mime.ContentType? content_type = null;
- if (part.get_content_type() != null) {
- content_type = new Mime.ContentType.from_gmime(part.get_content_type());
- if (content_type.has_media_type("text")) {
- if (content_type.has_media_subtype(text_subtype)) {
- body = mime_part_to_memory_buffer(part, true, to_html).to_string();
- return true;
- }
+ if (this_content_type != null && this_content_type.has_media_type("text")) {
+ if (this_content_type.has_media_subtype(text_subtype)) {
+ body = mime_part_to_memory_buffer(part, true, to_html).to_string();
- // We were the wrong kind of text part
- return false;
+ return true;
}
+
+ // We were the wrong kind of text part
+ return false;
}
// If images have no disposition, they are handled elsewhere; see #7299
if (disposition == null || disposition.disposition_type == Mime.DispositionType.UNSPECIFIED)
return false;
- if (replacer == null)
+ // Use inline part replacer *only* if in a mixed multipart where each element is to be
+ // presented to the user as structure dictates; for alternative and related, the inline
+ // part is referred to elsewhere in the document and it's the callers responsibility to
+ // locate them
+ if (replacer == null || container_subtype != Mime.MultipartSubtype.MIXED)
return false;
// Hand off to the replacer for processing
- string? replaced_part = replacer(RFC822.Utils.get_clean_attachment_filename(part), content_type,
- disposition, part.get_content_id(), mime_part_to_memory_buffer(part));
+ string? replaced_part = replacer(RFC822.Utils.get_clean_attachment_filename(part),
+ this_content_type, disposition, part.get_content_id(), mime_part_to_memory_buffer(part));
if (replaced_part != null)
body = replaced_part;
- return allow_only_replaced && (replaced_part != null);
+ return replaced_part != null;
}
/**
@@ -537,8 +552,8 @@ public class Geary.RFC822.Message : BaseObject {
private string? internal_get_body(bool allow_only_replaced, string text_subtype, bool to_html,
InlinePartReplacer? replacer) throws RFC822Error {
string? body = null;
- if (!construct_body_from_mime_parts(ref body, replacer, text_subtype, allow_only_replaced,
- to_html, null)) {
+ if (!construct_body_from_mime_parts(ref body, replacer, text_subtype, to_html, null,
+ Mime.MultipartSubtype.UNSPECIFIED)) {
throw new RFC822Error.NOT_FOUND("Could not find any \"text/%s\" parts", text_subtype);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]