/* * meli - email module * * Copyright 2019 Manos Pitsidianakis * * This file is part of meli. * * meli 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 of the License, or * (at your option) any later version. * * meli 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 meli. If not, see . */ use super::*; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GroupAddress { pub raw: Vec, pub display_name: StrBuilder, pub mailbox_list: Vec
, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct MailboxAddress { pub raw: Vec, pub display_name: StrBuilder, pub address_spec: StrBuilder, } #[derive(Clone, Serialize, Deserialize)] pub enum Address { Mailbox(MailboxAddress), Group(GroupAddress), } impl Address { pub fn raw(&self) -> &[u8] { match self { Address::Mailbox(m) => m.raw.as_slice(), Address::Group(g) => g.raw.as_slice(), } } pub fn get_display_name(&self) -> String { match self { Address::Mailbox(m) => m.display_name.display(&m.raw), Address::Group(g) => g.display_name.display(&g.raw), } } pub fn get_email(&self) -> String { match self { Address::Mailbox(m) => m.address_spec.display(&m.raw), Address::Group(_) => String::new(), } } pub fn get_fqdn(&self) -> Option { match self { Address::Mailbox(m) => { let raw_address = m.address_spec.display_bytes(&m.raw); let fqdn_pos = raw_address.iter().position(|&b| b == b'@')? + 1; Some(String::from_utf8_lossy(&raw_address[fqdn_pos..]).into()) } Address::Group(_) => None, } } pub fn get_tags(&self, separator: char) -> Vec { let mut ret = Vec::new(); let email = self.get_email(); let at_pos = email.as_bytes().iter().position(|&b| b == b'@').unwrap(); let email: &str = email[..at_pos].into(); ret.extend(email.split(separator).skip(1).map(str::to_string)); ret } } 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::Group(s), Address::Group(o)) => { s.display_name.display_bytes(&s.raw) == o.display_name.display_bytes(&o.raw) && s.mailbox_list .iter() .zip(o.mailbox_list.iter()) .fold(true, |b, (s, o)| b && (s == o)) } } } } impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Address::Mailbox(m) if m.display_name.length > 0 => write!( f, "{} <{}>", m.display_name.display(&m.raw), m.address_spec.display(&m.raw) ), Address::Group(g) => { let attachment_strings: Vec = g.mailbox_list.iter().map(|a| format!("{}", a)).collect(); write!( f, "{}: {}", g.display_name.display(&g.raw), attachment_strings.join(", ") ) } Address::Mailbox(m) => write!(f, "{}", m.address_spec.display(&m.raw)), } } } impl fmt::Debug for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } /// Helper struct to return slices from a struct field on demand. #[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq, Copy)] pub struct StrBuilder { pub offset: usize, pub length: usize, } /// Structs implementing this trait must contain a `StrBuilder` field. pub trait StrBuild { /// Create a new `Self` out of a string and a slice fn new(string: &[u8], slice: &[u8]) -> Self; /// Get the slice part of the string fn raw(&self) -> &[u8]; /// Get the entire string as a slice fn val(&self) -> &[u8]; } impl StrBuilder { pub fn display<'a>(&self, s: &'a [u8]) -> String { let offset = self.offset; let length = self.length; String::from_utf8(s[offset..offset + length].to_vec()).unwrap() } pub fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] { &b[self.offset..(self.offset + self.length)] } } /// `MessageID` is accessed through the `StrBuild` trait. #[derive(Clone, Serialize, Deserialize, Default)] pub struct MessageID(pub Vec, pub StrBuilder); impl StrBuild for MessageID { fn new(string: &[u8], slice: &[u8]) -> Self { let offset = string.find(slice).unwrap(); MessageID( string.to_owned(), StrBuilder { offset, length: slice.len() + 1, }, ) } fn raw(&self) -> &[u8] { let offset = self.1.offset; let length = self.1.length; &self.0[offset..offset + length.saturating_sub(1)] } fn val(&self) -> &[u8] { &self.0 } } #[test] fn test_strbuilder() { let m_id = b"<20170825132332.6734-1@el13635@mail.ntua.gr>"; let (_, slice) = parser::message_id(m_id).unwrap(); assert_eq!( MessageID::new(m_id, slice), MessageID( m_id.to_vec(), StrBuilder { offset: 1, length: 43, } ) ); } impl fmt::Display for MessageID { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.val().is_ascii() { write!(f, "{}", unsafe { str::from_utf8_unchecked(self.val()) }) } else { write!(f, "{}", String::from_utf8_lossy(self.val())) } } } impl PartialEq for MessageID { fn eq(&self, other: &MessageID) -> bool { self.raw() == other.raw() } } impl fmt::Debug for MessageID { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", String::from_utf8(self.raw().to_vec()).unwrap()) } } #[derive(Clone, Serialize, Deserialize)] pub struct References { pub raw: Vec, pub refs: Vec, } impl fmt::Debug for References { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#?}", self.refs) } }