summaryrefslogtreecommitdiffstats
path: root/melib
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-09-24 16:51:51 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-09-24 16:54:06 +0300
commite407b1e22489b8898b4b672e2aab89a3ca28a401 (patch)
tree34bddc4888d38d4137908fbf5b04d916fbe4c437 /melib
parenta1e3f269de2725262a1aff98564db51992091595 (diff)
melib: add README.md and email module doco
Diffstat (limited to 'melib')
-rw-r--r--melib/Cargo.toml7
-rw-r--r--melib/README.md83
-rw-r--r--melib/src/email.rs69
-rw-r--r--melib/src/email/attachments.rs1
-rw-r--r--melib/src/email/compose.rs1
-rw-r--r--melib/src/email/headers.rs1
-rw-r--r--melib/src/email/list_management.rs1
-rw-r--r--melib/src/email/mailto.rs1
-rw-r--r--melib/src/email/parser.rs6
-rw-r--r--melib/src/email/signatures.rs14
10 files changed, 175 insertions, 9 deletions
diff --git a/melib/Cargo.toml b/melib/Cargo.toml
index 2213a74a..0da14196 100644
--- a/melib/Cargo.toml
+++ b/melib/Cargo.toml
@@ -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"
diff --git a/melib/README.md b/melib/README.md
new file mode 100644
index 00000000..b8d82cab
--- /dev/null
+++ b/melib/README.md
@@ -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");
+```
diff --git a/melib/src/email.rs b/melib/src/email.rs
index 89cc1375..6e31cdb0 100644
--- a/melib/src/email.rs
+++ b/melib/src/email.rs
@@ -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;
diff --git a/melib/src/email/attachments.rs b/melib/src/email/attachments.rs
index a5a1a7c8..ccffe7a0 100644
--- a/melib/src/email/attachments.rs
+++ b/melib/src/email/attachments.rs
@@ -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},
diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs
index 59c5eacd..1a7ad905 100644
--- a/melib/src/email/compose.rs
+++ b/melib/src/email/compose.rs
@@ -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,
diff --git a/melib/src/email/headers.rs b/melib/src/email/headers.rs
index 4dbe9899..1568fe4f 100644
--- a/melib/src/email/headers.rs
+++ b/melib/src/email/headers.rs
@@ -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;
diff --git a/melib/src/email/list_management.rs b/melib/src/email/list_management.rs
index 64a8d248..aad769fd 100644
--- a/melib/src/email/list_management.rs
+++ b/melib/src/email/list_management.rs
@@ -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;
diff --git a/melib/src/email/mailto.rs b/melib/src/email/mailto.rs
index af36cac6..a5ecaa15 100644
--- a/melib/src/email/mailto.rs
+++ b/melib/src/email/mailto.rs
@@ -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;
diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs
index 08e15cf4..2d5c2625 100644
--- a/melib/src/email/parser.rs
+++ b/melib/src/email/parser.rs
@@ -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;
diff --git a/melib/src/email/signatures.rs b/melib/src/email/signatures.rs
index e53b19f8..121434a5 100644
--- a/melib/src/email/signatures.rs
+++ b/melib/src/email/signatures.rs
@@ -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",
+ )),
}
}