melib: add README.md and email module doco

jmap-eventsource
Manos Pitsidianakis 2020-09-24 16:51:51 +03:00
parent a1e3f269de
commit e407b1e224
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
10 changed files with 175 additions and 9 deletions

View File

@ -8,10 +8,11 @@ build = "build.rs"
homepage = "https://meli.delivery"
repository = "https://git.meli.delivery/meli/meli.git"
description = "backend mail client library"
keywords = ["mail", "mua", "maildir", "imap"]
categories = [ "email"]
description = "mail library"
keywords = ["mail", "mua", "maildir", "imap", "jmap"]
categories = [ "email", "parser-implementations"]
license = "GPL-3.0-or-later"
readme = "README.md"
[lib]
name = "melib"

83
melib/README.md 100644
View File

@ -0,0 +1,83 @@
# melib
[![GitHub license](https://img.shields.io/github/license/meli/meli)](https://github.com/meli/meli/blob/master/COPYING) [![Crates.io](https://img.shields.io/crates/v/melib)](https://crates.io/crates/melib) [![docs.rs](https://docs.rs/melib/badge.svg)](https://docs.rs/melib)
Library for handling mail.
## optional features
| feature flag | dependencies | notes |
| ---------------------- | ----------------------------------- | ------------------------ |
| `imap_backend` | `native-tls` | |
| `deflate_compression` | `flate2` | for use with IMAP |
| `jmap_backend` | `isahc`, `native-tls`, `serde_json` | |
| `maildir_backend` | `notify`, `memmap` | |
| `mbox_backend` | `notify`, `memmap` | |
| `smtp` | `native-tls`, `base64` | async SMTP communication |
## Example: Parsing bytes into an `Envelope`
An `Envelope` represents the information you can get from an email's headers
and body structure. Addresses in `To`, `From` fields etc are parsed into
`Address` types.
```rust
use melib::{Attachment, Envelope};
let raw_mail = r#"From: "some name" <some@address.com>
To: "me" <myself@i.tld>
Cc:
Subject: =?utf-8?Q?gratuitously_encoded_subject?=
Message-ID: <h2g7f.z0gy2pgaen5m@address.com>
MIME-Version: 1.0
Content-Type: multipart/mixed; charset="utf-8";
boundary="bzz_bzz__bzz__"
This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly.
--bzz_bzz__bzz__
hello world.
--bzz_bzz__bzz__
Content-Type: image/gif; name="test_image.gif"; charset="utf-8"
Content-Disposition: attachment
Content-Transfer-Encoding: base64
R0lGODdhKAAXAOfZAAABzAADzQAEzgQFtBEAxAAGxBcAxwALvRcFwAAPwBcLugATuQEUuxoNuxYQ
sxwOvAYVvBsStSAVtx8YsRUcuhwhth4iuCQsyDAwuDc1vTc3uDg4uT85rkc9ukJBvENCvURGukdF
wUVKt0hLuUxPvVZSvFlYu1hbt2BZuFxdul5joGhqlnNuf3FvlnBvwXJyt3Jxw3N0oXx1gH12gV99
z317f3N7spFxwHp5wH99gYB+goF/g25+26tziIOBhWqD3oiBjICAuudkjIN+zHeC2n6Bzc1vh4eF
iYaBw8F0kImHi4KFxYyHmIWIvI2Lj4uIvYaJyY+IuJGMi5iJl4qKxZSMmIuLxpONnpGPk42NvI2M
1LKGl46OvZePm5ORlZiQnJqSnpaUmLyJnJuTn5iVmZyUoJGVyZ2VoZSVw5iXoZmWrO18rJiUyp6W
opuYnKaVnZ+Xo5yZncaMoaCYpJiaqo+Z2Z2annuf5qGZpa2WoJybpZmayZ2Z0KCZypydrZ6dp6Cd
oZ6a0aGay5ucy5+eqKGeouWMgp+b0qKbzKCfqdqPnp2ezaGgqqOgpKafqrScpp+gz6ajqKujr62j
qayksKmmq62lsaiosqqorOyWnaqqtKeqzLGptaurta2rr7Kqtq+ssLOrt6+uuLGusuqhfbWtubCv
ubKvs7GwurOwtPSazbevu+ali7SxtbiwvOykjLOyvLWytuCmqOankrSzvbazuLmyvrW0vre0uba1
wLi1ury0wLm2u721wbe3wbq3vMC2vLi4wr+3w7m5w8C4xLi6yry6vsG5xbu7xcC6zMK6xry8xry+
u8O7x729x8C9wb++yMG+wsO+vMK/w8a+y8e/zMnBzcXH18nL2///////////////////////////
////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////ywAAAAAKAAXAAAI/gBP4Cjh
IYMLEh0w4EgBgsMLEyFGFBEB5cOFABgzatS4AVssZAOsLOHCxooVMzCyoNmzaBOkJlS0VEDyZMjG
mxk3XOMF60CDBgsoPABK9KcDCRImPCiQYAECAgQCRMU4VSrGCjFarBgUSJCgQ10FBTrkNRCfPnz4
dA3UNa1btnDZqgU7Ntqzu3ej2X2mFy9eaHuhNRtMGJrhwYYN930G2K7eaNIY34U2mfJkwpgzI9Yr
GBqwR2KSvAlMOXHnw5pTNzPdLNoWIWtU9XjGjDEYS8LAlFm1SrVvzIKj5TH0KpORSZOryPgCZgqL
Ob+jG0YVRBErUrOiiGJ8KxgtYsh27xWL/tswnTtEbsiRVYdJNMHk4yOGhswGjR88UKjQ9Ey+/8TL
XKKGGn7Akph/8XX2WDTTcAYfguVt9hhrEPqmzIOJ3VUheb48WJiHG6amC4i+WVJKKCimqGIoYxyj
WWK8kKjaJ9bA18sxvXjYhourmbbMMrjI+OIn1QymDCVXANGFK4S1gQw0PxozzC+33FLLKUJq9gk1
gyWDhyNwrMLkYGUEM4wvuLRiCiieXIJJJVlmJskcZ9TZRht1lnFGGmTMkMoonVQSSSOFAGJHHI0w
ouiijDaaCCGQRgrpH3q4QYYXWDihxBE+7KCDDjnUIEVAADs=
--bzz_bzz__bzz__--"#;
let envelope = Envelope::from_bytes(raw_mail.as_bytes(), None).expect("Could not parse mail");
assert_eq!(envelope.subject().as_ref(), "gratuitously encoded subject");
assert_eq!(envelope.message_id_display().as_ref(), "<h2g7f.z0gy2pgaen5m@address.com>");
let body = envelope.body_bytes(raw_mail.as_bytes());
assert_eq!(body.content_type().to_string().as_str(), "multipart/mixed");
let body_text = body.text();
assert_eq!(body_text.as_str(), "hello world.");
let subattachments: Vec<Attachment> = body.attachments();
assert_eq!(subattachments.len(), 3);
assert_eq!(subattachments[2].content_type().name().unwrap(), "test_image.gif");
```

View File

@ -20,8 +20,75 @@
*/
/*!
* Email parsing, handling, sending etc.
* Email parsing and composing.
*
* # Parsing bytes into an `Envelope`
*
* An [`Envelope`](Envelope) represents the information you can get from an email's headers and body
* structure. Addresses in `To`, `From` fields etc are parsed into [`Address`](email::address::Address) types.
*
* ```
* use melib::{Attachment, Envelope};
*
* let raw_mail = r#"From: "some name" <some@address.com>
* To: "me" <myself@i.tld>
* Cc:
* Subject: =?utf-8?Q?gratuitously_encoded_subject?=
* Message-ID: <h2g7f.z0gy2pgaen5m@address.com>
* MIME-Version: 1.0
* Content-Type: multipart/mixed; charset="utf-8";
* boundary="bzz_bzz__bzz__"
*
* This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly.
* --bzz_bzz__bzz__
*
* hello world.
* --bzz_bzz__bzz__
* Content-Type: image/gif; name="test_image.gif"; charset="utf-8"
* Content-Disposition: attachment
* Content-Transfer-Encoding: base64
*
* R0lGODdhKAAXAOfZAAABzAADzQAEzgQFtBEAxAAGxBcAxwALvRcFwAAPwBcLugATuQEUuxoNuxYQ
* sxwOvAYVvBsStSAVtx8YsRUcuhwhth4iuCQsyDAwuDc1vTc3uDg4uT85rkc9ukJBvENCvURGukdF
* wUVKt0hLuUxPvVZSvFlYu1hbt2BZuFxdul5joGhqlnNuf3FvlnBvwXJyt3Jxw3N0oXx1gH12gV99
* z317f3N7spFxwHp5wH99gYB+goF/g25+26tziIOBhWqD3oiBjICAuudkjIN+zHeC2n6Bzc1vh4eF
* iYaBw8F0kImHi4KFxYyHmIWIvI2Lj4uIvYaJyY+IuJGMi5iJl4qKxZSMmIuLxpONnpGPk42NvI2M
* 1LKGl46OvZePm5ORlZiQnJqSnpaUmLyJnJuTn5iVmZyUoJGVyZ2VoZSVw5iXoZmWrO18rJiUyp6W
* opuYnKaVnZ+Xo5yZncaMoaCYpJiaqo+Z2Z2annuf5qGZpa2WoJybpZmayZ2Z0KCZypydrZ6dp6Cd
* oZ6a0aGay5ucy5+eqKGeouWMgp+b0qKbzKCfqdqPnp2ezaGgqqOgpKafqrScpp+gz6ajqKujr62j
* qayksKmmq62lsaiosqqorOyWnaqqtKeqzLGptaurta2rr7Kqtq+ssLOrt6+uuLGusuqhfbWtubCv
* ubKvs7GwurOwtPSazbevu+ali7SxtbiwvOykjLOyvLWytuCmqOankrSzvbazuLmyvrW0vre0uba1
* wLi1ury0wLm2u721wbe3wbq3vMC2vLi4wr+3w7m5w8C4xLi6yry6vsG5xbu7xcC6zMK6xry8xry+
* u8O7x729x8C9wb++yMG+wsO+vMK/w8a+y8e/zMnBzcXH18nL2///////////////////////////
* ////////////////////////////////////////////////////////////////////////////
* /////////////////////////////////////////////////////ywAAAAAKAAXAAAI/gBP4Cjh
* IYMLEh0w4EgBgsMLEyFGFBEB5cOFABgzatS4AVssZAOsLOHCxooVMzCyoNmzaBOkJlS0VEDyZMjG
* mxk3XOMF60CDBgsoPABK9KcDCRImPCiQYAECAgQCRMU4VSrGCjFarBgUSJCgQ10FBTrkNRCfPnz4
* dA3UNa1btnDZqgU7Ntqzu3ej2X2mFy9eaHuhNRtMGJrhwYYN930G2K7eaNIY34U2mfJkwpgzI9Yr
* GBqwR2KSvAlMOXHnw5pTNzPdLNoWIWtU9XjGjDEYS8LAlFm1SrVvzIKj5TH0KpORSZOryPgCZgqL
* Ob+jG0YVRBErUrOiiGJ8KxgtYsh27xWL/tswnTtEbsiRVYdJNMHk4yOGhswGjR88UKjQ9Ey+/8TL
* XKKGGn7Akph/8XX2WDTTcAYfguVt9hhrEPqmzIOJ3VUheb48WJiHG6amC4i+WVJKKCimqGIoYxyj
* WWK8kKjaJ9bA18sxvXjYhourmbbMMrjI+OIn1QymDCVXANGFK4S1gQw0PxozzC+33FLLKUJq9gk1
* gyWDhyNwrMLkYGUEM4wvuLRiCiieXIJJJVlmJskcZ9TZRht1lnFGGmTMkMoonVQSSSOFAGJHHI0w
* ouiijDaaCCGQRgrpH3q4QYYXWDihxBE+7KCDDjnUIEVAADs=
* --bzz_bzz__bzz__--"#;
*
* let envelope = Envelope::from_bytes(raw_mail.as_bytes(), None).expect("Could not parse mail");
* assert_eq!(envelope.subject().as_ref(), "gratuitously encoded subject");
* assert_eq!(envelope.message_id_display().as_ref(), "<h2g7f.z0gy2pgaen5m@address.com>");
*
* let body = envelope.body_bytes(raw_mail.as_bytes());
* assert_eq!(body.content_type().to_string().as_str(), "multipart/mixed");
*
* let body_text = body.text();
* assert_eq!(body_text.as_str(), "hello world.");
*
* let subattachments: Vec<Attachment> = body.attachments();
* assert_eq!(subattachments.len(), 3);
* assert_eq!(subattachments[2].content_type().name().unwrap(), "test_image.gif");
* ```
*/
pub mod address;
pub mod attachment_types;
pub mod attachments;

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Encoding/decoding of attachments */
use crate::email::{
address::StrBuilder,
parser::{self, BytesExt},

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Compose a `Draft`, with MIME and attachment support */
use super::*;
use crate::email::attachment_types::{
Charset, ContentTransferEncoding, ContentType, MultipartType,

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Wrapper type `HeaderName` for case-insensitive comparisons */
use crate::error::MeliError;
use indexmap::IndexMap;
use smallvec::SmallVec;

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Parsing of rfc2369/rfc2919 `List-*` headers */
use super::parser;
use super::Envelope;
use smallvec::SmallVec;

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Parsing of `mailto` addresses */
use super::*;
use std::convert::TryFrom;

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Parsers for email. See submodules */
use crate::error::{MeliError, Result, ResultIntoMeliError};
use nom::{
branch::alt,
@ -281,6 +282,7 @@ pub fn mail(input: &[u8]) -> Result<(Vec<(&[u8], &[u8])>, &[u8])> {
}
pub mod dates {
/*! Date values in headers */
use super::generic::*;
use super::*;
use crate::datetime::UnixTimestamp;
@ -505,6 +507,7 @@ pub mod dates {
}
pub mod generic {
/*! Generally useful parser combinators */
use super::*;
#[inline(always)]
pub fn byte_in_slice<'a>(slice: &'static [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], u8> {
@ -1205,6 +1208,7 @@ List-Archive: <http://www.host.com/list/archive/> (Web Archive)
}
pub mod headers {
/*! Email headers */
use super::*;
pub fn headers(input: &[u8]) -> IResult<&[u8], Vec<(&[u8], &[u8])>> {
@ -1465,6 +1469,7 @@ pub mod headers {
}
pub mod attachments {
/*! Email attachments */
use super::*;
use crate::email::address::*;
use crate::email::attachment_types::{ContentDisposition, ContentDispositionKind};
@ -1731,6 +1736,7 @@ pub mod attachments {
}
pub mod encodings {
/*! Email encodings (quoted printable, MIME) */
use super::*;
use crate::email::attachment_types::Charset;
use data_encoding::BASE64_MIME;

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Verification of OpenPGP signatures */
use crate::email::parser::BytesExt;
use crate::email::{
attachment_types::{ContentType, MultipartType},
@ -26,7 +27,10 @@ use crate::email::{
};
use crate::{MeliError, Result};
/// rfc3156
/// Convert raw attachment to the form needed for signature verification ([rfc3156](https://tools.ietf.org/html/rfc3156))
///
/// ## rfc3156
/// ```text
/// Upon receipt of a signed message, an application MUST:
///
/// (1) Convert line endings to the canonical <CR><LF> sequence before
@ -35,7 +39,7 @@ use crate::{MeliError, Result};
/// (2) Pass both the signed data and its associated content headers
/// along with the OpenPGP signature to the signature verification
/// service.
///
/// ```
pub fn convert_attachment_to_rfc_spec(input: &[u8]) -> Vec<u8> {
if input.is_empty() {
return Vec::new();
@ -125,8 +129,8 @@ pub fn verify_signature(a: &Attachment) -> Result<(Vec<u8>, &[u8])> {
};
Ok((signed_part, signature))
}
_ => {
unreachable!("Should not give non-signed attachments to this function");
}
_ => Err(MeliError::new(
"Should not give non-signed attachments to this function",
)),
}
}