2022-03-19 17:56:10 +01:00
|
|
|
/*
|
|
|
|
** Copyright (C) 2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
**
|
|
|
|
** This program is free software; you can redistribute it and/or modify it
|
|
|
|
** under the terms of the GNU General Public License as published by the
|
|
|
|
** Free Software Foundation; either version 3, or (at your option) any
|
|
|
|
** later version.
|
|
|
|
**
|
|
|
|
** This program is distributed in the hope that it will be useful,
|
|
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
** GNU General Public License for more details.
|
|
|
|
**
|
|
|
|
** You should have received a copy of the GNU General Public License
|
|
|
|
** along with this program; if not, write to the Free Software Foundation,
|
|
|
|
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef MU_MESSAGE_HH__
|
|
|
|
#define MU_MESSAGE_HH__
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#include "mu-contact.hh"
|
|
|
|
#include "mu-priority.hh"
|
|
|
|
#include "mu-flags.hh"
|
|
|
|
#include "mu-fields.hh"
|
|
|
|
#include "mu-document.hh"
|
2022-03-26 15:19:08 +01:00
|
|
|
#include "mu-message-part.hh"
|
2022-03-19 17:56:10 +01:00
|
|
|
|
2022-04-18 20:56:49 +02:00
|
|
|
#include "utils/mu-utils.hh"
|
2022-03-28 21:38:37 +02:00
|
|
|
#include "utils/mu-option.hh"
|
2022-03-26 15:19:08 +01:00
|
|
|
#include "utils/mu-result.hh"
|
2022-04-04 22:48:33 +02:00
|
|
|
#include "utils/mu-sexp.hh"
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
namespace Mu {
|
|
|
|
|
|
|
|
class Message {
|
|
|
|
public:
|
2022-04-18 20:56:49 +02:00
|
|
|
enum struct Options {
|
|
|
|
None = 0, /**< Defaults */
|
|
|
|
Decrypt = 1 << 0, /**< Attempt to decrypt */
|
|
|
|
RetrieveKeys = 1 << 1, /**< Auto-retrieve crypto keys (implies network
|
|
|
|
* access) */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2022-03-26 15:19:08 +01:00
|
|
|
/**
|
|
|
|
* Move CTOR
|
|
|
|
*
|
|
|
|
* @param some other message
|
|
|
|
*/
|
|
|
|
Message(Message&& msg);
|
|
|
|
|
2022-03-19 17:56:10 +01:00
|
|
|
/**
|
2022-03-28 21:38:37 +02:00
|
|
|
* Construct a message based on a path. The maildir is optional; however
|
|
|
|
* messages without maildir cannot be stored in the database
|
2022-03-19 17:56:10 +01:00
|
|
|
*
|
2022-04-18 20:56:49 +02:00
|
|
|
* @param opts options
|
2022-03-19 17:56:10 +01:00
|
|
|
* @param path path to message
|
2022-03-28 21:38:37 +02:00
|
|
|
* @param mdir the maildir for this message; i.e, if the path is
|
2022-03-19 17:56:10 +01:00
|
|
|
* ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar; you can
|
2022-04-09 00:28:26 +02:00
|
|
|
* pass Nothing for this parameter, in which case some maildir-specific
|
2022-03-19 17:56:10 +01:00
|
|
|
* information is not available.
|
2022-03-26 15:19:08 +01:00
|
|
|
*
|
|
|
|
* @return a message or an error
|
2022-03-19 17:56:10 +01:00
|
|
|
*/
|
2022-04-18 20:56:49 +02:00
|
|
|
static Result<Message> make_from_path(Options opts,
|
|
|
|
const std::string& path,
|
2022-04-09 00:28:26 +02:00
|
|
|
const std::string& mdir={}) try {
|
2022-04-18 20:56:49 +02:00
|
|
|
return Ok(Message{opts, path, mdir});
|
2022-03-26 15:19:08 +01:00
|
|
|
} catch (Error& err) {
|
|
|
|
return Err(err);
|
|
|
|
} catch (...) {
|
|
|
|
return Err(Mu::Error(Error::Code::Message, "failed to create message"));
|
|
|
|
}
|
|
|
|
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a message based on a Message::Document
|
|
|
|
*
|
|
|
|
* @param doc
|
|
|
|
*
|
2022-03-26 15:19:08 +01:00
|
|
|
* @return a message or an error
|
2022-03-19 17:56:10 +01:00
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
static Result<Message> make_from_document(Document& doc) try {
|
|
|
|
return Ok(Message{doc});
|
|
|
|
} catch (Error& err) {
|
|
|
|
return Err(err);
|
|
|
|
} catch (...) {
|
|
|
|
return Err(Mu::Error(Error::Code::Message, "failed to create message"));
|
2022-03-19 17:56:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-03-26 15:19:08 +01:00
|
|
|
* Construct a message from a string. This is mostly useful for testing.
|
|
|
|
*
|
2022-04-18 20:56:49 +02:00
|
|
|
* @param opts options
|
2022-03-26 15:19:08 +01:00
|
|
|
* @param text message text
|
2022-04-09 00:28:26 +02:00
|
|
|
* @param path path to message - optional; path does not have to exist.
|
|
|
|
* this is useful for testing.
|
|
|
|
* @param mdir the maildir for this message; optional, useful for testing.
|
2022-03-19 17:56:10 +01:00
|
|
|
*
|
2022-03-26 15:19:08 +01:00
|
|
|
* @return a message or an error
|
2022-03-19 17:56:10 +01:00
|
|
|
*/
|
2022-04-18 20:56:49 +02:00
|
|
|
static Result<Message> make_from_text(Options opts,
|
|
|
|
const std::string& text,
|
2022-04-09 00:28:26 +02:00
|
|
|
const std::string& path={},
|
|
|
|
const std::string& mdir={}) try {
|
2022-04-18 20:56:49 +02:00
|
|
|
return Ok(Message{opts, text, path, mdir});
|
2022-03-26 15:19:08 +01:00
|
|
|
} catch (Error& err) {
|
|
|
|
return Err(err);
|
|
|
|
} catch (...) {
|
2022-04-09 00:28:26 +02:00
|
|
|
return Err(Mu::Error(Error::Code::Message,
|
|
|
|
"failed to create message from text"));
|
2022-03-19 17:56:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DTOR
|
|
|
|
*/
|
|
|
|
~Message();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the document.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @return document
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
const Document& document() const;
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the file system path of this message
|
|
|
|
*
|
|
|
|
* @return the path of this Message or NULL in case of error.
|
|
|
|
* the returned string should *not* be modified or freed.
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
std::string path() const { return document().string_value(Field::Id::Path); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the sender (From:) of this message
|
|
|
|
*
|
|
|
|
* @return the sender(s) of this Message
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
Contacts from() const { return document().contacts_value(Field::Id::From); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the recipient(s) (To:) for this message
|
|
|
|
*
|
|
|
|
* @return recipients
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
Contacts to() const { return document().contacts_value(Field::Id::To); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the recipient(s) (Cc:) for this message
|
|
|
|
*
|
|
|
|
* @return recipients
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
Contacts cc() const { return document().contacts_value(Field::Id::Cc); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the recipient(s) (Bcc:) for this message
|
|
|
|
*
|
|
|
|
* @return recipients
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
Contacts bcc() const { return document().contacts_value(Field::Id::Bcc); }
|
|
|
|
|
2022-03-19 17:56:10 +01:00
|
|
|
/**
|
|
|
|
* Get the maildir this message lives in; ie, if the path is
|
|
|
|
* ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar
|
|
|
|
*
|
|
|
|
* @return the maildir requested or empty */
|
2022-03-26 15:19:08 +01:00
|
|
|
std::string maildir() const { return document().string_value(Field::Id::Maildir); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the subject of this message
|
|
|
|
*
|
|
|
|
* @return the subject of this Message
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
std::string subject() const { return document().string_value(Field::Id::Subject); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the Message-Id of this message
|
|
|
|
*
|
|
|
|
* @return the Message-Id of this message (without the enclosing <>), or
|
2022-04-09 00:28:26 +02:00
|
|
|
* a fake message-id for messages that don't have them.
|
|
|
|
*
|
|
|
|
* For file-backed message, this fake message-id is based on a hash of the
|
|
|
|
* message contents. For non-file-backed (test) messages, some other value
|
|
|
|
* is concocted.
|
2022-03-19 17:56:10 +01:00
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
std::string message_id() const { return document().string_value(Field::Id::MessageId);}
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get the mailing list for a message, i.e. the mailing-list
|
|
|
|
* identifier in the List-Id header.
|
|
|
|
*
|
|
|
|
* @return the mailing list id for this message (without the enclosing <>)
|
|
|
|
* or NULL in case of error or if there is none.
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
std::string mailing_list() const { return document().string_value(Field::Id::MailingList);}
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get the message date/time (the Date: field) as time_t, using UTC
|
|
|
|
*
|
|
|
|
* @return message date/time or 0 in case of error or if there
|
|
|
|
* is no such header.
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
::time_t date() const { return static_cast<time_t>(document().integer_value(Field::Id::Date)); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
2022-04-09 00:28:26 +02:00
|
|
|
* get the flags for this message.
|
2022-03-19 17:56:10 +01:00
|
|
|
*
|
|
|
|
* @return the file/content flags
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
Flags flags() const { return document().flags_value(); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get the message priority for this message. The X-Priority, X-MSMailPriority,
|
|
|
|
* Importance and Precedence header are checked, in that order. if no known or
|
|
|
|
* explicit priority is set, Priority::Id::Normal is assumed
|
|
|
|
*
|
|
|
|
* @return the message priority
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
Priority priority() const { return document().priority_value(); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get the file size in bytes of this message
|
|
|
|
*
|
|
|
|
* @return the filesize
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
size_t size() const { return static_cast<size_t>(document().integer_value(Field::Id::Size)); }
|
2022-03-19 17:56:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get the list of references (consisting of both the References and
|
|
|
|
* In-Reply-To fields), with the oldest first and the direct parent as
|
|
|
|
* the last one. Note, any reference (message-id) will appear at most
|
|
|
|
* once, duplicates are filtered out.
|
|
|
|
*
|
|
|
|
* @return a vec with the references for this msg.
|
|
|
|
*/
|
|
|
|
std::vector<std::string> references() const {
|
2022-03-26 15:19:08 +01:00
|
|
|
return document().string_vec_value(Field::Id::References);
|
2022-03-19 17:56:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get the list of tags (ie., X-Label)
|
|
|
|
*
|
|
|
|
* @param msg a valid MuMsg
|
|
|
|
*
|
|
|
|
* @return a list with the tags for this msg. Don't modify/free
|
|
|
|
*/
|
|
|
|
std::vector<std::string> tags() const {
|
2022-03-26 15:19:08 +01:00
|
|
|
return document().string_vec_value(Field::Id::References);
|
2022-03-19 17:56:10 +01:00
|
|
|
}
|
|
|
|
|
2022-03-26 15:19:08 +01:00
|
|
|
|
|
|
|
/*
|
2022-04-04 22:48:33 +02:00
|
|
|
* Convert to Sexp
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* convert the message to a Lisp symbolic expression (for further
|
|
|
|
* processing in e.g. emacs)
|
|
|
|
*
|
2022-03-26 15:19:08 +01:00
|
|
|
*
|
2022-04-04 22:48:33 +02:00
|
|
|
* @return a Mu::Sexp or a Mu::Sexp::List representing the message.
|
2022-03-26 15:19:08 +01:00
|
|
|
*/
|
2022-04-04 22:48:33 +02:00
|
|
|
Mu::Sexp::List to_sexp_list() const;
|
|
|
|
Mu::Sexp to_sexp() const;
|
2022-03-26 15:19:08 +01:00
|
|
|
|
2022-04-04 22:48:33 +02:00
|
|
|
/*
|
|
|
|
* Below require a file-backed message, which is a relatively slow
|
|
|
|
* if there isn't one already; see load_mime_message()
|
|
|
|
*
|
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the text body
|
|
|
|
*
|
|
|
|
* @return text body
|
|
|
|
*/
|
|
|
|
Option<std::string> body_text() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the HTML body
|
|
|
|
*
|
|
|
|
* @return text body
|
|
|
|
*/
|
|
|
|
Option<std::string> body_html() const;
|
|
|
|
|
2022-03-19 17:56:10 +01:00
|
|
|
/**
|
|
|
|
* Get some message-header
|
|
|
|
*
|
|
|
|
* @param header_field name of the header
|
|
|
|
*
|
2022-03-26 15:19:08 +01:00
|
|
|
* @return the value (UTF-8), or Nothing.
|
|
|
|
*/
|
|
|
|
Option<std::string> header(const std::string& header_field) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all contacts for this message.
|
|
|
|
*
|
|
|
|
* @return contacts
|
|
|
|
*/
|
|
|
|
Contacts all_contacts() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get information about MIME-parts in this message.
|
|
|
|
*
|
|
|
|
* @return mime-part info.
|
2022-03-19 17:56:10 +01:00
|
|
|
*/
|
2022-03-26 15:19:08 +01:00
|
|
|
using Part = MessagePart;
|
|
|
|
const std::vector<Part>& parts() const;
|
2022-03-19 17:56:10 +01:00
|
|
|
|
2022-03-26 15:19:08 +01:00
|
|
|
/**
|
|
|
|
* Load the GMime (file) message (for a database-backed message),
|
|
|
|
* if not already (but see @param reload).
|
|
|
|
*
|
|
|
|
* Affects cached-state only, so we still mark this as 'const'
|
|
|
|
*
|
|
|
|
* @param reload whether to force reloading (even if already)
|
|
|
|
*
|
|
|
|
* @return true if loading worked; false otherwise.
|
|
|
|
*/
|
|
|
|
bool load_mime_message(bool reload=false) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear the GMime message.
|
|
|
|
*
|
|
|
|
* Affects cached-state only, so we still mark this as 'const'
|
|
|
|
*/
|
|
|
|
void unload_mime_message() const;
|
|
|
|
|
2022-04-04 22:48:33 +02:00
|
|
|
/**
|
|
|
|
* Has a (file-base) GMime message been loaded?
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
bool has_mime_message() const;
|
|
|
|
|
|
|
|
|
2022-03-26 15:19:08 +01:00
|
|
|
struct Private;
|
2022-03-19 17:56:10 +01:00
|
|
|
private:
|
2022-04-18 20:56:49 +02:00
|
|
|
Message(Options opts, const std::string& path, const std::string& mdir);
|
|
|
|
Message(Options opts, const std::string& str, const std::string& path,
|
2022-04-09 00:28:26 +02:00
|
|
|
const std::string& mdir);
|
2022-03-26 15:19:08 +01:00
|
|
|
Message(Document& doc);
|
|
|
|
|
|
|
|
std::unique_ptr<Private> priv_;
|
2022-03-19 17:56:10 +01:00
|
|
|
}; // Message
|
2022-04-18 20:56:49 +02:00
|
|
|
MU_ENABLE_BITOPS(Message::Options);
|
|
|
|
|
2022-03-19 17:56:10 +01:00
|
|
|
} // Mu
|
|
|
|
#endif /* MU_MESSAGE_HH__ */
|