Geary.RFC822: Clean up message data interfaces and classes
Split Geary.RFC822.MessageData interface up into DecodedMessageData and EncodedMessageData so the difference between the two is clear and they can't be used interchangeably. Add `DecodedMessageData::to_rfc822_string` to provide a common interface for round-tripping decoded data. Update all classes to implement one of these and follow the same general API patterns.
This commit is contained in:
parent
5b253cbee6
commit
6e7631b8d3
10 changed files with 138 additions and 78 deletions
|
|
@ -106,7 +106,7 @@ private class Geary.ImapDB.MessageRow {
|
|||
if (fields.is_all_set(Geary.Email.Field.DATE)) {
|
||||
try {
|
||||
email.set_send_date(
|
||||
!String.is_empty(date) ? new RFC822.Date(date) : null
|
||||
!String.is_empty(date) ? new RFC822.Date.from_rfc822_string(date) : null
|
||||
);
|
||||
} catch (GLib.Error err) {
|
||||
debug("Error loading message date from db: %s", err.message);
|
||||
|
|
@ -133,7 +133,7 @@ private class Geary.ImapDB.MessageRow {
|
|||
}
|
||||
|
||||
if (fields.is_all_set(Geary.Email.Field.SUBJECT))
|
||||
email.set_message_subject(new RFC822.Subject.decode(subject ?? ""));
|
||||
email.set_message_subject(new RFC822.Subject.from_rfc822_string(subject ?? ""));
|
||||
|
||||
if (fields.is_all_set(Geary.Email.Field.HEADER))
|
||||
email.set_message_header(new RFC822.Header(header ?? Memory.EmptyBuffer.instance));
|
||||
|
|
@ -191,7 +191,7 @@ private class Geary.ImapDB.MessageRow {
|
|||
// null if empty
|
||||
|
||||
if (email.fields.is_all_set(Geary.Email.Field.DATE)) {
|
||||
date = (email.date != null) ? email.date.original : null;
|
||||
date = (email.date != null) ? email.date.to_rfc822_string() : null;
|
||||
date_time_t = (email.date != null) ? email.date.value.to_unix() : -1;
|
||||
|
||||
fields = fields.set(Geary.Email.Field.DATE);
|
||||
|
|
@ -222,7 +222,7 @@ private class Geary.ImapDB.MessageRow {
|
|||
}
|
||||
|
||||
if (email.fields.is_all_set(Geary.Email.Field.SUBJECT)) {
|
||||
subject = (email.subject != null) ? email.subject.original : null;
|
||||
subject = (email.subject != null) ? email.subject.to_rfc822_string() : null;
|
||||
|
||||
fields = fields.set(Geary.Email.Field.SUBJECT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -901,7 +901,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
|
|||
RFC822.Date? date = null;
|
||||
if (!String.is_empty(value)) {
|
||||
try {
|
||||
date = new RFC822.Date(value);
|
||||
date = new RFC822.Date.from_rfc822_string(value);
|
||||
} catch (GLib.Error err) {
|
||||
warning(
|
||||
"Error parsing date from FETCH response: %s",
|
||||
|
|
@ -979,7 +979,7 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
|
|||
if (required_but_not_set(Geary.Email.Field.SUBJECT, required_fields, email)) {
|
||||
string? value = headers.get("Subject");
|
||||
if (value != null)
|
||||
email.set_message_subject(new RFC822.Subject.decode(value));
|
||||
email.set_message_subject(new RFC822.Subject.from_rfc822_string(value));
|
||||
else
|
||||
email.set_message_subject(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
|
|||
Geary.RFC822.Date? sent_date = null;
|
||||
if (sent != null) {
|
||||
try {
|
||||
sent_date = new RFC822.Date(sent.ascii);
|
||||
sent_date = new RFC822.Date.from_rfc822_string(sent.ascii);
|
||||
} catch (GLib.Error err) {
|
||||
debug(
|
||||
"Error parsing sent date from FETCH envelope: %s",
|
||||
|
|
@ -159,7 +159,7 @@ public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
|
|||
|
||||
return new Envelope(
|
||||
sent_date,
|
||||
new Geary.RFC822.Subject.decode(subject.ascii),
|
||||
new Geary.RFC822.Subject.from_rfc822_string(subject.ascii),
|
||||
parse_addresses(from), parse_addresses(sender), parse_addresses(reply_to),
|
||||
(to != null) ? parse_addresses(to) : null,
|
||||
(cc != null) ? parse_addresses(cc) : null,
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@
|
|||
* See [[https://tools.ietf.org/html/rfc5322#section-3.4]]
|
||||
*/
|
||||
public class Geary.RFC822.MailboxAddress :
|
||||
Geary.MessageData.AbstractMessageData,
|
||||
Geary.MessageData.SearchableMessageData,
|
||||
Gee.Hashable<MailboxAddress>,
|
||||
BaseObject {
|
||||
DecodedMessageData {
|
||||
|
||||
private static unichar[] ATEXT = {
|
||||
'!', '#', '$', '%', '&', '\'', '*', '+', '-',
|
||||
|
|
@ -551,7 +552,7 @@ public class Geary.RFC822.MailboxAddress :
|
|||
*
|
||||
* @see to_rfc822_string
|
||||
*/
|
||||
public string to_string() {
|
||||
public override string to_string() {
|
||||
return to_rfc822_string();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
public class Geary.RFC822.MailboxAddresses :
|
||||
Geary.MessageData.AbstractMessageData,
|
||||
Geary.MessageData.SearchableMessageData,
|
||||
Geary.RFC822.MessageData, Gee.Hashable<MailboxAddresses> {
|
||||
Gee.Hashable<MailboxAddresses>,
|
||||
DecodedMessageData {
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,46 +1,75 @@
|
|||
/* Copyright 2016 Software Freedom Conservancy Inc.
|
||||
/*
|
||||
* Copyright © 2016 Software Freedom Conservancy Inc.
|
||||
* Copyright © 2020 Michael Gratton <mike@vee.net>
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* RFC822.MessageData represents a base class for all the various elements that may be present in
|
||||
* an RFC822 message header. Note that some common elements (such as MailAccount) are not
|
||||
* MessageData because they exist in an RFC822 header in list (i.e. multiple email addresses) form.
|
||||
* A base interface for objects that represent decoded RFC822 headers.
|
||||
*
|
||||
* The value of these objects is the decoded form of the header
|
||||
* data. Encoded forms can be obtained via {@link to_rfc822_string}.
|
||||
*/
|
||||
public interface Geary.RFC822.DecodedMessageData :
|
||||
Geary.MessageData.AbstractMessageData {
|
||||
|
||||
/** Returns an RFC822-safe string representation of the data. */
|
||||
public abstract string to_rfc822_string();
|
||||
|
||||
public interface Geary.RFC822.MessageData : Geary.MessageData.AbstractMessageData {
|
||||
}
|
||||
|
||||
/**
|
||||
* An RFC822 Message-ID.
|
||||
* A base interface for objects that represent encoded RFC822 header data.
|
||||
*
|
||||
* MessageID will normalize all strings so that they begin and end with the proper brackets ("<" and
|
||||
* ">").
|
||||
* The value of these objects is the RFC822 encoded form of the header
|
||||
* data. Decoded forms can be obtained via means specific to
|
||||
* implementations of this interface.
|
||||
*/
|
||||
public class Geary.RFC822.MessageID : Geary.MessageData.StringMessageData, Geary.RFC822.MessageData {
|
||||
public interface Geary.RFC822.EncodedMessageData :
|
||||
Geary.MessageData.BlockMessageData {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A RFC822 Message-ID.
|
||||
*
|
||||
* The decoded form of the id is the `addr-spec` portion, that is,
|
||||
* without the leading `<` and tailing `>`.
|
||||
*/
|
||||
public class Geary.RFC822.MessageID :
|
||||
Geary.MessageData.StringMessageData, DecodedMessageData {
|
||||
|
||||
private string rfc822;
|
||||
|
||||
public MessageID(string value) {
|
||||
string? normalized = normalize(value);
|
||||
base (normalized ?? value);
|
||||
base(value);
|
||||
}
|
||||
|
||||
// Adds brackets if required, null if no change required
|
||||
private static string? normalize(string value) {
|
||||
bool needs_prefix = !value.has_prefix("<");
|
||||
bool needs_suffix = !value.has_suffix(">");
|
||||
if (!needs_prefix && !needs_suffix)
|
||||
return null;
|
||||
|
||||
return "%s%s%s".printf(needs_prefix ? "<" : "", value, needs_suffix ? ">" : "");
|
||||
public MessageID.from_rfc822_string(string rfc822) {
|
||||
base(GMime.utils_decode_message_id(rfc822));
|
||||
this.rfc822 = rfc822;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Date} in RFC 822 format.
|
||||
*/
|
||||
public string to_rfc822_string() {
|
||||
if (this.rfc822 == null) {
|
||||
this.rfc822 = "<%s>".printf(this.value);
|
||||
}
|
||||
return this.rfc822;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A immutable list of RFC822 Message-ID values.
|
||||
*/
|
||||
public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData, Geary.RFC822.MessageData {
|
||||
public Gee.List<MessageID> list { get; private set; }
|
||||
public class Geary.RFC822.MessageIDList :
|
||||
Geary.MessageData.AbstractMessageData,
|
||||
DecodedMessageData {
|
||||
|
||||
|
||||
/** Returns the number of ids in this list. */
|
||||
|
|
@ -189,71 +218,87 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
|
|||
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Date : Geary.RFC822.MessageData, Geary.MessageData.AbstractMessageData,
|
||||
Gee.Hashable<Geary.RFC822.Date> {
|
||||
public class Geary.RFC822.Date :
|
||||
Geary.MessageData.AbstractMessageData,
|
||||
Gee.Hashable<Geary.RFC822.Date>,
|
||||
DecodedMessageData {
|
||||
|
||||
public string original { get; private set; }
|
||||
public DateTime value { get; private set; }
|
||||
|
||||
public Date(string rfc822) throws RFC822Error {
|
||||
public GLib.DateTime value { get; private set; }
|
||||
|
||||
private string? rfc822;
|
||||
|
||||
|
||||
public Date(GLib.DateTime datetime) {
|
||||
this.value = datetime;
|
||||
this.rfc822 = null;
|
||||
}
|
||||
|
||||
public Date.from_rfc822_string(string rfc822) throws RFC822Error {
|
||||
var date = GMime.utils_header_decode_date(rfc822);
|
||||
if (date == null) {
|
||||
throw new RFC822Error.INVALID("Not ISO-8601 date: %s", rfc822);
|
||||
}
|
||||
this.rfc822 = rfc822;
|
||||
this.value = date;
|
||||
this.original = rfc822;
|
||||
}
|
||||
|
||||
public Date.from_date_time(DateTime datetime) {
|
||||
this.original = null;
|
||||
this.value = datetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Date} in RFC 822 format.
|
||||
*/
|
||||
public string to_rfc822_string() {
|
||||
return GMime.utils_header_format_date(this.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Date} for transmission.
|
||||
*
|
||||
* @see to_rfc822_string
|
||||
*/
|
||||
public virtual string serialize() {
|
||||
return to_rfc822_string();
|
||||
if (this.rfc822 == null) {
|
||||
this.rfc822 = GMime.utils_header_format_date(this.value);
|
||||
}
|
||||
return this.rfc822;
|
||||
}
|
||||
|
||||
public virtual bool equal_to(Geary.RFC822.Date other) {
|
||||
return (this != other) ? value.equal(other.value) : true;
|
||||
return this == other || this.value.equal(other.value);
|
||||
}
|
||||
|
||||
public virtual uint hash() {
|
||||
return value.hash();
|
||||
return this.value.hash();
|
||||
}
|
||||
|
||||
public override string to_string() {
|
||||
return original ?? value.to_string();
|
||||
return this.value.to_string();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
|
||||
Geary.MessageData.SearchableMessageData, Geary.RFC822.MessageData {
|
||||
public class Geary.RFC822.Subject :
|
||||
Geary.MessageData.StringMessageData,
|
||||
Geary.MessageData.SearchableMessageData,
|
||||
DecodedMessageData {
|
||||
|
||||
public const string REPLY_PREFACE = "Re:";
|
||||
public const string FORWARD_PREFACE = "Fwd:";
|
||||
|
||||
public string original { get; private set; }
|
||||
|
||||
private string rfc822;
|
||||
|
||||
|
||||
public Subject(string value) {
|
||||
base (value);
|
||||
original = value;
|
||||
base(value);
|
||||
this.rfc822 = null;
|
||||
}
|
||||
|
||||
public Subject.decode(string value) {
|
||||
base (GMime.utils_header_decode_text(Geary.RFC822.get_parser_options(), value));
|
||||
original = value;
|
||||
public Subject.from_rfc822_string(string rfc822) {
|
||||
base(GMime.utils_header_decode_text(get_parser_options(), rfc822));
|
||||
this.rfc822 = rfc822;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subject line encoded for an RFC 822 message.
|
||||
*/
|
||||
public string to_rfc822_string() {
|
||||
if (this.rfc822 == null) {
|
||||
this.rfc822 = GMime.utils_header_encode_text(
|
||||
get_format_options(), this.value, null
|
||||
);
|
||||
}
|
||||
return this.rfc822;
|
||||
}
|
||||
|
||||
public bool is_reply() {
|
||||
|
|
@ -312,9 +357,13 @@ public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
|
|||
public string to_searchable_string() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Header : Geary.MessageData.BlockMessageData, Geary.RFC822.MessageData {
|
||||
public class Geary.RFC822.Header :
|
||||
Geary.MessageData.BlockMessageData, EncodedMessageData {
|
||||
|
||||
|
||||
private GMime.Message? message = null;
|
||||
private string[]? names = null;
|
||||
|
||||
|
|
@ -353,22 +402,30 @@ public class Geary.RFC822.Header : Geary.MessageData.BlockMessageData, Geary.RFC
|
|||
}
|
||||
return this.names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Text : Geary.MessageData.BlockMessageData, Geary.RFC822.MessageData {
|
||||
public class Geary.RFC822.Text :
|
||||
Geary.MessageData.BlockMessageData, EncodedMessageData {
|
||||
|
||||
public Text(Memory.Buffer buffer) {
|
||||
base ("RFC822.Text", buffer);
|
||||
base("RFC822.Text", buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Geary.RFC822.Full : Geary.MessageData.BlockMessageData, Geary.RFC822.MessageData {
|
||||
public class Geary.RFC822.Full :
|
||||
Geary.MessageData.BlockMessageData, EncodedMessageData {
|
||||
|
||||
public Full(Memory.Buffer buffer) {
|
||||
base ("RFC822.Full", buffer);
|
||||
base("RFC822.Full", buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Used for decoding preview text.
|
||||
/** Represents text providing a preview of an email's body. */
|
||||
public class Geary.RFC822.PreviewText : Geary.RFC822.Text {
|
||||
|
||||
public PreviewText(Memory.Buffer _buffer) {
|
||||
base (_buffer);
|
||||
}
|
||||
|
|
@ -415,4 +472,5 @@ public class Geary.RFC822.PreviewText : Geary.RFC822.Text {
|
|||
public PreviewText.from_string(string preview) {
|
||||
base (new Geary.Memory.StringBuffer(preview));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
|
|||
references.message_id
|
||||
);
|
||||
}
|
||||
email.set_send_date(new Geary.RFC822.Date.from_date_time(now));
|
||||
email.set_send_date(new RFC822.Date(now));
|
||||
email.set_email_properties(new MockEmailProperties(now));
|
||||
email.set_full_references(mid, null, refs_list);
|
||||
return email;
|
||||
|
|
|
|||
|
|
@ -489,7 +489,7 @@ class Geary.App.ConversationSetTest : TestCase {
|
|||
references.message_id
|
||||
);
|
||||
}
|
||||
email.set_send_date(new Geary.RFC822.Date.from_date_time(now));
|
||||
email.set_send_date(new RFC822.Date(now));
|
||||
email.set_email_properties(new MockEmailProperties(now));
|
||||
email.set_full_references(mid, null, refs_list);
|
||||
return email;
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ class Geary.App.ConversationTest : TestCase {
|
|||
);
|
||||
email.set_full_references(mid, null, null);
|
||||
email.set_email_properties(new MockEmailProperties(now));
|
||||
email.set_send_date(new Geary.RFC822.Date.from_date_time(now));
|
||||
email.set_send_date(new RFC822.Date(now));
|
||||
return email;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class Geary.RFC822.MessageDataTest : TestCase {
|
|||
|
||||
public void date_from_rfc822() throws GLib.Error {
|
||||
const string FULL_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 -0100";
|
||||
Date full_hour_tz = new Date(FULL_HOUR_TZ);
|
||||
Date full_hour_tz = new Date.from_rfc822_string(FULL_HOUR_TZ);
|
||||
assert_int64(
|
||||
((int64) (-1 * 3600)) * 1000 * 1000,
|
||||
full_hour_tz.value.get_utc_offset(),
|
||||
|
|
@ -81,7 +81,7 @@ class Geary.RFC822.MessageDataTest : TestCase {
|
|||
);
|
||||
|
||||
const string HALF_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 +1030";
|
||||
Date half_hour_tz = new Date(HALF_HOUR_TZ);
|
||||
Date half_hour_tz = new Date.from_rfc822_string(HALF_HOUR_TZ);
|
||||
assert_int64(
|
||||
((int64) (10.5 * 3600)) * 1000 * 1000,
|
||||
half_hour_tz.value.get_utc_offset()
|
||||
|
|
@ -96,15 +96,15 @@ class Geary.RFC822.MessageDataTest : TestCase {
|
|||
|
||||
public void date_to_rfc822() throws GLib.Error {
|
||||
const string FULL_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 -0100";
|
||||
Date full_hour_tz = new Date(FULL_HOUR_TZ);
|
||||
Date full_hour_tz = new Date.from_rfc822_string(FULL_HOUR_TZ);
|
||||
assert_string(FULL_HOUR_TZ, full_hour_tz.to_rfc822_string());
|
||||
|
||||
const string HALF_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 +1030";
|
||||
Date half_hour_tz = new Date(HALF_HOUR_TZ);
|
||||
Date half_hour_tz = new Date.from_rfc822_string(HALF_HOUR_TZ);
|
||||
assert_string(HALF_HOUR_TZ, half_hour_tz.to_rfc822_string());
|
||||
|
||||
const string NEG_HALF_HOUR_TZ = "Thu, 28 Feb 2019 00:00:00 -1030";
|
||||
Date neg_half_hour_tz = new Date(NEG_HALF_HOUR_TZ);
|
||||
Date neg_half_hour_tz = new Date.from_rfc822_string(NEG_HALF_HOUR_TZ);
|
||||
assert_string(NEG_HALF_HOUR_TZ, neg_half_hour_tz.to_rfc822_string());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue