melib/smtp: add BINARYMIME support to smtp client
Concerns #49 IMAP: Lemonade profile tracking issueimap-lemonade
parent
ed16e29de1
commit
808bdf75a1
|
@ -204,7 +204,7 @@ pub struct SmtpExtensionSupport {
|
||||||
//envelope exchange.
|
//envelope exchange.
|
||||||
#[serde(default = "crate::conf::true_val")]
|
#[serde(default = "crate::conf::true_val")]
|
||||||
prdr: bool,
|
prdr: bool,
|
||||||
#[serde(default = "crate::conf::false_val")]
|
#[serde(default = "crate::conf::true_val")]
|
||||||
binarymime: bool,
|
binarymime: bool,
|
||||||
//Resources:
|
//Resources:
|
||||||
//- http://www.postfix.org/SMTPUTF8_README.html
|
//- http://www.postfix.org/SMTPUTF8_README.html
|
||||||
|
@ -227,7 +227,7 @@ impl Default for SmtpExtensionSupport {
|
||||||
chunking: true,
|
chunking: true,
|
||||||
prdr: true,
|
prdr: true,
|
||||||
_8bitmime: true,
|
_8bitmime: true,
|
||||||
binarymime: false,
|
binarymime: true,
|
||||||
smtputf8: true,
|
smtputf8: true,
|
||||||
auth: true,
|
auth: true,
|
||||||
dsn_notify: Some("FAILURE".into()),
|
dsn_notify: Some("FAILURE".into()),
|
||||||
|
@ -641,7 +641,9 @@ impl SmtpConnection {
|
||||||
if self.server_conf.extensions.prdr {
|
if self.server_conf.extensions.prdr {
|
||||||
current_command.push(b" PRDR");
|
current_command.push(b" PRDR");
|
||||||
}
|
}
|
||||||
if self.server_conf.extensions._8bitmime {
|
if self.server_conf.extensions.binarymime {
|
||||||
|
current_command.push(b" BODY=BINARYMIME");
|
||||||
|
} else if self.server_conf.extensions._8bitmime {
|
||||||
current_command.push(b" BODY=8BITMIME");
|
current_command.push(b" BODY=8BITMIME");
|
||||||
}
|
}
|
||||||
self.send_command(¤t_command).await?;
|
self.send_command(¤t_command).await?;
|
||||||
|
@ -688,71 +690,81 @@ impl SmtpConnection {
|
||||||
//permitted on either side of the colon following FROM in the MAIL command or TO in the
|
//permitted on either side of the colon following FROM in the MAIL command or TO in the
|
||||||
//RCPT command. The syntax is exactly as given above.
|
//RCPT command. The syntax is exactly as given above.
|
||||||
|
|
||||||
//The third step in the procedure is the DATA command
|
if self.server_conf.extensions.binarymime {
|
||||||
//(or some alternative specified in a service extension).
|
let mail_length = format!("{}", mail.as_bytes().len());
|
||||||
//DATA <CRLF>
|
self.send_command(&[b"BDAT", mail_length.as_bytes(), b"LAST"])
|
||||||
self.send_command(&[b"DATA"]).await?;
|
.await?;
|
||||||
//Client SMTP implementations that employ pipelining MUST check ALL statuses associated
|
self.stream
|
||||||
//with each command in a group. For example, if none of the RCPT TO recipient addresses
|
.write_all(mail.as_bytes())
|
||||||
//were accepted the client must then check the response to the DATA command -- the client
|
.await
|
||||||
//cannot assume that the DATA command will be rejected just because none of the RCPT TO
|
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||||
//commands worked. If the DATA command was properly rejected the client SMTP can just
|
} else {
|
||||||
//issue RSET, but if the DATA command was accepted the client SMTP should send a single
|
//The third step in the procedure is the DATA command
|
||||||
//dot.
|
//(or some alternative specified in a service extension).
|
||||||
let mut _all_error = self.server_conf.extensions.pipelining;
|
//DATA <CRLF>
|
||||||
let mut _any_error = false;
|
self.send_command(&[b"DATA"]).await?;
|
||||||
let mut ignore_mailfrom = true;
|
//Client SMTP implementations that employ pipelining MUST check ALL statuses associated
|
||||||
for expected_reply_code in pipelining_queue {
|
//with each command in a group. For example, if none of the RCPT TO recipient addresses
|
||||||
let reply = self.read_lines(&mut res, expected_reply_code).await?;
|
//were accepted the client must then check the response to the DATA command -- the client
|
||||||
if !ignore_mailfrom {
|
//cannot assume that the DATA command will be rejected just because none of the RCPT TO
|
||||||
_all_error &= reply.code.is_err();
|
//commands worked. If the DATA command was properly rejected the client SMTP can just
|
||||||
_any_error |= reply.code.is_err();
|
//issue RSET, but if the DATA command was accepted the client SMTP should send a single
|
||||||
|
//dot.
|
||||||
|
let mut _all_error = self.server_conf.extensions.pipelining;
|
||||||
|
let mut _any_error = false;
|
||||||
|
let mut ignore_mailfrom = true;
|
||||||
|
for expected_reply_code in pipelining_queue {
|
||||||
|
let reply = self.read_lines(&mut res, expected_reply_code).await?;
|
||||||
|
if !ignore_mailfrom {
|
||||||
|
_all_error &= reply.code.is_err();
|
||||||
|
_any_error |= reply.code.is_err();
|
||||||
|
}
|
||||||
|
ignore_mailfrom = false;
|
||||||
|
pipelining_results.push(reply.into());
|
||||||
}
|
}
|
||||||
ignore_mailfrom = false;
|
|
||||||
pipelining_results.push(reply.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
//If accepted, the SMTP server returns a 354 Intermediate reply and considers all
|
//If accepted, the SMTP server returns a 354 Intermediate reply and considers all
|
||||||
//succeeding lines up to but not including the end of mail data indicator to be the
|
//succeeding lines up to but not including the end of mail data indicator to be the
|
||||||
//message text. When the end of text is successfully received and stored, the
|
//message text. When the end of text is successfully received and stored, the
|
||||||
//SMTP-receiver sends a "250 OK" reply.
|
//SMTP-receiver sends a "250 OK" reply.
|
||||||
self.read_lines(&mut res, Some((ReplyCode::_354, &[])))
|
self.read_lines(&mut res, Some((ReplyCode::_354, &[])))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
//Before sending a line of mail text, the SMTP client checks the first character of the
|
//Before sending a line of mail text, the SMTP client checks the first character of the
|
||||||
//line.If it is a period, one additional period is inserted at the beginning of the line.
|
//line.If it is a period, one additional period is inserted at the beginning of the line.
|
||||||
for line in mail.lines() {
|
for line in mail.lines() {
|
||||||
if line.starts_with('.') {
|
if line.starts_with('.') {
|
||||||
|
self.stream
|
||||||
|
.write_all(b".")
|
||||||
|
.await
|
||||||
|
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||||
|
}
|
||||||
self.stream
|
self.stream
|
||||||
.write_all(b".")
|
.write_all(line.as_bytes())
|
||||||
|
.await
|
||||||
|
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||||
|
self.stream
|
||||||
|
.write_all(b"\r\n")
|
||||||
.await
|
.await
|
||||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||||
}
|
}
|
||||||
self.stream
|
|
||||||
.write_all(line.as_bytes())
|
|
||||||
.await
|
|
||||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
|
||||||
self.stream
|
|
||||||
.write_all(b"\r\n")
|
|
||||||
.await
|
|
||||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mail.ends_with('\n') {
|
if !mail.ends_with('\n') {
|
||||||
|
self.stream
|
||||||
|
.write_all(b".\r\n")
|
||||||
|
.await
|
||||||
|
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
//The mail data are terminated by a line containing only a period, that is, the character
|
||||||
|
//sequence "<CRLF>.<CRLF>", where the first <CRLF> is actually the terminator of the
|
||||||
|
//previous line (see Section 4.5.2). This is the end of mail data indication.
|
||||||
self.stream
|
self.stream
|
||||||
.write_all(b".\r\n")
|
.write_all(b".\r\n")
|
||||||
.await
|
.await
|
||||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The mail data are terminated by a line containing only a period, that is, the character
|
|
||||||
//sequence "<CRLF>.<CRLF>", where the first <CRLF> is actually the terminator of the
|
|
||||||
//previous line (see Section 4.5.2). This is the end of mail data indication.
|
|
||||||
self.stream
|
|
||||||
.write_all(b".\r\n")
|
|
||||||
.await
|
|
||||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
|
||||||
|
|
||||||
//The end of mail data indicator also confirms the mail transaction and tells the SMTP
|
//The end of mail data indicator also confirms the mail transaction and tells the SMTP
|
||||||
//server to now process the stored recipients and mail data. If accepted, the SMTP
|
//server to now process the stored recipients and mail data. If accepted, the SMTP
|
||||||
//server returns a "250 OK" reply.
|
//server returns a "250 OK" reply.
|
||||||
|
|
Loading…
Reference in New Issue