remove lettre crate
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ codegen-units = 1
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
email = "*"
|
email = "*"
|
||||||
email-encoding = "*"
|
email-encoding = "*"
|
||||||
lettre = {version="*", features=["builder"]}
|
quoted_printable = "*"
|
||||||
charset = "*"
|
charset = "*"
|
||||||
mailparse = "*"
|
mailparse = "*"
|
||||||
mediatype = "*"
|
mediatype = "*"
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
//! Body transfer-encoding.
|
||||||
|
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use email_encoding::body::{Encoding, base64};
|
||||||
|
|
||||||
|
/// The `Content-Transfer-Encoding` of a body, as defined in RFC 2045 section 6.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ContentTransferEncoding {
|
||||||
|
SevenBit,
|
||||||
|
EightBit,
|
||||||
|
QuotedPrintable,
|
||||||
|
Base64,
|
||||||
|
Binary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ContentTransferEncoding {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(match self {
|
||||||
|
Self::SevenBit => "7bit",
|
||||||
|
Self::EightBit => "8bit",
|
||||||
|
Self::QuotedPrintable => "quoted-printable",
|
||||||
|
Self::Base64 => "base64",
|
||||||
|
Self::Binary => "binary",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Body content to be encoded.
|
||||||
|
///
|
||||||
|
/// Pass [`BodyContent::Text`] whenever the content is valid UTF-8: it allows the more
|
||||||
|
/// efficient `7bit` and `quoted-printable` encodings to be chosen, while binary content
|
||||||
|
/// always ends up as `base64`.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum BodyContent {
|
||||||
|
Text(String),
|
||||||
|
Binary(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for BodyContent {
|
||||||
|
fn from(content: String) -> Self {
|
||||||
|
Self::Text(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for BodyContent {
|
||||||
|
fn from(content: &str) -> Self {
|
||||||
|
Self::Text(content.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for BodyContent {
|
||||||
|
fn from(content: Vec<u8>) -> Self {
|
||||||
|
Self::Binary(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u8]> for BodyContent {
|
||||||
|
fn from(content: &[u8]) -> Self {
|
||||||
|
Self::Binary(content.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BodyContent> for Vec<u8> {
|
||||||
|
fn from(content: BodyContent) -> Self {
|
||||||
|
match content {
|
||||||
|
BodyContent::Text(content) => content.into_bytes(),
|
||||||
|
BodyContent::Binary(content) => content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode `content`, choosing the most compact of `7bit`, `quoted-printable` and `base64`.
|
||||||
|
///
|
||||||
|
/// Line endings of text content are normalized to CRLF.
|
||||||
|
pub fn encode(content: impl Into<BodyContent>) -> (Vec<u8>, ContentTransferEncoding) {
|
||||||
|
let mut content = content.into();
|
||||||
|
|
||||||
|
// Chosen before normalizing line endings, since inserting `\r` changes line lengths.
|
||||||
|
let encoding = match &content {
|
||||||
|
BodyContent::Text(content) => Encoding::choose(content.as_str(), false),
|
||||||
|
BodyContent::Binary(content) => Encoding::choose(content.as_slice(), false),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let BodyContent::Text(content) = &mut content {
|
||||||
|
crlf_line_endings(content);
|
||||||
|
}
|
||||||
|
let content = Vec::from(content);
|
||||||
|
|
||||||
|
match encoding {
|
||||||
|
Encoding::SevenBit => (content, ContentTransferEncoding::SevenBit),
|
||||||
|
Encoding::EightBit => (content, ContentTransferEncoding::EightBit),
|
||||||
|
Encoding::QuotedPrintable => (
|
||||||
|
quoted_printable::encode(content),
|
||||||
|
ContentTransferEncoding::QuotedPrintable,
|
||||||
|
),
|
||||||
|
Encoding::Base64 => {
|
||||||
|
let mut encoded = String::with_capacity(base64::encoded_len(content.len()));
|
||||||
|
base64::encode(&content, &mut encoded).expect("String is always writable");
|
||||||
|
(encoded.into_bytes(), ContentTransferEncoding::Base64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In place conversion to CRLF line endings.
|
||||||
|
///
|
||||||
|
/// Ported from `lettre::message::body` (MIT licensed).
|
||||||
|
fn crlf_line_endings(content: &mut String) {
|
||||||
|
// relies on the indices being in reverse order
|
||||||
|
for ix in lone_lf_indices(content) {
|
||||||
|
content.insert(ix, '\r');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find, in reverse order, the indices at which a `\r` has to be inserted to give
|
||||||
|
/// `content` CRLF line endings.
|
||||||
|
fn lone_lf_indices(content: &str) -> Vec<usize> {
|
||||||
|
let mut indices = Vec::new();
|
||||||
|
|
||||||
|
let mut found_lf = false;
|
||||||
|
for (ix, c) in content.char_indices().rev() {
|
||||||
|
if mem::take(&mut found_lf) && c != '\r' {
|
||||||
|
// the previous character was a `\n`, but this one is not a `\r`
|
||||||
|
indices.push(ix + c.len_utf8());
|
||||||
|
}
|
||||||
|
found_lf = c == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if found_lf {
|
||||||
|
// the first character is a `\n`
|
||||||
|
indices.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
indices
|
||||||
|
}
|
||||||
+7
-9
@@ -1,15 +1,15 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
pub mod body;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
pub use body::{BodyContent, ContentTransferEncoding};
|
||||||
use header::CowHeader;
|
use header::CowHeader;
|
||||||
pub use header::{Header, HeaderMap, parse_headers};
|
pub use header::{Header, HeaderMap, parse_headers};
|
||||||
|
|
||||||
use lettre::message::MaybeString;
|
|
||||||
use lettre::message::header::ContentTransferEncoding;
|
|
||||||
use mailparse::{
|
use mailparse::{
|
||||||
DispositionType, MailParseError, ParsedContentDisposition, ParsedContentType, body::Body,
|
DispositionType, MailParseError, ParsedContentDisposition, ParsedContentType, body::Body,
|
||||||
parse_content_disposition, parse_content_type,
|
parse_content_disposition, parse_content_type,
|
||||||
@@ -268,29 +268,27 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
|
|||||||
|
|
||||||
self.headers.add_header("Content-Type", ctype);
|
self.headers.add_header("Content-Type", ctype);
|
||||||
|
|
||||||
let body = lettre::message::Body::new(content);
|
let (bytes, encoding) = body::encode(content);
|
||||||
let encoding = body.encoding();
|
|
||||||
if encoding != ContentTransferEncoding::SevenBit {
|
if encoding != ContentTransferEncoding::SevenBit {
|
||||||
self.headers.add_header("Content-Transfer-Encoding", encoding);
|
self.headers.add_header("Content-Transfer-Encoding", encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.body_bytes = Cow::Owned(body.into_vec());
|
self.body_bytes = Cow::Owned(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_content_raw<B: Into<MaybeString>>(&mut self, content: B, ctype: Option<MediaType>) {
|
pub fn set_content_raw<B: Into<BodyContent>>(&mut self, content: B, ctype: Option<MediaType>) {
|
||||||
self.clear_content();
|
self.clear_content();
|
||||||
let body = lettre::message::Body::new(content);
|
|
||||||
|
|
||||||
if let Some(ctype) = ctype {
|
if let Some(ctype) = ctype {
|
||||||
self.headers.add_header("Content-Type", ctype);
|
self.headers.add_header("Content-Type", ctype);
|
||||||
}
|
}
|
||||||
|
|
||||||
let encoding = body.encoding();
|
let (bytes, encoding) = body::encode(content);
|
||||||
if encoding != ContentTransferEncoding::SevenBit {
|
if encoding != ContentTransferEncoding::SevenBit {
|
||||||
self.headers.add_header("Content-Transfer-Encoding", encoding);
|
self.headers.add_header("Content-Transfer-Encoding", encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.body_bytes = Cow::Owned(body.into_vec());
|
self.body_bytes = Cow::Owned(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_filename(&self) -> Option<String> {
|
pub fn get_filename(&self) -> Option<String> {
|
||||||
|
|||||||
Reference in New Issue
Block a user