mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-17 03:26:20 +00:00
melib: cleanup commit
Cleanup melib module exports, add some document tests, change some documentation.
This commit is contained in:
parent
d14f26569e
commit
c6c0da7fcb
@ -97,10 +97,13 @@ impl AddressBook {
|
||||
}
|
||||
|
||||
pub fn with_account(s: &crate::conf::AccountSettings) -> AddressBook {
|
||||
let mut ret = AddressBook::new(s.name.clone());
|
||||
|
||||
#[cfg(not(feature = "vcard"))]
|
||||
{
|
||||
AddressBook::new(s.name.clone())
|
||||
}
|
||||
#[cfg(feature = "vcard")]
|
||||
{
|
||||
let mut ret = AddressBook::new(s.name.clone());
|
||||
if let Some(vcard_path) = s.vcard_folder() {
|
||||
if let Ok(cards) = vcard::load_cards(&std::path::Path::new(vcard_path)) {
|
||||
for c in cards {
|
||||
@ -108,9 +111,8 @@ impl AddressBook {
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn add_card(&mut self, card: Card) {
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::email::address::{Address, MailboxAddress};
|
||||
use crate::email::parser::{BytesExt, IResult};
|
||||
use crate::error::ResultIntoMeliError;
|
||||
use crate::get_path_hash;
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
use super::*;
|
||||
use crate::backends::jmap::rfc8620::bool_false;
|
||||
use crate::email::address::{Address, MailboxAddress};
|
||||
use core::marker::PhantomData;
|
||||
use serde::de::{Deserialize, Deserializer};
|
||||
use serde_json::Value;
|
||||
|
@ -18,6 +18,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Basic mail account configuration to use with [`backends`](./backends/index.html)
|
||||
use crate::backends::SpecialUsageMailbox;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::HashMap;
|
||||
|
@ -18,6 +18,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Connections layers (TCP/fd/TLS/Deflate) to use with remote backends.
|
||||
#[cfg(feature = "deflate_compression")]
|
||||
use flate2::{read::DeflateDecoder, write::DeflateEncoder, Compression};
|
||||
|
||||
|
@ -19,11 +19,28 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Functions for dealing with date strings and UNIX Epoch timestamps.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use melib::datetime::*;
|
||||
//! // Get current UNIX Epoch timestamp.
|
||||
//! let now: UnixTimestamp = now();
|
||||
//!
|
||||
//! // Parse date from string
|
||||
//! let date_val = "Wed, 8 Jan 2020 10:44:03 -0800";
|
||||
//! let timestamp = rfc822_to_timestamp(date_val).unwrap();
|
||||
//! assert_eq!(timestamp, 1578509043);
|
||||
//!
|
||||
//! // Convert timestamp back to string
|
||||
//! let s = timestamp_to_string(timestamp, Some("%Y-%m-%d"));
|
||||
//! assert_eq!(s, "2020-01-08");
|
||||
//! ```
|
||||
use crate::error::Result;
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
pub type UnixTimestamp = u64;
|
||||
|
||||
use libc::{timeval, timezone};
|
||||
|
@ -22,38 +22,33 @@
|
||||
/*!
|
||||
* Email parsing, handling, sending etc.
|
||||
*/
|
||||
use std::convert::TryInto;
|
||||
mod compose;
|
||||
pub use self::compose::*;
|
||||
|
||||
pub mod list_management;
|
||||
mod mailto;
|
||||
pub use mailto::*;
|
||||
mod attachment_types;
|
||||
pub mod address;
|
||||
pub mod attachment_types;
|
||||
pub mod attachments;
|
||||
pub use crate::attachments::*;
|
||||
mod address;
|
||||
//pub mod parser;
|
||||
pub mod compose;
|
||||
pub mod headers;
|
||||
pub mod list_management;
|
||||
pub mod mailto;
|
||||
pub mod parser;
|
||||
use crate::parser::BytesExt;
|
||||
pub use address::*;
|
||||
mod headers;
|
||||
pub mod signatures;
|
||||
|
||||
pub use address::{Address, MessageID, References, StrBuild, StrBuilder};
|
||||
pub use attachments::{Attachment, AttachmentBuilder};
|
||||
pub use compose::{attachment_from_file, Draft};
|
||||
pub use headers::*;
|
||||
pub use mailto::*;
|
||||
|
||||
use crate::datetime::UnixTimestamp;
|
||||
use crate::error::{MeliError, Result};
|
||||
use crate::parser::BytesExt;
|
||||
use crate::thread::ThreadNodeHash;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fmt;
|
||||
use std::convert::TryInto;
|
||||
use std::hash::Hasher;
|
||||
use std::option::Option;
|
||||
use std::str;
|
||||
use std::string::String;
|
||||
use std::ops::Deref;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
@ -81,15 +76,16 @@ impl PartialEq<&str> for Flag {
|
||||
}
|
||||
}
|
||||
|
||||
///`Mail` holds both the envelope info of an email in its `envelope` field and the raw bytes that
|
||||
///describe the email in `bytes`. Its body as an `melib::email::Attachment` can be parsed on demand
|
||||
///with the `melib::email::Mail::body` method.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EnvelopeWrapper {
|
||||
envelope: Envelope,
|
||||
buffer: Vec<u8>,
|
||||
pub struct Mail {
|
||||
pub envelope: Envelope,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
impl Deref for EnvelopeWrapper {
|
||||
impl Deref for Mail {
|
||||
type Target = Envelope;
|
||||
|
||||
fn deref(&self) -> &Envelope {
|
||||
@ -97,56 +93,57 @@ impl Deref for EnvelopeWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
impl EnvelopeWrapper {
|
||||
pub fn new(buffer: Vec<u8>) -> Result<Self> {
|
||||
Ok(EnvelopeWrapper {
|
||||
envelope: Envelope::from_bytes(&buffer, None)?,
|
||||
buffer,
|
||||
impl Mail {
|
||||
pub fn new(bytes: Vec<u8>) -> Result<Self> {
|
||||
Ok(Mail {
|
||||
envelope: Envelope::from_bytes(&bytes, None)?,
|
||||
bytes,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn envelope(&self) -> &Envelope {
|
||||
&self.envelope
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buffer
|
||||
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
pub fn body(&self) -> Attachment {
|
||||
self.envelope.body_bytes(&self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
pub type EnvelopeHash = u64;
|
||||
|
||||
/// `Envelope` represents all the data of an email we need to know.
|
||||
/// `Envelope` represents all the header and structure data of an email we need to know.
|
||||
///
|
||||
/// Attachments (the email's body) is parsed on demand with `body`.
|
||||
/// Attachments (the email's body) is parsed on demand with `body` method.
|
||||
///
|
||||
/// Access to the underlying email object in the account's backend (for example the file or the
|
||||
/// entry in an IMAP server) is given through `operation_token`. For more information see
|
||||
/// `BackendOp`.
|
||||
///To access the email attachments, you need to parse them from the raw email bytes into an
|
||||
///`Attachment` object.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Envelope {
|
||||
date: String,
|
||||
from: SmallVec<[Address; 1]>,
|
||||
to: SmallVec<[Address; 1]>,
|
||||
cc: SmallVec<[Address; 1]>,
|
||||
bcc: Vec<Address>,
|
||||
subject: Option<String>,
|
||||
message_id: MessageID,
|
||||
in_reply_to: Option<MessageID>,
|
||||
pub hash: EnvelopeHash,
|
||||
pub date: String,
|
||||
pub timestamp: UnixTimestamp,
|
||||
pub from: SmallVec<[Address; 1]>,
|
||||
pub to: SmallVec<[Address; 1]>,
|
||||
pub cc: SmallVec<[Address; 1]>,
|
||||
pub bcc: Vec<Address>,
|
||||
pub subject: Option<String>,
|
||||
pub message_id: MessageID,
|
||||
pub in_reply_to: Option<MessageID>,
|
||||
pub references: Option<References>,
|
||||
other_headers: HeaderMap,
|
||||
|
||||
timestamp: UnixTimestamp,
|
||||
thread: ThreadNodeHash,
|
||||
|
||||
hash: EnvelopeHash,
|
||||
|
||||
flags: Flag,
|
||||
has_attachments: bool,
|
||||
labels: SmallVec<[u64; 8]>,
|
||||
pub other_headers: HeaderMap,
|
||||
pub thread: ThreadNodeHash,
|
||||
pub flags: Flag,
|
||||
pub has_attachments: bool,
|
||||
pub labels: SmallVec<[u64; 8]>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Envelope {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
impl core::fmt::Debug for Envelope {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("Envelope")
|
||||
.field("Subject", &self.subject())
|
||||
.field("Date", &self.date)
|
||||
@ -169,7 +166,9 @@ impl Default for Envelope {
|
||||
impl Envelope {
|
||||
pub fn new(hash: EnvelopeHash) -> Self {
|
||||
Envelope {
|
||||
hash,
|
||||
date: String::new(),
|
||||
timestamp: 0,
|
||||
from: SmallVec::new(),
|
||||
to: SmallVec::new(),
|
||||
cc: SmallVec::new(),
|
||||
@ -179,12 +178,7 @@ impl Envelope {
|
||||
in_reply_to: None,
|
||||
references: None,
|
||||
other_headers: Default::default(),
|
||||
|
||||
timestamp: 0,
|
||||
|
||||
thread: ThreadNodeHash::null(),
|
||||
|
||||
hash,
|
||||
has_attachments: false,
|
||||
flags: Flag::default(),
|
||||
labels: SmallVec::new(),
|
||||
@ -212,6 +206,7 @@ impl Envelope {
|
||||
pub fn hash(&self) -> EnvelopeHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
pub fn populate_headers(&mut self, mut bytes: &[u8]) -> Result<()> {
|
||||
if bytes.starts_with(b"From ") {
|
||||
/* Attempt to recover if message includes the mbox From label as first line */
|
||||
@ -352,9 +347,11 @@ impl Envelope {
|
||||
pub fn date_as_str(&self) -> &str {
|
||||
&self.date
|
||||
}
|
||||
|
||||
pub fn from(&self) -> &[Address] {
|
||||
self.from.as_slice()
|
||||
}
|
||||
|
||||
pub fn field_bcc_to_string(&self) -> String {
|
||||
if self.bcc.is_empty() {
|
||||
self.other_headers
|
||||
@ -372,6 +369,7 @@ impl Envelope {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_cc_to_string(&self) -> String {
|
||||
if self.cc.is_empty() {
|
||||
self.other_headers
|
||||
@ -389,6 +387,7 @@ impl Envelope {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_from_to_string(&self) -> String {
|
||||
if self.from.is_empty() {
|
||||
self.other_headers
|
||||
@ -406,9 +405,11 @@ impl Envelope {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to(&self) -> &[Address] {
|
||||
self.to.as_slice()
|
||||
}
|
||||
|
||||
pub fn field_to_to_string(&self) -> String {
|
||||
if self.to.is_empty() {
|
||||
self.other_headers
|
||||
@ -429,6 +430,7 @@ impl Envelope {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_references_to_string(&self) -> String {
|
||||
let refs = self.references();
|
||||
if refs.is_empty() {
|
||||
@ -455,7 +457,6 @@ impl Envelope {
|
||||
builder.build()
|
||||
}
|
||||
|
||||
/// Requests bytes from backend and thus can fail
|
||||
pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result<Vec<(&'a str, &'a str)>> {
|
||||
let ret = parser::headers::headers(bytes)?.1;
|
||||
let len = ret.len();
|
||||
@ -486,36 +487,46 @@ impl Envelope {
|
||||
.as_ref()
|
||||
.map(|m| String::from_utf8_lossy(m.val()))
|
||||
}
|
||||
|
||||
pub fn in_reply_to_raw(&self) -> Option<Cow<str>> {
|
||||
self.in_reply_to
|
||||
.as_ref()
|
||||
.map(|m| String::from_utf8_lossy(m.raw()))
|
||||
}
|
||||
|
||||
pub fn message_id(&self) -> &MessageID {
|
||||
&self.message_id
|
||||
}
|
||||
|
||||
pub fn message_id_display(&self) -> Cow<str> {
|
||||
String::from_utf8_lossy(self.message_id.val())
|
||||
}
|
||||
|
||||
pub fn message_id_raw(&self) -> Cow<str> {
|
||||
String::from_utf8_lossy(self.message_id.raw())
|
||||
}
|
||||
|
||||
pub fn set_date(&mut self, new_val: &[u8]) {
|
||||
let new_val = new_val.trim();
|
||||
self.date = String::from_utf8_lossy(new_val).into_owned();
|
||||
}
|
||||
|
||||
pub fn set_bcc(&mut self, new_val: Vec<Address>) {
|
||||
self.bcc = new_val;
|
||||
}
|
||||
|
||||
pub fn set_cc(&mut self, new_val: SmallVec<[Address; 1]>) {
|
||||
self.cc = new_val;
|
||||
}
|
||||
|
||||
pub fn set_from(&mut self, new_val: SmallVec<[Address; 1]>) {
|
||||
self.from = new_val;
|
||||
}
|
||||
|
||||
pub fn set_to(&mut self, new_val: SmallVec<[Address; 1]>) {
|
||||
self.to = new_val;
|
||||
}
|
||||
|
||||
pub fn set_in_reply_to(&mut self, new_val: &[u8]) {
|
||||
// FIXME msg_id_list
|
||||
let new_val = new_val.trim();
|
||||
@ -528,6 +539,7 @@ impl Envelope {
|
||||
};
|
||||
self.in_reply_to = Some(val);
|
||||
}
|
||||
|
||||
pub fn set_subject(&mut self, new_val: Vec<u8>) {
|
||||
let mut new_val = String::from_utf8(new_val)
|
||||
.unwrap_or_else(|err| String::from_utf8_lossy(&err.into_bytes()).into());
|
||||
@ -542,6 +554,7 @@ impl Envelope {
|
||||
|
||||
self.subject = Some(new_val);
|
||||
}
|
||||
|
||||
pub fn set_message_id(&mut self, new_val: &[u8]) {
|
||||
let new_val = new_val.trim();
|
||||
match parser::address::msg_id(new_val) {
|
||||
@ -553,6 +566,7 @@ impl Envelope {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_references(&mut self, new_ref: MessageID) {
|
||||
match self.references {
|
||||
Some(ref mut s) => {
|
||||
@ -578,6 +592,7 @@ impl Envelope {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_references(&mut self, new_val: &[u8]) {
|
||||
let new_val = new_val.trim();
|
||||
match self.references {
|
||||
@ -592,6 +607,7 @@ impl Envelope {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn references(&self) -> SmallVec<[&MessageID; 8]> {
|
||||
match self.references {
|
||||
Some(ref s) => s.refs.iter().fold(SmallVec::new(), |mut acc, x| {
|
||||
@ -613,27 +629,35 @@ impl Envelope {
|
||||
pub fn thread(&self) -> ThreadNodeHash {
|
||||
self.thread
|
||||
}
|
||||
|
||||
pub fn set_thread(&mut self, new_val: ThreadNodeHash) {
|
||||
self.thread = new_val;
|
||||
}
|
||||
|
||||
pub fn set_datetime(&mut self, new_val: UnixTimestamp) {
|
||||
self.timestamp = new_val;
|
||||
}
|
||||
|
||||
pub fn set_flag(&mut self, f: Flag, value: bool) {
|
||||
self.flags.set(f, value);
|
||||
}
|
||||
|
||||
pub fn set_flags(&mut self, f: Flag) {
|
||||
self.flags = f;
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> Flag {
|
||||
self.flags
|
||||
}
|
||||
|
||||
pub fn set_seen(&mut self) {
|
||||
self.set_flag(Flag::SEEN, true)
|
||||
}
|
||||
|
||||
pub fn set_unseen(&mut self) {
|
||||
self.set_flag(Flag::SEEN, false)
|
||||
}
|
||||
|
||||
pub fn is_seen(&self) -> bool {
|
||||
self.flags.contains(Flag::SEEN)
|
||||
}
|
||||
@ -656,14 +680,15 @@ impl Envelope {
|
||||
}
|
||||
|
||||
impl Eq for Envelope {}
|
||||
|
||||
impl Ord for Envelope {
|
||||
fn cmp(&self, other: &Envelope) -> Ordering {
|
||||
fn cmp(&self, other: &Envelope) -> std::cmp::Ordering {
|
||||
self.datetime().cmp(&other.datetime())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Envelope {
|
||||
fn partial_cmp(&self, other: &Envelope) -> Option<Ordering> {
|
||||
fn partial_cmp(&self, other: &Envelope) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Email addresses. Parsing functions are in [melib::email::parser::address](../parser/address/index.html).
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
@ -60,6 +61,35 @@ pub struct MailboxAddress {
|
||||
pub address_spec: StrBuilder,
|
||||
}
|
||||
|
||||
impl Eq for MailboxAddress {}
|
||||
|
||||
impl PartialEq for MailboxAddress {
|
||||
fn eq(&self, other: &MailboxAddress) -> bool {
|
||||
self.address_spec.display_bytes(&self.raw) == other.address_spec.display_bytes(&other.raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// An email address.
|
||||
///
|
||||
/// Conforms to [RFC5322 - Internet Message Format](https://tools.ietf.org/html/rfc5322).
|
||||
///
|
||||
/// # Creating an `Address`
|
||||
/// You can directly create an address with `Address::new`,
|
||||
///
|
||||
/// ```rust
|
||||
/// # use melib::email::Address;
|
||||
/// let addr = Address::new(Some("Jörg Doe".to_string()), "joerg@example.com".to_string());
|
||||
/// assert_eq!(addr.to_string().as_str(), "Jörg Doe <joerg@example.com>");
|
||||
/// ```
|
||||
///
|
||||
/// or parse it from a raw value:
|
||||
///
|
||||
/// ```rust
|
||||
/// let (rest_bytes, addr) = melib::email::parser::address::address("=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>".as_bytes()).unwrap();
|
||||
/// assert!(rest_bytes.is_empty());
|
||||
/// assert_eq!(addr.get_display_name(), "Jörg Doe");
|
||||
/// assert_eq!(addr.get_email(), "joerg@example.com");
|
||||
/// ```
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum Address {
|
||||
Mailbox(MailboxAddress),
|
||||
@ -121,6 +151,22 @@ impl Address {
|
||||
Address::Group(g) => g.raw.as_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the display name of this address.
|
||||
///
|
||||
/// If it's a group, it's the name of the group. Otherwise it's the `display_name` part of
|
||||
/// the mailbox:
|
||||
///
|
||||
///
|
||||
/// ```text
|
||||
/// raw raw
|
||||
/// ┌──────────┴────────────┐ ┌──────────┴────────────────────┐
|
||||
/// Name <address@domain.tld> "Name Name2" <address@domain.tld>
|
||||
/// └─┬┘ └──────────┬─────┘ └─────┬──┘ └──────────┬─────┘
|
||||
/// display_name │ display_name │
|
||||
/// │ │
|
||||
/// address_spec address_spec
|
||||
///```
|
||||
pub fn get_display_name(&self) -> String {
|
||||
match self {
|
||||
Address::Mailbox(m) => m.display_name.display(&m.raw),
|
||||
@ -128,6 +174,7 @@ impl Address {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the address spec part of this address. A group returns an empty `String`.
|
||||
pub fn get_email(&self) -> String {
|
||||
match self {
|
||||
Address::Mailbox(m) => m.address_spec.display(&m.raw),
|
||||
@ -176,15 +223,14 @@ impl Address {
|
||||
}
|
||||
|
||||
impl Eq for Address {}
|
||||
|
||||
impl PartialEq for Address {
|
||||
fn eq(&self, other: &Address) -> bool {
|
||||
match (self, other) {
|
||||
(Address::Mailbox(_), Address::Group(_)) | (Address::Group(_), Address::Mailbox(_)) => {
|
||||
false
|
||||
}
|
||||
(Address::Mailbox(s), Address::Mailbox(o)) => {
|
||||
s.address_spec.display_bytes(&s.raw) == o.address_spec.display_bytes(&o.raw)
|
||||
}
|
||||
(Address::Mailbox(s), Address::Mailbox(o)) => s == o,
|
||||
(Address::Group(s), Address::Group(o)) => {
|
||||
s.display_name.display_bytes(&s.raw) == o.display_name.display_bytes(&o.raw)
|
||||
&& s.mailbox_list.iter().collect::<HashSet<_>>()
|
||||
@ -210,8 +256,8 @@ impl Hash for Address {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
impl core::fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self {
|
||||
Address::Mailbox(m) if m.display_name.length > 0 => write!(
|
||||
f,
|
||||
@ -234,8 +280,8 @@ impl fmt::Display for Address {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
impl core::fmt::Debug for Address {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self {
|
||||
Address::Mailbox(m) => f
|
||||
.debug_struct("Address::Mailbox")
|
||||
@ -332,10 +378,12 @@ fn test_strbuilder() {
|
||||
);
|
||||
}
|
||||
|
||||
impl fmt::Display for MessageID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
impl core::fmt::Display for MessageID {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
if self.val().is_ascii() {
|
||||
write!(f, "{}", unsafe { str::from_utf8_unchecked(self.val()) })
|
||||
write!(f, "{}", unsafe {
|
||||
std::str::from_utf8_unchecked(self.val())
|
||||
})
|
||||
} else {
|
||||
write!(f, "{}", String::from_utf8_lossy(self.val()))
|
||||
}
|
||||
@ -347,8 +395,8 @@ impl PartialEq for MessageID {
|
||||
self.raw() == other.raw()
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for MessageID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
impl core::fmt::Debug for MessageID {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{}", String::from_utf8(self.raw().to_vec()).unwrap())
|
||||
}
|
||||
}
|
||||
@ -359,8 +407,8 @@ pub struct References {
|
||||
pub refs: Vec<MessageID>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for References {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
impl core::fmt::Debug for References {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{:#?}", self.refs)
|
||||
}
|
||||
}
|
||||
|
@ -18,15 +18,17 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::email::address::StrBuilder;
|
||||
use crate::email::parser;
|
||||
use crate::email::parser::BytesExt;
|
||||
use crate::email::EnvelopeWrapper;
|
||||
|
||||
use crate::email::{
|
||||
address::StrBuilder,
|
||||
parser::{self, BytesExt},
|
||||
Mail,
|
||||
};
|
||||
use core::fmt;
|
||||
use core::str;
|
||||
use data_encoding::BASE64_MIME;
|
||||
|
||||
pub use crate::email::attachment_types::*;
|
||||
use crate::email::attachment_types::*;
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct AttachmentBuilder {
|
||||
@ -360,7 +362,7 @@ impl fmt::Display for Attachment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.content_type {
|
||||
ContentType::MessageRfc822 => {
|
||||
match EnvelopeWrapper::new(self.body.display_bytes(&self.raw).to_vec()) {
|
||||
match Mail::new(self.body.display_bytes(&self.raw).to_vec()) {
|
||||
Ok(wrapper) => write!(
|
||||
f,
|
||||
"message/rfc822: {} - {} - {}",
|
||||
|
@ -20,7 +20,10 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::email::attachments::AttachmentBuilder;
|
||||
use crate::email::attachment_types::{
|
||||
Charset, ContentTransferEncoding, ContentType, MultipartType,
|
||||
};
|
||||
use crate::email::attachments::{decode, decode_rec, AttachmentBuilder};
|
||||
use crate::shellexpand::ShellExpandTrait;
|
||||
use data_encoding::BASE64_MIME;
|
||||
use std::ffi::OsStr;
|
||||
|
@ -1667,6 +1667,13 @@ pub mod encodings {
|
||||
}
|
||||
|
||||
pub mod address {
|
||||
//! Parsing of address values and address-related headers.
|
||||
//!
|
||||
//! Implemented RFCs:
|
||||
//!
|
||||
//! - [RFC5322 "Internet Message Format"](https://tools.ietf.org/html/rfc5322)
|
||||
//! - [RFC6532 "Internationalized Email Headers"](https://tools.ietf.org/html/rfc6532)
|
||||
//! - [RFC2047 "MIME Part Three: Message Header Extensions for Non-ASCII Text"](https://tools.ietf.org/html/rfc2047)
|
||||
use super::*;
|
||||
use crate::email::address::*;
|
||||
use crate::email::parser::generic::{
|
||||
@ -1775,7 +1782,7 @@ pub mod address {
|
||||
}
|
||||
|
||||
///`angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr`
|
||||
fn angle_addr(input: &[u8]) -> IResult<&[u8], Address> {
|
||||
pub fn angle_addr(input: &[u8]) -> IResult<&[u8], Address> {
|
||||
let (input, _) = opt(cfws)(input)?;
|
||||
let (input, _) = tag("<")(input)?;
|
||||
let (input, addr_spec) = addr_spec(input)?;
|
||||
@ -1784,66 +1791,66 @@ pub mod address {
|
||||
Ok((input, addr_spec))
|
||||
}
|
||||
|
||||
///`obs-domain = atom *("." atom)`
|
||||
pub fn obs_domain(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
let (mut input, atom_) = context("obs_domain", atom)(input)?;
|
||||
let mut ret: Vec<u8> = atom_.into();
|
||||
loop {
|
||||
if !input.starts_with(b".") {
|
||||
break;
|
||||
}
|
||||
ret.push(b'.');
|
||||
input = &input[1..];
|
||||
if let Ok((_input, atom_)) = context("obs_domain", atom)(input) {
|
||||
ret.extend_from_slice(&atom_);
|
||||
input = _input;
|
||||
} else {
|
||||
return Err(nom::Err::Error(
|
||||
(input, "obs_domain(): expected <atom> after DOT").into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok((input, ret.into()))
|
||||
}
|
||||
|
||||
///`local-part = dot-atom / quoted-string / obs-local-part`
|
||||
pub fn local_part(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
alt((dot_atom, quoted_string))(input)
|
||||
}
|
||||
|
||||
///`domain = dot-atom / domain-literal / obs-domain`
|
||||
pub fn domain(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
alt((dot_atom, domain_literal, obs_domain))(input)
|
||||
}
|
||||
|
||||
///`domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]`
|
||||
pub fn domain_literal(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
use crate::email::parser::generic::fws;
|
||||
let (input, first_opt_space) = context("domain_literal()", opt(cfws))(input)?;
|
||||
let (input, _) = context("domain_literal()", tag("["))(input)?;
|
||||
let (input, dtexts) = many0(pair(opt(fws), dtext))(input)?;
|
||||
let (input, end_fws): (_, Option<_>) = context("domain_literal()", opt(fws))(input)?;
|
||||
let (input, _) = context("domain_literal()", tag("]"))(input)?;
|
||||
let (input, _) = context("domain_literal()", opt(cfws))(input)?;
|
||||
let mut ret_s = vec![b'['];
|
||||
if let Some(first_opt_space) = first_opt_space {
|
||||
ret_s.extend_from_slice(&first_opt_space);
|
||||
}
|
||||
for (fws_opt, dtext) in dtexts {
|
||||
if let Some(fws_opt) = fws_opt {
|
||||
ret_s.extend_from_slice(&fws_opt);
|
||||
}
|
||||
ret_s.push(dtext);
|
||||
}
|
||||
if let Some(end_fws) = end_fws {
|
||||
ret_s.extend_from_slice(&end_fws);
|
||||
}
|
||||
ret_s.push(b']');
|
||||
Ok((input, ret_s.into()))
|
||||
}
|
||||
|
||||
///`addr-spec = local-part "@" domain`
|
||||
pub fn addr_spec(input: &[u8]) -> IResult<&[u8], Address> {
|
||||
///`obs-domain = atom *("." atom)`
|
||||
fn obs_domain(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
let (mut input, atom_) = context("obs_domain", atom)(input)?;
|
||||
let mut ret: Vec<u8> = atom_.into();
|
||||
loop {
|
||||
if !input.starts_with(b".") {
|
||||
break;
|
||||
}
|
||||
ret.push(b'.');
|
||||
input = &input[1..];
|
||||
if let Ok((_input, atom_)) = context("obs_domain", atom)(input) {
|
||||
ret.extend_from_slice(&atom_);
|
||||
input = _input;
|
||||
} else {
|
||||
return Err(nom::Err::Error(
|
||||
(input, "obs_domain(): expected <atom> after DOT").into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok((input, ret.into()))
|
||||
}
|
||||
|
||||
///`local-part = dot-atom / quoted-string / obs-local-part`
|
||||
fn local_part(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
alt((dot_atom, quoted_string))(input)
|
||||
}
|
||||
|
||||
///`domain = dot-atom / domain-literal / obs-domain`
|
||||
fn domain(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
alt((dot_atom, domain_literal, obs_domain))(input)
|
||||
}
|
||||
|
||||
///`domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]`
|
||||
fn domain_literal(input: &[u8]) -> IResult<&[u8], Cow<'_, [u8]>> {
|
||||
use crate::email::parser::generic::fws;
|
||||
let (input, first_opt_space) = context("domain_literal()", opt(cfws))(input)?;
|
||||
let (input, _) = context("domain_literal()", tag("["))(input)?;
|
||||
let (input, dtexts) = many0(pair(opt(fws), dtext))(input)?;
|
||||
let (input, end_fws): (_, Option<_>) = context("domain_literal()", opt(fws))(input)?;
|
||||
let (input, _) = context("domain_literal()", tag("]"))(input)?;
|
||||
let (input, _) = context("domain_literal()", opt(cfws))(input)?;
|
||||
let mut ret_s = vec![b'['];
|
||||
if let Some(first_opt_space) = first_opt_space {
|
||||
ret_s.extend_from_slice(&first_opt_space);
|
||||
}
|
||||
for (fws_opt, dtext) in dtexts {
|
||||
if let Some(fws_opt) = fws_opt {
|
||||
ret_s.extend_from_slice(&fws_opt);
|
||||
}
|
||||
ret_s.push(dtext);
|
||||
}
|
||||
if let Some(end_fws) = end_fws {
|
||||
ret_s.extend_from_slice(&end_fws);
|
||||
}
|
||||
ret_s.push(b']');
|
||||
Ok((input, ret_s.into()))
|
||||
}
|
||||
|
||||
let (input, local_part) = context("addr_spec()", local_part)(input)?;
|
||||
let (input, _) = context("addr_spec()", tag("@"))(input)?;
|
||||
let (input, domain) = context("addr_spec()", domain)(input)?;
|
||||
@ -1857,6 +1864,17 @@ pub mod address {
|
||||
))
|
||||
}
|
||||
|
||||
///Returns the raw `local_part` and `domain` parts.
|
||||
///
|
||||
///`addr-spec = local-part "@" domain`
|
||||
pub fn addr_spec_raw(input: &[u8]) -> IResult<&[u8], (Cow<'_, [u8]>, Cow<'_, [u8]>)> {
|
||||
let (input, local_part) = context("addr_spec()", local_part)(input)?;
|
||||
let (input, _) = context("addr_spec()", tag("@"))(input)?;
|
||||
let (input, domain) = context("addr_spec()", domain)(input)?;
|
||||
|
||||
Ok((input, (local_part, domain)))
|
||||
}
|
||||
|
||||
///`display-name = phrase`
|
||||
pub fn display_name(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
|
||||
let (rest, ret) = phrase2(input)?;
|
||||
|
@ -19,8 +19,11 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::email::attachments::{Attachment, ContentType, MultipartType};
|
||||
use crate::email::parser::BytesExt;
|
||||
use crate::email::{
|
||||
attachment_types::{ContentType, MultipartType},
|
||||
attachments::Attachment,
|
||||
};
|
||||
use crate::{MeliError, Result};
|
||||
|
||||
/// rfc3156
|
||||
|
@ -20,21 +20,19 @@
|
||||
*/
|
||||
|
||||
//! A crate that performs mail client operations such as
|
||||
//! - Hold an `Envelope` with methods convenient for mail client use. (see module `email`)
|
||||
//! - Abstract through mail storages through the `MailBackend` trait, and handle
|
||||
//! read/writes/updates through it. (see module `melib::backends`)
|
||||
//! - Decode attachments (see module `melib::email::attachments`)
|
||||
//! - Create new mail (see `email::Draft`)
|
||||
//! - Send mail with an SMTP client (see module `smtp`)
|
||||
//! - Manage an `addressbook` i.e. have contacts (see module `addressbook`)
|
||||
//! - Build thread structures out of a list of mail via their `In-Reply-To` and `References` header
|
||||
//! values (see module `thread`)
|
||||
//! - Hold an [`Envelope`](./email/struct.Envelope.html) with methods convenient for mail client use. (see module [`email`](./email/index.html))
|
||||
//! - Abstract through mail storages through the [`MailBackend`](./backends/trait.MailBackend.html) trait, and handle read/writes/updates through it. (see module [`backends`](./backends/index.html))
|
||||
//! - Decode attachments (see module [`email::attachments`](./email/attachments/index.html))
|
||||
//! - Create new mail (see [`email::Draft`](./email/compose/struct.Draft.html))
|
||||
//! - Send mail with an SMTP client (see module [`smtp`](./smtp/index.html))
|
||||
//! - Manage an `addressbook` i.e. have contacts (see module [`addressbook`](./addressbook/index.html))
|
||||
//! - Build thread structures out of a list of mail via their `In-Reply-To` and `References` header values (see module [`thread`](./thread/index.html))
|
||||
//!
|
||||
//! Other exports are
|
||||
//! - Basic mail account configuration to use with `backends` (see module `conf`)
|
||||
//! - Parser combinators (see module `parsec`)
|
||||
//! - Basic mail account configuration to use with [`backends`](./backends/index.html) (see module [`conf`](./conf/index.html))
|
||||
//! - Parser combinators (see module [`parsec`](./parsec/index.html))
|
||||
//! - A `ShellExpandTrait` to expand paths like a shell.
|
||||
//! - A `debug` macro that works like `std::dbg` but for multiple threads. (see `dbg` module)
|
||||
//! - A `debug` macro that works like `std::dbg` but for multiple threads. (see [`debug` macro](./macro.debug.html))
|
||||
#[macro_use]
|
||||
pub mod dbg {
|
||||
|
||||
@ -104,14 +102,19 @@ pub use self::logging::LoggingLevel::*;
|
||||
pub use self::logging::*;
|
||||
|
||||
pub mod addressbook;
|
||||
pub use addressbook::*;
|
||||
pub mod backends;
|
||||
pub use backends::*;
|
||||
mod collection;
|
||||
pub use collection::*;
|
||||
pub mod conf;
|
||||
pub use conf::*;
|
||||
pub mod email;
|
||||
pub use email::*;
|
||||
pub mod error;
|
||||
pub use crate::error::*;
|
||||
pub mod thread;
|
||||
pub use crate::email::*;
|
||||
pub use crate::thread::*;
|
||||
pub use thread::*;
|
||||
pub mod connections;
|
||||
pub mod parsec;
|
||||
pub mod search;
|
||||
@ -126,26 +129,15 @@ extern crate serde_derive;
|
||||
/* parser */
|
||||
extern crate data_encoding;
|
||||
extern crate encoding;
|
||||
pub use nom;
|
||||
pub extern crate nom;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
pub extern crate futures;
|
||||
pub extern crate indexmap;
|
||||
extern crate uuid;
|
||||
pub use smallvec;
|
||||
|
||||
pub use futures;
|
||||
pub use smol;
|
||||
|
||||
pub use crate::backends::{
|
||||
BackendEvent, BackendEventConsumer, Backends, RefreshEvent, SpecialUsageMailbox,
|
||||
};
|
||||
pub use crate::collection::*;
|
||||
pub use crate::conf::*;
|
||||
pub use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||
pub use crate::error::{IntoMeliError, MeliError, Result, ResultIntoMeliError};
|
||||
|
||||
pub use crate::addressbook::*;
|
||||
pub extern crate smallvec;
|
||||
pub extern crate smol;
|
||||
pub extern crate uuid;
|
||||
|
||||
pub use shellexpand::ShellExpandTrait;
|
||||
pub mod shellexpand {
|
||||
|
@ -19,6 +19,8 @@
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Parser combinators.
|
||||
|
||||
pub type Result<'a, Output> = std::result::Result<(&'a str, Output), &'a str>;
|
||||
|
||||
pub trait Parser<'a, Output> {
|
||||
|
@ -309,7 +309,7 @@ fn run_app(opt: Opt) -> Result<()> {
|
||||
if let Some(SubCommand::View { path }) = opt.subcommand {
|
||||
let bytes = std::fs::read(&path)
|
||||
.chain_err_summary(|| format!("Could not read from `{}`", path.display()))?;
|
||||
let wrapper = EnvelopeWrapper::new(bytes)
|
||||
let wrapper = Mail::new(bytes)
|
||||
.chain_err_summary(|| format!("Could not parse `{}`", path.display()))?;
|
||||
state = State::new(
|
||||
Some(Settings::without_accounts().unwrap_or_default()),
|
||||
|
@ -23,6 +23,7 @@
|
||||
*/
|
||||
use super::*;
|
||||
use melib::backends::{AccountHash, Mailbox, MailboxHash};
|
||||
use melib::email::{attachment_types::*, attachments::*};
|
||||
use melib::thread::ThreadNodeHash;
|
||||
|
||||
pub mod listing;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use melib::email::attachment_types::{ContentType, MultipartType};
|
||||
use melib::list_management;
|
||||
use melib::Draft;
|
||||
|
||||
|
@ -481,7 +481,6 @@ impl Component for AccountStatus {
|
||||
None,
|
||||
);
|
||||
|
||||
use melib::backends::MailBackendExtensionStatus;
|
||||
let (width, height) = self.content.size();
|
||||
let (x, y) = match status {
|
||||
MailBackendExtensionStatus::Unsupported { comment: _ } => write_string_to_grid(
|
||||
|
@ -481,7 +481,7 @@ impl MailView {
|
||||
}
|
||||
} else {
|
||||
match u.content_type() {
|
||||
ContentType::MessageRfc822 => match EnvelopeWrapper::new(u.body().to_vec()) {
|
||||
ContentType::MessageRfc822 => match Mail::new(u.body().to_vec()) {
|
||||
Ok(wrapper) => {
|
||||
context
|
||||
.replies
|
||||
|
@ -51,7 +51,7 @@ pub struct EnvelopeView {
|
||||
subview: Option<Box<dyn Component>>,
|
||||
dirty: bool,
|
||||
mode: ViewMode,
|
||||
wrapper: EnvelopeWrapper,
|
||||
mail: Mail,
|
||||
|
||||
account_hash: AccountHash,
|
||||
cmd_buf: String,
|
||||
@ -66,7 +66,7 @@ impl fmt::Display for EnvelopeView {
|
||||
|
||||
impl EnvelopeView {
|
||||
pub fn new(
|
||||
wrapper: EnvelopeWrapper,
|
||||
mail: Mail,
|
||||
pager: Option<Pager>,
|
||||
subview: Option<Box<dyn Component>>,
|
||||
account_hash: AccountHash,
|
||||
@ -76,7 +76,7 @@ impl EnvelopeView {
|
||||
subview,
|
||||
dirty: true,
|
||||
mode: ViewMode::Normal,
|
||||
wrapper,
|
||||
mail,
|
||||
account_hash,
|
||||
cmd_buf: String::with_capacity(4),
|
||||
id: ComponentId::new_v4(),
|
||||
@ -225,15 +225,13 @@ impl Component for EnvelopeView {
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
let y: usize = {
|
||||
let envelope: &Envelope = &self.wrapper;
|
||||
|
||||
if self.mode == ViewMode::Raw {
|
||||
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
||||
context.dirty_areas.push_back(area);
|
||||
get_y(upper_left).saturating_sub(1)
|
||||
} else {
|
||||
let (x, y) = write_string_to_grid(
|
||||
&format!("Date: {}", envelope.date_as_str()),
|
||||
&format!("Date: {}", self.mail.date_as_str()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
@ -247,7 +245,7 @@ impl Component for EnvelopeView {
|
||||
grid[(x, y)].set_fg(Color::Default);
|
||||
}
|
||||
let (x, y) = write_string_to_grid(
|
||||
&format!("From: {}", envelope.field_from_to_string()),
|
||||
&format!("From: {}", self.mail.field_from_to_string()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
@ -261,7 +259,7 @@ impl Component for EnvelopeView {
|
||||
grid[(x, y)].set_fg(Color::Default);
|
||||
}
|
||||
let (x, y) = write_string_to_grid(
|
||||
&format!("To: {}", envelope.field_to_to_string()),
|
||||
&format!("To: {}", self.mail.field_to_to_string()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
@ -275,7 +273,7 @@ impl Component for EnvelopeView {
|
||||
grid[(x, y)].set_fg(Color::Default);
|
||||
}
|
||||
let (x, y) = write_string_to_grid(
|
||||
&format!("Subject: {}", envelope.subject()),
|
||||
&format!("Subject: {}", self.mail.subject()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
@ -289,7 +287,7 @@ impl Component for EnvelopeView {
|
||||
grid[(x, y)].set_fg(Color::Default);
|
||||
}
|
||||
let (x, y) = write_string_to_grid(
|
||||
&format!("Message-ID: <{}>", envelope.message_id_raw()),
|
||||
&format!("Message-ID: <{}>", self.mail.message_id_raw()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
@ -315,7 +313,7 @@ impl Component for EnvelopeView {
|
||||
};
|
||||
|
||||
if self.dirty {
|
||||
let body = self.wrapper.body_bytes(self.wrapper.buffer());
|
||||
let body = self.mail.body();
|
||||
match self.mode {
|
||||
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
|
||||
let attachment = &body.attachments()[aidx];
|
||||
@ -409,12 +407,7 @@ impl Component for EnvelopeView {
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||
|
||||
{
|
||||
let envelope: &Envelope = self.wrapper.envelope();
|
||||
if let Some(u) = envelope
|
||||
.body_bytes(self.wrapper.buffer())
|
||||
.attachments()
|
||||
.get(lidx)
|
||||
{
|
||||
if let Some(u) = self.mail.body().attachments().get(lidx) {
|
||||
match u.content_type() {
|
||||
ContentType::MessageRfc822 => {
|
||||
self.mode = ViewMode::Subview;
|
||||
@ -518,9 +511,8 @@ impl Component for EnvelopeView {
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||
let url = {
|
||||
let envelope: &Envelope = self.wrapper.envelope();
|
||||
let finder = LinkFinder::new();
|
||||
let t = envelope.body_bytes(self.wrapper.buffer()).text();
|
||||
let t = self.mail.body().text();
|
||||
let links: Vec<Link> = finder.links(&t).collect();
|
||||
if let Some(u) = links.get(lidx) {
|
||||
u.as_str().to_string()
|
||||
|
@ -9,7 +9,7 @@ fn build_draft() {
|
||||
.expect("Could not open test_image.gif.");
|
||||
if let Ok(mime_type) = query_mime_info("./tests/test_image.gif") {
|
||||
match attachment.content_type {
|
||||
melib::email::ContentType::Other { ref mut tag, .. } => {
|
||||
melib::email::attachment_types::ContentType::Other { ref mut tag, .. } => {
|
||||
*tag = mime_type;
|
||||
}
|
||||
_ => {}
|
||||
|
@ -5,28 +5,17 @@ use melib::smol;
|
||||
use melib::smtp::*;
|
||||
use melib::Result;
|
||||
|
||||
/// Opens an interactive shell on an IMAP server. Suggested use is with rlwrap(1)
|
||||
///
|
||||
/// # Example invocation:
|
||||
/// ```sh
|
||||
/// ./imap_conn server_hostname server_username server_password server_port");
|
||||
/// ```
|
||||
///
|
||||
/// `danger_accept_invalid_certs` is turned on by default, so no certificate validation is performed.
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let conf = SmtpServerConf {
|
||||
hostname: "smtp1.ntua.gr".into(),
|
||||
hostname: "smtp1.example.com".into(),
|
||||
port: 587,
|
||||
security: SmtpSecurity::StartTLS {
|
||||
danger_accept_invalid_certs: false,
|
||||
},
|
||||
extensions: SmtpExtensionSupport::default(),
|
||||
auth: SmtpAuth::Auto {
|
||||
username: "el13635".into(),
|
||||
password: Password::CommandEval(
|
||||
"gpg2 --no-tty -q -d ~/.passwords/msmtp/ntua.gpg".into(),
|
||||
),
|
||||
username: "username".into(),
|
||||
password: Password::CommandEval("gpg2 --no-tty -q -d ~/.passwords/password.gpg".into()),
|
||||
require_auth: true,
|
||||
},
|
||||
envelope_from: String::new(),
|
||||
@ -37,14 +26,15 @@ fn main() -> Result<()> {
|
||||
|
||||
let mut conn = futures::executor::block_on(SmtpConnection::new_connection(conf)).unwrap();
|
||||
futures::executor::block_on(conn.mail_transaction(
|
||||
r##"To: pr.birch@gmail.com
|
||||
r##"To: username@example.com
|
||||
Auto-Submitted: auto-generated
|
||||
Subject: Fwd: *** SMTP TEST #2 information ***
|
||||
From: Manos <el13635@mail.ntua.gr>
|
||||
Message-Id: <E1hSjnr-0003fN-RL2@postretch>
|
||||
From: Xxxxx <username@example.com>
|
||||
Message-Id: <E1hSjnr-0003fN-RL2@example>
|
||||
Date: Mon, 13 Jul 2020 15:02:15 +0300
|
||||
|
||||
postretch : May 20 18:02:00 : epilys : user NOT in sudoers ; TTY=pts/13 ; PWD=/tmp/db-project ; USER=postgres ; COMMAND=/usr/bin/dropdb Prescriptions-R-X"##,
|
||||
machine : May 20 18:02:00 : user : user NOT in sudoers ; TTY=pts/13 ; PWD=/tmp/db-project ; USER=postgres ; COMMAND=/usr/bin/dropdb Prescriptions-R-X"##,
|
||||
None
|
||||
)).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user