diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-09-16 13:07:26 +0300 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-09-16 15:17:48 +0300 |
commit | 3618bdcffba4911a5eb0e4a7a4c846e5445cdaf5 (patch) | |
tree | 02318e8c9eeba557b67554554c5fb4ef6e7e8ff6 | |
parent | 366e557e1cc96a7f9b12c10f28f088d2ef4712cb (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.rs | 80 | ||||
-rw-r--r-- | melib/src/backends/imap/cache/sync.rs | 16 | ||||
-rw-r--r-- | melib/src/backends/imap/connection.rs | 118 | ||||
-rw-r--r-- | melib/src/backends/imap/operations.rs | 10 | ||||
-rw-r--r-- | melib/src/backends/imap/protocol_parser.rs | 404 | ||||
-rw-r--r-- | melib/src/backends/imap/untagged.rs | 14 | ||||
-rw-r--r-- | melib/src/backends/imap/watch.rs | 37 | ||||
-rw-r--r-- | melib/src/email/parser.rs | 21 |
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"); } |