Use GMime to display the text/plain portion of email messages: #3710
This makes available MIME portions of the message, although more work will need to be done to expose all MIME parts.
This commit is contained in:
parent
7f293f9a6f
commit
4b03ba004e
12 changed files with 551 additions and 148 deletions
|
|
@ -253,9 +253,13 @@ public class MainWindow : Gtk.Window {
|
|||
return;
|
||||
}
|
||||
|
||||
Geary.Email text = yield current_folder.fetch_email_async(email.location.position,
|
||||
Geary.Email.Field.BODY);
|
||||
message_buffer.set_text(text.body.buffer.to_ascii_string());
|
||||
Geary.Email full = yield current_folder.fetch_email_async(email.location.position,
|
||||
Geary.Email.Field.HEADER | Geary.Email.Field.BODY);
|
||||
|
||||
Geary.Memory.AbstractBuffer buffer = full.get_message().get_first_mime_part_of_content_type(
|
||||
"text/plain");
|
||||
|
||||
message_buffer.set_text(buffer.to_utf8());
|
||||
}
|
||||
|
||||
private void on_select_message_completed(Object? source, AsyncResult result) {
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ public class Geary.Email : Object {
|
|||
|
||||
public Geary.Email.Field fields { get; private set; default = Field.NONE; }
|
||||
|
||||
private Geary.RFC822.Message? message = null;
|
||||
|
||||
public Email(Geary.EmailLocation location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
|
@ -130,12 +132,18 @@ public class Geary.Email : Object {
|
|||
public void set_message_header(Geary.RFC822.Header header) {
|
||||
this.header = header;
|
||||
|
||||
// reset the message object, which is built from this text
|
||||
message = null;
|
||||
|
||||
fields |= Field.HEADER;
|
||||
}
|
||||
|
||||
public void set_message_body(Geary.RFC822.Text body) {
|
||||
this.body = body;
|
||||
|
||||
// reset the message object, which is built from this text
|
||||
message = null;
|
||||
|
||||
fields |= Field.BODY;
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +153,18 @@ public class Geary.Email : Object {
|
|||
fields |= Field.PROPERTIES;
|
||||
}
|
||||
|
||||
public Geary.RFC822.Message get_message() throws EngineError, RFC822.Error {
|
||||
if (message != null)
|
||||
return message;
|
||||
|
||||
if (!fields.fulfills(Field.HEADER | Field.BODY))
|
||||
throw new EngineError.INCOMPLETE_MESSAGE("Parsed email requires HEADER and BODY");
|
||||
|
||||
message = new Geary.RFC822.Message.from_parts(header, body);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
|
|||
message_id = null;
|
||||
|
||||
return new Envelope(new Geary.RFC822.Date(sent.value),
|
||||
new Geary.RFC822.Subject(subject.value),
|
||||
new Geary.RFC822.Subject.from_rfc822(subject.value),
|
||||
parse_addresses(from), parse_addresses(sender), parse_addresses(reply_to),
|
||||
(to != null) ? parse_addresses(to) : null,
|
||||
(cc != null) ? parse_addresses(cc) : null,
|
||||
|
|
|
|||
11
src/engine/rfc822/rfc822-error.vala
Normal file
11
src/engine/rfc822/rfc822-error.vala
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* Copyright 2011 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.
|
||||
*/
|
||||
|
||||
public errordomain Geary.RFC822.Error {
|
||||
INVALID,
|
||||
NOT_FOUND
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +48,10 @@ public class Geary.RFC822.Subject : Geary.Common.StringMessageData, Geary.RFC822
|
|||
public Subject(string value) {
|
||||
base (value);
|
||||
}
|
||||
|
||||
public Subject.from_rfc822(string value) {
|
||||
base (GMime.utils_header_decode_text(value));
|
||||
}
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Header : Geary.Common.BlockMessageData, Geary.RFC822.MessageData {
|
||||
|
|
|
|||
78
src/engine/rfc822/rfc822-message.vala
Normal file
78
src/engine/rfc822/rfc822-message.vala
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* Copyright 2011 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.
|
||||
*/
|
||||
|
||||
public class Geary.RFC822.Message : Object {
|
||||
private GMime.Message? message;
|
||||
|
||||
public Message(Full full) {
|
||||
GMime.Parser parser = new GMime.Parser.with_stream(
|
||||
new GMime.StreamMem.with_buffer(full.buffer.get_buffer()));
|
||||
|
||||
message = parser.construct_message();
|
||||
}
|
||||
|
||||
public Message.from_parts(Header header, Text body) {
|
||||
GMime.StreamCat stream_cat = new GMime.StreamCat();
|
||||
stream_cat.add_source(new GMime.StreamMem.with_buffer(header.buffer.get_buffer()));
|
||||
stream_cat.add_source(new GMime.StreamMem.with_buffer(body.buffer.get_buffer()));
|
||||
|
||||
GMime.Parser parser = new GMime.Parser.with_stream(stream_cat);
|
||||
|
||||
message = parser.construct_message();
|
||||
}
|
||||
|
||||
public bool is_decoded() {
|
||||
return message != null;
|
||||
}
|
||||
|
||||
public Geary.Memory.AbstractBuffer get_first_mime_part_of_content_type(string content_type)
|
||||
throws RFC822.Error {
|
||||
if (!is_decoded())
|
||||
throw new RFC822.Error.INVALID("Message could not be decoded");
|
||||
|
||||
// search for content type starting from the root
|
||||
GMime.Part? part = find_first_mime_part(message.get_mime_part(), content_type);
|
||||
if (part == null) {
|
||||
throw new RFC822.Error.NOT_FOUND("Could not find a MIME part with content-type %s",
|
||||
content_type);
|
||||
}
|
||||
|
||||
// convert payload to a buffer
|
||||
GMime.DataWrapper? wrapper = part.get_content_object();
|
||||
if (wrapper == null) {
|
||||
throw new RFC822.Error.INVALID("Could not get the content wrapper for content-type %s",
|
||||
content_type);
|
||||
}
|
||||
|
||||
ByteArray byte_array = new ByteArray();
|
||||
GMime.StreamMem stream = new GMime.StreamMem.with_byte_array(byte_array);
|
||||
stream.set_owner(false);
|
||||
|
||||
wrapper.write_to_stream(stream);
|
||||
|
||||
return new Geary.Memory.Buffer(byte_array.data, byte_array.len);
|
||||
}
|
||||
|
||||
private GMime.Part? find_first_mime_part(GMime.Object current_root, string content_type) {
|
||||
// descend looking for the content type in a GMime.Part
|
||||
GMime.Multipart? multipart = current_root as GMime.Multipart;
|
||||
if (multipart != null) {
|
||||
int count = multipart.get_count();
|
||||
for (int ctr = 0; ctr < count; ctr++) {
|
||||
GMime.Part? child_part = find_first_mime_part(multipart.get_part(ctr), content_type);
|
||||
if (child_part != null)
|
||||
return child_part;
|
||||
}
|
||||
}
|
||||
|
||||
GMime.Part? part = current_root as GMime.Part;
|
||||
if (part != null && part.get_content_type().to_string() == content_type)
|
||||
return part;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +77,7 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder {
|
|||
location.uid.to_string(), to_string());
|
||||
}
|
||||
|
||||
// TODO: The following steps should be atomic
|
||||
message_id = yield message_table.create_async(
|
||||
new MessageRow.from_email(message_table, email),
|
||||
cancellable);
|
||||
|
|
|
|||
|
|
@ -191,13 +191,13 @@ public class Geary.Sqlite.MessageRow : Geary.Sqlite.Row {
|
|||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.HEADER) != 0) {
|
||||
header = (email.header != null) ? email.header.buffer.to_ascii_string() : null;
|
||||
header = (email.header != null) ? email.header.buffer.to_utf8() : null;
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.HEADER);
|
||||
}
|
||||
|
||||
if ((fields & Geary.Email.Field.BODY) != 0) {
|
||||
body = (email.body != null) ? email.body.buffer.to_ascii_string() : null;
|
||||
body = (email.body != null) ? email.body.buffer.to_utf8() : null;
|
||||
|
||||
this.fields = this.fields.set(Geary.Email.Field.BODY);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,17 +20,18 @@ public abstract class Geary.Memory.AbstractBuffer {
|
|||
public abstract InputStream get_input_stream();
|
||||
|
||||
/**
|
||||
* Returns the contents of the buffer as though it was an ASCII (or 7-bit) string. Note that
|
||||
* Returns the contents of the buffer as though it was an UTF-8 string. Note that
|
||||
* this involves reading the entire buffer into memory.
|
||||
*
|
||||
* If the conversion fails or decodes as invalid UTF-8, an empty string is returned.
|
||||
*/
|
||||
public string to_ascii_string() {
|
||||
public string to_utf8() {
|
||||
uint8[] buffer = get_buffer();
|
||||
buffer += (uint8) '\0';
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (uint8 byte in buffer)
|
||||
builder.append_c((char) byte);
|
||||
string str = (string) buffer;
|
||||
|
||||
return builder.str;
|
||||
return str.validate() ? str : "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,10 @@ def build(bld):
|
|||
'../engine/imap/transport/imap-serializable.vala',
|
||||
'../engine/imap/transport/imap-serializer.vala',
|
||||
|
||||
'../engine/rfc822/rfc822-error.vala',
|
||||
'../engine/rfc822/rfc822-mailbox-addresses.vala',
|
||||
'../engine/rfc822/rfc822-mailbox-address.vala',
|
||||
'../engine/rfc822/rfc822-message.vala',
|
||||
'../engine/rfc822/rfc822-message-data.vala',
|
||||
|
||||
'../engine/sqlite/abstract/sqlite-database.vala',
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,24 @@
|
|||
GMime cheader_filename="gmime/gmime.h" lower_case_cprefix="g_mime_"
|
||||
GMime lower_case_cprefix="gmime_" cheader_filename="gmime/gmime.h"
|
||||
|
||||
GMimeObject abstract="1"
|
||||
GMimeStream abstract="1"
|
||||
|
||||
g_mime_header_list_get_iter.iter is_out="1"
|
||||
g_mime_message_get_date.date is_out="1"
|
||||
g_mime_message_get_date.tz_offset is_out="1"
|
||||
g_mime_message_get_mime_part is_nullable="1"
|
||||
g_mime_object_to_string transfer_ownership="1"
|
||||
g_mime_param_next name="get_next"
|
||||
g_mime_parser_construct_message nullable="1"
|
||||
g_mime_part_get_content_part nullable="1"
|
||||
g_mime_signer_next name="get_next"
|
||||
|
||||
g_mime_stream_mem_new_with_buffer.buffer is_array="1" array_length_pos="1.0" type_name="uint8[]"
|
||||
g_mime_stream_mem_new_with_buffer.len hidden="1"
|
||||
g_mime_utils_decode_8bit transfer_ownership="1"
|
||||
g_mime_utils_header_decode_date type_name="time_t"
|
||||
g_mime_utils_header_decode_date.tz_offset is_out="1" nullable="1"
|
||||
g_mime_utils_header_decode_phrase transfer_ownership="1"
|
||||
g_mime_utils_header_decode_text transfer_ownership="1"
|
||||
|
||||
InternetAddress hidden="1"
|
||||
internet_address_* hidden="1"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue