summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-09-16 13:07:26 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-09-16 15:17:48 +0300
commit3618bdcffba4911a5eb0e4a7a4c846e5445cdaf5 (patch)
tree02318e8c9eeba557b67554554c5fb4ef6e7e8ff6
parent366e557e1cc96a7f9b12c10f28f088d2ef4712cb (diff)
melib/imap: treat server input as bytes
Server input was assumed valid ascii and converted haphazardly to &str. Don't do that, since it might not be valid UTF8.
-rw-r--r--melib/src/backends/imap.rs80
-rw-r--r--melib/src/backends/imap/cache/sync.rs16
-rw-r--r--melib/src/backends/imap/connection.rs118
-rw-r--r--melib/src/backends/imap/operations.rs10
-rw-r--r--melib/src/backends/imap/protocol_parser.rs404
-rw-r--r--melib/src/backends/imap/untagged.rs14
-rw-r--r--melib/src/backends/imap/watch.rs37
-rw-r--r--melib/src/email/parser.rs21
8 files changed, 367 insertions, 333 deletions
diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs
index cb4d994b..60e3c381 100644
--- a/melib/src/backends/imap.rs
+++ b/melib/src/backends/imap.rs
@@ -44,7 +44,7 @@ use crate::backends::{
use crate::conf::AccountSettings;
use crate::connections::timeout;
-use crate::email::*;
+use crate::email::{parser::BytesExt, *};
use crate::error::{MeliError, Result, ResultIntoMeliError};
use futures::lock::Mutex as FutureMutex;
use futures::stream::Stream;
@@ -534,7 +534,7 @@ impl MailBackend for ImapType {
let uid_store = self.uid_store.clone();
let connection = self.connection.clone();
Ok(Box::pin(async move {
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let mut conn = connection.lock().await;
conn.select_mailbox(mailbox_hash, &mut response, true)
.await?;
@@ -645,7 +645,7 @@ impl MailBackend for ImapType {
mailbox.imap_path().to_string()
};
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let mut conn = connection.lock().await;
conn.select_mailbox(source_mailbox_hash, &mut response, false)
.await?;
@@ -711,7 +711,7 @@ impl MailBackend for ImapType {
return Ok(());
}
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let mut conn = connection.lock().await;
conn.select_mailbox(mailbox_hash, &mut response, false)
.await?;
@@ -883,7 +883,7 @@ impl MailBackend for ImapType {
* flag set. */
}
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
{
let mut conn_lck = connection.lock().await;
conn_lck.unselect().await?;
@@ -901,11 +901,11 @@ impl MailBackend for ImapType {
.read_response(&mut response, RequiredResponses::empty())
.await?;
}
- let ret: Result<()> = ImapResponse::try_from(response.as_str())?.into();
+ let ret: Result<()> = ImapResponse::try_from(response.as_slice())?.into();
ret?;
let new_hash = get_path_hash!(path.as_str());
uid_store.mailboxes.lock().await.clear();
- Ok((new_hash, new_mailbox_fut?.await.map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
+ Ok((new_hash, new_mailbox_fut?.await.map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", String::from_utf8_lossy(&response), err)))?))
}))
}
@@ -928,7 +928,7 @@ impl MailBackend for ImapType {
return Err(MeliError::new(format!("You do not have permission to delete `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
}
}
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
{
let mut conn_lck = connection.lock().await;
/* make sure mailbox is not selected before it gets deleted, otherwise
@@ -950,10 +950,10 @@ impl MailBackend for ImapType {
.read_response(&mut response, RequiredResponses::empty())
.await?;
}
- let ret: Result<()> = ImapResponse::try_from(response.as_str())?.into();
+ let ret: Result<()> = ImapResponse::try_from(response.as_slice())?.into();
ret?;
uid_store.mailboxes.lock().await.clear();
- new_mailbox_fut?.await.map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
+ new_mailbox_fut?.await.map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", String::from_utf8_lossy(&response), err).into())
}))
}
@@ -974,7 +974,7 @@ impl MailBackend for ImapType {
command = format!("SUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path());
}
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
{
let mut conn_lck = connection.lock().await;
if new_val {
@@ -989,7 +989,7 @@ impl MailBackend for ImapType {
.await?;
}
- let ret: Result<()> = ImapResponse::try_from(response.as_str())?.into();
+ let ret: Result<()> = ImapResponse::try_from(response.as_slice())?.into();
if ret.is_ok() {
uid_store
.mailboxes
@@ -1014,7 +1014,7 @@ impl MailBackend for ImapType {
let new_mailbox_fut = self.mailboxes();
Ok(Box::pin(async move {
let command: String;
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
{
let mailboxes = uid_store.mailboxes.lock().await;
let permissions = mailboxes[&mailbox_hash].permissions();
@@ -1041,10 +1041,10 @@ impl MailBackend for ImapType {
.await?;
}
let new_hash = get_path_hash!(new_path.as_str());
- let ret: Result<()> = ImapResponse::try_from(response.as_str())?.into();
+ let ret: Result<()> = ImapResponse::try_from(response.as_slice())?.into();
ret?;
uid_store.mailboxes.lock().await.clear();
- new_mailbox_fut?.await.map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
+ new_mailbox_fut?.await.map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", String::from_utf8_lossy(&response), err))?;
Ok(BackendMailbox::clone(
&uid_store.mailboxes.lock().await[&new_hash],
))
@@ -1172,7 +1172,7 @@ impl MailBackend for ImapType {
let uid_store = self.uid_store.clone();
Ok(Box::pin(async move {
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let mut conn = connection.lock().await;
conn.examine_mailbox(mailbox_hash, &mut response, false)
.await?;
@@ -1180,16 +1180,18 @@ impl MailBackend for ImapType {
.await?;
conn.read_response(&mut response, RequiredResponses::SEARCH)
.await?;
- debug!(&response);
-
- let mut lines = response.lines();
- for l in lines.by_ref() {
- if l.starts_with("* SEARCH") {
+ debug!(
+ "searching for {} returned: {}",
+ query_str,
+ String::from_utf8_lossy(&response)
+ );
+
+ for l in response.split_rn() {
+ if l.starts_with(b"* SEARCH") {
use std::iter::FromIterator;
let uid_index = uid_store.uid_index.lock()?;
return Ok(SmallVec::from_iter(
- l["* SEARCH".len()..]
- .trim()
+ String::from_utf8_lossy(l[b"* SEARCH".len()..].trim())
.split_whitespace()
.map(UID::from_str)
.filter_map(std::result::Result::ok)
@@ -1198,7 +1200,9 @@ impl MailBackend for ImapType {
));
}
}
- Err(MeliError::new(response))
+ Err(MeliError::new(
+ String::from_utf8_lossy(&response).to_string(),
+ ))
}))
}
}
@@ -1303,7 +1307,7 @@ impl ImapType {
futures::executor::block_on(timeout(self.server_conf.timeout, conn.connect()))
.unwrap()
.unwrap();
- let mut res = String::with_capacity(8 * 1024);
+ let mut res = Vec::with_capacity(8 * 1024);
futures::executor::block_on(timeout(
self.server_conf.timeout,
conn.send_command(b"NOOP"),
@@ -1332,7 +1336,7 @@ impl ImapType {
.unwrap();
futures::executor::block_on(timeout(
self.server_conf.timeout,
- conn.read_lines(&mut res, String::new()),
+ conn.read_lines(&mut res, Vec::new()),
))
.unwrap()
.unwrap();
@@ -1348,7 +1352,7 @@ impl ImapType {
conn = iter.into_conn();
}
*/
- println!("S: {}", &res);
+ println!("S: {}", String::from_utf8_lossy(&res));
}
Err(error) => println!("error: {}", error),
}
@@ -1359,7 +1363,7 @@ impl ImapType {
connection: &Arc<FutureMutex<ImapConnection>>,
) -> Result<HashMap<MailboxHash, ImapMailbox>> {
let mut mailboxes: HashMap<MailboxHash, ImapMailbox> = Default::default();
- let mut res = String::with_capacity(8 * 1024);
+ let mut res = Vec::with_capacity(8 * 1024);
let mut conn = connection.lock().await;
let has_list_status: bool = conn
.uid_store
@@ -1381,14 +1385,12 @@ impl ImapType {
conn.read_response(&mut res, RequiredResponses::LIST_REQUIRED)
.await?;
}
- debug!("out: {}", &res);
+ debug!("out: {}", String::from_utf8_lossy(&res));
let mut lines = res.split_rn();
/* Remove "M__ OK .." line */
lines.next_back();
for l in lines {
- if let Ok(mut mailbox) =
- protocol_parser::list_mailbox_result(l.as_bytes()).map(|(_, v)| v)
- {
+ if let Ok(mut mailbox) = protocol_parser::list_mailbox_result(&l).map(|(_, v)| v) {
if let Some(parent) = mailbox.parent {
if mailboxes.contains_key(&parent) {
mailboxes
@@ -1414,9 +1416,7 @@ impl ImapType {
} else {
mailboxes.insert(mailbox.hash, mailbox);
}
- } else if let Ok(status) =
- protocol_parser::status_response(l.as_bytes()).map(|(_, v)| v)
- {
+ } else if let Ok(status) = protocol_parser::status_response(&l).map(|(_, v)| v) {
if let Some(mailbox_hash) = status.mailbox {
if mailboxes.contains_key(&mailbox_hash) {
let entry = mailboxes.entry(mailbox_hash).or_default();
@@ -1437,13 +1437,11 @@ impl ImapType {
conn.read_response(&mut res, RequiredResponses::LSUB_REQUIRED)
.await?;
let mut lines = res.split_rn();
- debug!("out: {}", &res);
+ debug!("out: {}", String::from_utf8_lossy(&res));
/* Remove "M__ OK .." line */
lines.next_back();
for l in lines {
- if let Ok(subscription) =
- protocol_parser::list_mailbox_result(l.as_bytes()).map(|(_, v)| v)
- {
+ if let Ok(subscription) = protocol_parser::list_mailbox_result(&l).map(|(_, v)| v) {
if let Some(f) = mailboxes.get_mut(&subscription.hash()) {
f.is_subscribed = true;
}
@@ -1651,7 +1649,7 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result<Vec<Envelope>> {
}
let mut conn = connection.lock().await;
debug!("locked for fetch {}", mailbox_path);
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let max_uid_left = max_uid;
let chunk_size = 250;
@@ -1686,7 +1684,7 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result<Vec<Envelope>> {
debug!(
"fetch response is {} bytes and {} lines",
response.len(),
- response.lines().count()
+ String::from_utf8_lossy(&response).lines().count()
);
let (_, mut v, _) = protocol_parser::fetch_responses(&response)?;
debug!("responses len is {}", v.len());
diff --git a/melib/src/backends/imap/cache/sync.rs b/melib/src/backends/imap/cache/sync.rs
index 1ff2e968..861f3cb8 100644
--- a/melib/src/backends/imap/cache/sync.rs
+++ b/melib/src/backends/imap/cache/sync.rs
@@ -83,7 +83,7 @@ impl ImapConnection {
mailbox_hash: MailboxHash,
) -> Result<()> {
debug!("build_cache {}", mailbox_hash);
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
// 1 get uidvalidity, highestmodseq
let select_response = self
.select_mailbox(mailbox_hash, &mut response, true)
@@ -119,7 +119,7 @@ impl ImapConnection {
) -> Result<Option<Vec<Envelope>>> {
let mut payload = vec![];
debug!("resync_basic");
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let cached_uidvalidity = self
.uid_store
.uidvalidity
@@ -181,7 +181,7 @@ impl ImapConnection {
debug!(
"fetch response is {} bytes and {} lines",
response.len(),
- response.lines().count()
+ String::from_utf8_lossy(&response).lines().count()
);
let (_, mut v, _) = protocol_parser::fetch_responses(&response)?;
debug!("responses len is {}", v.len());
@@ -344,7 +344,7 @@ impl ImapConnection {
) -> Result<Option<Vec<Envelope>>> {
let mut payload = vec![];
debug!("resync_condstore");
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let cached_uidvalidity = self
.uid_store
.uidvalidity
@@ -465,7 +465,7 @@ impl ImapConnection {
debug!(
"fetch response is {} bytes and {} lines",
response.len(),
- response.lines().count()
+ String::from_utf8_lossy(&response).lines().count()
);
let (_, mut v, _) = protocol_parser::fetch_responses(&response)?;
debug!("responses len is {}", v.len());
@@ -597,7 +597,7 @@ impl ImapConnection {
self.read_response(&mut response, RequiredResponses::SEARCH)
.await?;
//1) update cached flags for old messages;
- let (_, v) = protocol_parser::search_results(response.as_bytes())?;
+ let (_, v) = protocol_parser::search_results(response.as_slice())?;
for uid in v {
valid_envs.insert(generate_envelope_hash(&mailbox_path, &uid));
}
@@ -644,7 +644,7 @@ impl ImapConnection {
}
pub async fn init_mailbox(&mut self, mailbox_hash: MailboxHash) -> Result<SelectResponse> {
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let (mailbox_path, mailbox_exists, unseen, permissions) = {
let f = &self.uid_store.mailboxes.lock().await[&mailbox_hash];
(
@@ -708,7 +708,7 @@ impl ImapConnection {
.await?;
self.read_response(&mut response, RequiredResponses::STATUS)
.await?;
- let (_, status) = protocol_parser::status_response(response.as_bytes())?;
+ let (_, status) = protocol_parser::status_response(response.as_slice())?;
if let Some(uidnext) = status.uidnext {
if uidnext == 0 {
return Err(MeliError::new(
diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs
index a2e46d5c..d13a5b7d 100644
--- a/melib/src/backends/imap/connection.rs
+++ b/melib/src/backends/imap/connection.rs
@@ -165,7 +165,7 @@ impl ImapStream {
.flush()
.await
.chain_err_kind(crate::error::ErrorKind::Network)?;
- let mut response = String::with_capacity(1024);
+ let mut response = Vec::with_capacity(1024);
let mut broken = false;
let now = Instant::now();
@@ -174,24 +174,24 @@ impl ImapStream {
.read(&mut buf)
.await
.chain_err_kind(crate::error::ErrorKind::Network)?;
- response.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..len]) });
+ response.extend_from_slice(&buf[0..len]);
match server_conf.protocol {
ImapProtocol::IMAP { .. } => {
- if response.starts_with("* OK ") && response.find("\r\n").is_some() {
- if let Some(pos) = response.as_bytes().find(b"\r\n") {
+ if response.starts_with(b"* OK ") && response.find(b"\r\n").is_some() {
+ if let Some(pos) = response.find(b"\r\n") {
response.drain(0..pos + 2);
}
}
}
ImapProtocol::ManageSieve => {
- if response.starts_with("OK ") && response.find("\r\n").is_some() {
+ if response.starts_with(b"OK ") && response.find(b"\r\n").is_some() {
response.clear();
broken = true;
break;
}
}
}
- if response.starts_with("M1 OK") {
+ if response.starts_with(b"M1 OK") {
broken = true;
break;
}
@@ -262,7 +262,7 @@ impl ImapStream {
crate::LoggingLevel::WARN,
);
}
- let mut res = String::with_capacity(8 * 1024);
+ let mut res = Vec::with_capacity(8 * 1024);
let mut ret = ImapStream {
cmd_id,
stream,
@@ -295,10 +295,10 @@ impl ImapStream {
ret.read_response(&mut res).await?;
let capabilities: std::result::Result<Vec<&[u8]>, _> = res
.split_rn()
- .find(|l| l.starts_with("* CAPABILITY"))
+ .find(|l| l.starts_with(b"* CAPABILITY"))
.ok_or_else(|| MeliError::new(""))
.and_then(|res| {
- protocol_parser::capabilities(res.as_bytes())
+ protocol_parser::capabilities(&res)
.map_err(|_| MeliError::new(""))
.map(|(_, v)| v)
});
@@ -306,8 +306,10 @@ impl ImapStream {
if capabilities.is_err() {
return Err(MeliError::new(format!(
"Could not connect to {}: expected CAPABILITY response but got:{}",
- &server_conf.server_hostname, res
- )));
+ &server_conf.server_hostname,
+ String::from_utf8_lossy(&res)
+ ))
+ .set_kind(ErrorKind::Bug));
}
let capabilities = capabilities.unwrap();
@@ -342,22 +344,22 @@ impl ImapStream {
let tag_start = format!("M{} ", (ret.cmd_id - 1));
loop {
- ret.read_lines(&mut res, &String::new(), false).await?;
+ ret.read_lines(&mut res, &[], false).await?;
let mut should_break = false;
for l in res.split_rn() {
- if l.starts_with("* CAPABILITY") {
- capabilities = protocol_parser::capabilities(l.as_bytes())
+ if l.starts_with(b"* CAPABILITY") {
+ capabilities = protocol_parser::capabilities(&l)
.map(|(_, capabilities)| {
HashSet::from_iter(capabilities.into_iter().map(|s: &[u8]| s.to_vec()))
})
.ok();
}
- if l.starts_with(tag_start.as_str()) {
- if !l[tag_start.len()..].trim().starts_with("OK ") {
+ if l.starts_with(tag_start.as_bytes()) {
+ if !l[tag_start.len()..].trim().starts_with(b"OK ") {
return Err(MeliError::new(format!(
"Could not connect. Server replied with '{}'",
- l[tag_start.len()..].trim()
+ String::from_utf8_lossy(l[tag_start.len()..].trim())
))
.set_err_kind(crate::error::ErrorKind::Authentication));
}
@@ -375,7 +377,7 @@ impl ImapStream {
drop(capabilities);
ret.send_command(b"CAPABILITY").await?;
ret.read_response(&mut res).await.unwrap();
- let capabilities = protocol_parser::capabilities(res.as_bytes())?.1;
+ let capabilities = protocol_parser::capabilities(&res)?.1;
let capabilities = HashSet::from_iter(capabilities.into_iter().map(|s| s.to_vec()));
Ok((capabilities, ret))
} else {
@@ -384,10 +386,10 @@ impl ImapStream {
}
}
- pub async fn read_response(&mut self, ret: &mut String) -> Result<()> {
+ pub async fn read_response(&mut self, ret: &mut Vec<u8>) -> Result<()> {
let id = match self.protocol {
- ImapProtocol::IMAP { .. } => format!("M{} ", self.cmd_id - 1),
- ImapProtocol::ManageSieve => String::new(),
+ ImapProtocol::IMAP { .. } => format!("M{} ", self.cmd_id - 1).into_bytes(),
+ ImapProtocol::ManageSieve => Vec::new(),
};
self.read_lines(ret, &id, true).await?;
Ok(())
@@ -395,8 +397,8 @@ impl ImapStream {
pub async fn read_lines(
&mut self,
- ret: &mut String,
- termination_string: &str,
+ ret: &mut Vec<u8>,
+ termination_string: &[u8],
keep_termination_string: bool,
) -> Result<()> {
let mut buf: Vec<u8> = vec![0; Connection::IO_BUF_SIZE];
@@ -406,31 +408,31 @@ impl ImapStream {
match timeout(self.timeout, self.stream.read(&mut buf)).await? {
Ok(0) => break,
Ok(b) => {
- ret.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..b]) });
+ ret.extend_from_slice(&buf[0..b]);
if let Some(mut pos) = ret[last_line_idx..].rfind("\r\n") {
- if ret[last_line_idx..].starts_with("* BYE") {
+ if ret[last_line_idx..].starts_with(b"* BYE") {
return Err(MeliError::new("Disconnected"));
}
if let Some(prev_line) =
- ret[last_line_idx..pos + last_line_idx].rfind("\r\n")
+ ret[last_line_idx..pos + last_line_idx].rfind(b"\r\n")
{
- last_line_idx += prev_line + "\r\n".len();
- pos -= prev_line + "\r\n".len();
+ last_line_idx += prev_line + b"\r\n".len();
+ pos -= prev_line + b"\r\n".len();
}
- if Some(pos + "\r\n".len()) == ret.get(last_line_idx..).map(|r| r.len()) {
+ if Some(pos + b"\r\n".len()) == ret.get(last_line_idx..).map(|r| r.len()) {
if !termination_string.is_empty()
&& ret[last_line_idx..].starts_with(termination_string)
{
debug!(&ret[last_line_idx..]);
if !keep_termination_string {
- ret.replace_range(last_line_idx.., "");
+ ret.splice(last_line_idx.., std::iter::empty::<u8>());
}
break;
} else if termination_string.is_empty() {
break;
}
}
- last_line_idx += pos + "\r\n".len();
+ last_line_idx += pos + b"\r\n".len();
}
}
Err(e) => {
@@ -443,9 +445,9 @@ impl ImapStream {
}
pub async fn wait_for_continuation_request(&mut self) -> Result<()> {
- let term = "+ ".to_string();
- let mut ret = String::new();
- self.read_lines(&mut ret, &term, false).await?;
+ let term = b"+ ";
+ let mut ret = Vec::new();
+ self.read_lines(&mut ret, &term[..], false).await?;
Ok(())
}
@@ -546,7 +548,7 @@ impl ImapConnection {
}
}
if debug!(self.stream.is_ok()) {
- let mut ret = String::new();
+ let mut ret = Vec::new();
if let Err(err) = try_await(async {
self.send_command(b"NOOP").await?;
self.read_response(&mut ret, RequiredResponses::empty())
@@ -586,7 +588,7 @@ impl ImapConnection {
SyncPolicy::None => { /* do nothing, sync is disabled */ }
_ => {
/* Upgrade to Condstore */
- let mut ret = String::new();
+ let mut ret = Vec::new();
if capabilities.contains(&b"ENABLE"[..]) {
self.send_command(b"ENABLE CONDSTORE").await?;
self.read_response(&mut ret, RequiredResponses::empty())
@@ -605,11 +607,11 @@ impl ImapConnection {
}
#[cfg(feature = "deflate_compression")]
if capabilities.contains(&b"COMPRESS=DEFLATE"[..]) && deflate {
- let mut ret = String::new();
+ let mut ret = Vec::new();
self.send_command(b"COMPRESS DEFLATE").await?;
self.read_response(&mut ret, RequiredResponses::empty())
.await?;
- match ImapResponse::try_from(ret.as_str())? {
+ match ImapResponse::try_from(ret.as_slice())? {
ImapResponse::No(code)
| ImapResponse::Bad(code)
| ImapResponse::Preauth(code)
@@ -645,25 +647,25 @@ impl ImapConnection {
pub fn read_response<'a>(
&'a mut self,
- ret: &'a mut String,
+ ret: &'a mut Vec<u8>,
required_responses: RequiredResponses,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(async move {
- let mut response = String::new();
+ let mut response = Vec::new();
ret.clear();
self.stream.as_mut()?.read_response(&mut response).await?;
*self.uid_store.is_online.lock().unwrap() = (Instant::now(), Ok(()));
match self.server_conf.protocol {
ImapProtocol::IMAP { .. } => {
- let r: ImapResponse = ImapResponse::try_from(response.as_str())?;
+ let r: ImapResponse = ImapResponse::try_from(response.as_slice())?;
match r {
ImapResponse::Bye(ref response_code) => {
self.stream = Err(MeliError::new(format!(
"Offline: received BYE: {:?}",
response_code
)));
- ret.push_str(&response);
+ ret.extend_from_slice(&response);
return r.into();
}
ImapResponse::No(ref response_code)
@@ -685,7 +687,7 @@ impl ImapConnection {
level: crate::logging::LoggingLevel::ERROR,
},
);
- ret.push_str(&response);
+ ret.extend_from_slice(&response);
return r.into();
}
ImapResponse::Bad(ref response_code) => {
@@ -699,7 +701,7 @@ impl ImapConnection {
level: crate::logging::LoggingLevel::ERROR,
},
);
- ret.push_str(&response);
+ ret.extend_from_slice(&response);
return r.into();
}
_ => {}
@@ -711,20 +713,24 @@ impl ImapConnection {
for l in response.split_rn() {
/*debug!("check line: {}", &l);*/
if required_responses.check(l) || !self.process_untagged(l).await? {
- ret.push_str(l);
+ ret.extend_from_slice(l);
}
}
Ok(())
}
ImapProtocol::ManageSieve => {
- ret.push_str(&response);
+ ret.extend_from_slice(&response);
Ok(())
}
}
})
}
- pub async fn read_lines(&mut self, ret: &mut String, termination_string: String) -> Result<()> {
+ pub async fn read_lines(
+ &mut self,
+ ret: &mut Vec<u8>,
+ termination_string: Vec<u8>,
+ ) -> Result<()> {
self.stream
.as_mut()?
.read_lines(ret, &termination_string, false)
@@ -783,7 +789,7 @@ impl ImapConnection {
pub async fn select_mailbox(
&mut self,
mailbox_hash: MailboxHash,
- ret: &mut String,
+ ret: &mut Vec<u8>,
force: bool,
) -> Result<Option<SelectResponse>> {
if !force && self.stream.as_ref()?.current_mailbox == MailboxSelection::Select(mailbox_hash)
@@ -809,7 +815,11 @@ impl ImapConnection {
.await?;
self.read_response(ret, RequiredResponses::SELECT_REQUIRED)
.await?;
- debug!("select response {}", ret);
+ debug!(
+ "{} select response {}",
+ imap_path,
+ String::from_utf8_lossy(&ret)
+ );
let select_response = protocol_parser::select_response(&ret).chain_err_summary(|| {
format!("Could not parse select response for mailbox {}", imap_path)
})?;
@@ -868,7 +878,7 @@ impl ImapConnection {
pub async fn examine_mailbox(
&mut self,
mailbox_hash: MailboxHash,
- ret: &mut String,
+ ret: &mut Vec<u8>,
force: bool,
) -> Result<Option<SelectResponse>> {
if !force
@@ -891,7 +901,7 @@ impl ImapConnection {
.await?;
self.read_response(ret, RequiredResponses::EXAMINE_REQUIRED)
.await?;
- debug!("examine response {}", ret);
+ debug!("examine response {}", String::from_utf8_lossy(&ret));
let select_response = protocol_parser::select_response(&ret).chain_err_summary(|| {
format!("Could not parse select response for mailbox {}", imap_path)
})?;
@@ -915,7 +925,7 @@ impl ImapConnection {
match self.stream.as_mut()?.current_mailbox.take() {
MailboxSelection::Examine(_) |
MailboxSelection::Select(_) => {
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
if self
.uid_store
.capabilities
@@ -970,7 +980,7 @@ impl ImapConnection {
_select_response: &SelectResponse,
) -> Result<()> {
debug_assert!(low > 0);
- let mut response = String::new();
+ let mut response = Vec::new();
self.send_command(format!("UID SEARCH {}:*", low).as_bytes())
.await?;
self.read_response(&mut response, RequiredResponses::SEARCH)
@@ -980,7 +990,7 @@ impl ImapConnection {
let msn_index = msn_index_lck.entry(mailbox_hash).or_default();
let _ = msn_index.drain(low - 1..);
msn_index.extend(
- debug!(protocol_parser::search_results(response.as_bytes()))?
+ debug!(protocol_parser::search_results(&response))?
.1
.into_iter(),
);
diff --git a/melib/src/backends/imap/operations.rs b/melib/src/backends/imap/operations.rs
index 1f6e363b..d1a6cfdb 100644
--- a/melib/src/backends/imap/operations.rs
+++ b/melib/src/backends/imap/operations.rs
@@ -64,7 +64,7 @@ impl BackendOp for ImapOp {
cache.bytes.is_some()
};
if !exists_in_cache {
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
{
let mut conn = timeout(uid_store.timeout, connection.lock()).await?;
conn.connect().await?;
@@ -78,7 +78,7 @@ impl BackendOp for ImapOp {
debug!(
"fetch response is {} bytes and {} lines",
response.len(),
- response.lines().count()
+ String::from_utf8_lossy(&response).lines().count()
);
let mut results = protocol_parser::fetch_responses(&response)?.1;
if results.len() != 1 {
@@ -114,7 +114,7 @@ impl BackendOp for ImapOp {
}
fn fetch_flags(&self) -> ResultFuture<Flag> {
- let mut response = String::with_capacity(8 * 1024);
+ let mut response = Vec::with_capacity(8 * 1024);
let connection = self.connection.clone();
let mailbox_hash = self.mailbox_hash;
let uid = self.uid;
@@ -138,9 +138,9 @@ impl BackendOp for ImapOp {
debug!(
"fetch response is {} bytes and {} lines",
response.len(),
- response.lines().count()
+ String::from_utf8_lossy(&response).lines().count()
);
- let v = protocol_parser::uid_fetch_flags_responses(response.as_bytes())
+ let v = protocol_parser::uid_fetch_flags_responses(&response)
.map(|(_, v)| v)
.map_err(MeliError::from)?;
if v.len() != 1 {
diff --git a/melib/src/backends/imap/protocol_parser.rs b/melib/src/backends/imap/protocol_parser.rs
index 334d3538..fb9ff9a4 100644
--- a/melib/src/backends/imap/protocol_parser.rs
+++ b/melib/src/backends/imap/protocol_parser.rs
@@ -66,63 +66,63 @@ bitflags! {
}
impl RequiredResponses {
- pub fn check(&self, line: &str) -> bool {
- if !line.starts_with("* ") {
+ pub fn check(&self, line: &[u8]) -> bool {
+ if !line.starts_with(b"* ") {
return false;
}
- let line = &line["* ".len()..];
+ let line = &line[b"* ".len()..];
let mut ret = false;
if self.intersects(RequiredResponses::CAPABILITY) {
- ret |= line.starts_with("CAPABILITY");
+ ret |= line.starts_with(b"CAPABILITY");
}
if self.intersects(RequiredResponses::BYE) {
- ret |= line.starts_with("BYE");
+ ret |= line.starts_with(b"BYE");
}
if self.intersects(RequiredResponses::FLAGS) {
- ret |= line.starts_with("FLAGS");
+ ret |= line.starts_with(b"FLAGS");
}
if self.intersects(RequiredResponses::EXISTS) {
- ret |= line.ends_with("EXISTS\r\n");
+ ret |= line.ends_with(b"EXISTS\r\n");
}
if self.intersects(RequiredResponses::RECENT) {
- ret |= line.ends_with("RECENT\r\n");
+ ret |= line.ends_with(b"RECENT\r\n");
}