geary/src/engine/imap/response/imap-fetch-data-decoder.vala
Michael Gratton d450e8a394 Clean up Imap.LiteralCommand API
Expose value buffer as a RO property, remove redundant method calls,
fix call sites.
2019-08-29 23:23:19 +10:00

226 lines
8.9 KiB
Vala

/* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
* FetchDataDecoder accept the result line of a FETCH response and decodes it into MessageData.
* While they can be used standalone, they're intended to be used by FetchResults to process
* a CommandResponse.
*
* Note that FetchDataDecoders are keyed off of FetchDataType; new implementations should add
* themselves to FetchDataType.get_decoder().
*
* In the future FetchDataDecoder may be used to decode MessageData stored in other formats, such
* as in a database.
*/
public abstract class Geary.Imap.FetchDataDecoder : BaseObject {
public FetchDataSpecifier data_item { get; private set; }
protected FetchDataDecoder(FetchDataSpecifier data_item) {
this.data_item = data_item;
}
/*
* The default implementation determines the type of the parameter and calls the appropriate
* virtual function; most implementations of a FetchResponseDecoder shouldn't need to override
* this method.
*/
public virtual MessageData decode(Parameter param) throws ImapError {
StringParameter? stringp = param as StringParameter;
if (stringp != null)
return decode_string(stringp);
ListParameter? listp = param as ListParameter;
if (listp != null)
return decode_list(listp);
LiteralParameter? literalp = param as LiteralParameter;
if (literalp != null) {
// because this method is called without the help of get_as_string() (which converts
// reasonably-length literals into StringParameters), do so here manually
try {
if (literalp.value.size <= ListParameter.MAX_STRING_LITERAL_LENGTH)
return decode_string(literalp.coerce_to_string_parameter());
} catch (ImapError imap_err) {
// if decode_string() throws a TYPE_ERROR, retry as a LiteralParameter, otherwise
// relay the exception to the caller
if (!(imap_err is ImapError.TYPE_ERROR))
throw imap_err;
}
return decode_literal(literalp);
}
NilParameter? nilp = param as NilParameter;
if (nilp != null)
return decode_nil(nilp);
// bad news; this means this function isn't handling a Parameter type properly
assert_not_reached();
}
protected virtual MessageData decode_string(StringParameter param) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a string parameter", data_item.to_string());
}
protected virtual MessageData decode_list(ListParameter list) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a list parameter", data_item.to_string());
}
protected virtual MessageData decode_literal(LiteralParameter literal) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a literal parameter", data_item.to_string());
}
protected virtual MessageData decode_nil(NilParameter nil) throws ImapError {
throw new ImapError.TYPE_ERROR("%s does not accept a nil parameter", data_item.to_string());
}
}
public class Geary.Imap.UIDDecoder : Geary.Imap.FetchDataDecoder {
public UIDDecoder() {
base (FetchDataSpecifier.UID);
}
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
return new UID.checked(stringp.as_int64());
}
}
public class Geary.Imap.MessageFlagsDecoder : Geary.Imap.FetchDataDecoder {
public MessageFlagsDecoder() {
base (FetchDataSpecifier.FLAGS);
}
protected override MessageData decode_list(ListParameter listp) throws ImapError {
Gee.List<MessageFlag> flags = new Gee.ArrayList<MessageFlag>();
for (int ctr = 0; ctr < listp.size; ctr++)
flags.add(new MessageFlag(listp.get_as_string(ctr).ascii));
return new MessageFlags(flags);
}
}
public class Geary.Imap.InternalDateDecoder : Geary.Imap.FetchDataDecoder {
public InternalDateDecoder() {
base (FetchDataSpecifier.INTERNALDATE);
}
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
return InternalDate.decode(stringp.ascii);
}
}
public class Geary.Imap.RFC822SizeDecoder : Geary.Imap.FetchDataDecoder {
public RFC822SizeDecoder() {
base (FetchDataSpecifier.RFC822_SIZE);
}
protected override MessageData decode_string(StringParameter stringp) throws ImapError {
return new RFC822Size(stringp.as_int64(0, int64.MAX));
}
}
public class Geary.Imap.EnvelopeDecoder : Geary.Imap.FetchDataDecoder {
public EnvelopeDecoder() {
base (FetchDataSpecifier.ENVELOPE);
}
protected override MessageData decode_list(ListParameter listp) throws ImapError {
StringParameter? sent = listp.get_as_nullable_string(0);
StringParameter subject = listp.get_as_empty_string(1);
ListParameter from = listp.get_as_empty_list(2);
ListParameter sender = listp.get_as_empty_list(3);
ListParameter reply_to = listp.get_as_empty_list(4);
ListParameter? to = listp.get_as_nullable_list(5);
ListParameter? cc = listp.get_as_nullable_list(6);
ListParameter? bcc = listp.get_as_nullable_list(7);
StringParameter? in_reply_to = listp.get_as_nullable_string(8);
StringParameter? message_id = listp.get_as_nullable_string(9);
// Although Message-ID is required to be returned by IMAP, it may be blank if the email
// does not supply it (optional according to RFC822); deal with this cognitive dissonance
if (message_id != null && message_id.is_empty())
message_id = null;
Geary.RFC822.Date? sent_date = null;
if (sent != null) {
try {
sent_date = new RFC822.Date(sent.ascii);
} catch (GLib.Error err) {
debug(
"Error parsing sent date from FETCH envelope: %s",
err.message
);
}
}
return new Envelope(
sent_date,
new Geary.RFC822.Subject.decode(subject.ascii),
parse_addresses(from), parse_addresses(sender), parse_addresses(reply_to),
(to != null) ? parse_addresses(to) : null,
(cc != null) ? parse_addresses(cc) : null,
(bcc != null) ? parse_addresses(bcc) : null,
(in_reply_to != null) ? new Geary.RFC822.MessageIDList.from_rfc822_string(in_reply_to.ascii) : null,
(message_id != null) ? new Geary.RFC822.MessageID(message_id.ascii) : null);
}
// TODO: This doesn't handle group lists (see Johnson, p.268) -- this will throw an
// ImapError.TYPE_ERROR if this occurs.
private Geary.RFC822.MailboxAddresses? parse_addresses(ListParameter listp) throws ImapError {
Gee.List<Geary.RFC822.MailboxAddress> list = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
for (int ctr = 0; ctr < listp.size; ctr++) {
ListParameter fields = listp.get_as_empty_list(ctr);
StringParameter? name = fields.get_as_nullable_string(0);
StringParameter? source_route = fields.get_as_nullable_string(1);
StringParameter mailbox = fields.get_as_empty_string(2);
StringParameter domain = fields.get_as_empty_string(3);
Geary.RFC822.MailboxAddress addr = new Geary.RFC822.MailboxAddress.imap(
(name != null) ? name.nullable_ascii : null,
(source_route != null) ? source_route.nullable_ascii : null,
mailbox.ascii,
domain.ascii);
list.add(addr);
}
return new Geary.RFC822.MailboxAddresses(list);
}
}
public class Geary.Imap.RFC822HeaderDecoder : Geary.Imap.FetchDataDecoder {
public RFC822HeaderDecoder() {
base (FetchDataSpecifier.RFC822_HEADER);
}
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
return new Geary.Imap.RFC822Header(literalp.value);
}
}
public class Geary.Imap.RFC822TextDecoder : Geary.Imap.FetchDataDecoder {
public RFC822TextDecoder() {
base (FetchDataSpecifier.RFC822_TEXT);
}
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
return new Geary.Imap.RFC822Text(literalp.value);
}
protected override MessageData decode_nil(NilParameter nilp) throws ImapError {
return new Geary.Imap.RFC822Text(Memory.EmptyBuffer.instance);
}
}
public class Geary.Imap.RFC822FullDecoder : Geary.Imap.FetchDataDecoder {
public RFC822FullDecoder() {
base (FetchDataSpecifier.RFC822);
}
protected override MessageData decode_literal(LiteralParameter literalp) throws ImapError {
return new Geary.Imap.RFC822Full(literalp.value);
}
}