/* * meli - imap * * Copyright 2020 Manos Pitsidianakis * * This file is part of meli. * * meli is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * meli is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ use super::ImapConnection; use crate::backends::imap::protocol_parser::{ ImapLineSplit, RequiredResponses, UidFetchResponse, UntaggedResponse, }; use crate::backends::BackendMailbox; use crate::backends::{ RefreshEvent, RefreshEventKind::{self, *}, }; use crate::email::Envelope; use crate::error::*; use std::time::Instant; impl ImapConnection { pub fn process_untagged(&mut self, line: &str) -> Result { macro_rules! try_fail { ($mailbox_hash: expr, $($result:expr)+) => { $(if let Err(err) = $result { *self.uid_store.is_online.lock().unwrap() = ( Instant::now(), Err(err.clone()), ); debug!("failure: {}", err.to_string()); self.uid_store.refresh_events.lock().unwrap().push(RefreshEvent { account_hash: self.uid_store.account_hash, mailbox_hash: $mailbox_hash, kind: RefreshEventKind::Failure(err.clone()), }); Err(err) } else { Ok(()) }?;)+ }; } //FIXME let mailbox_hash = if let Some(mailbox_hash) = self.current_mailbox { mailbox_hash } else { return Ok(false); }; let mailbox = std::clone::Clone::clone(&self.uid_store.mailboxes.read().unwrap()[&mailbox_hash]); let mut response = String::with_capacity(8 * 1024); let untagged_response = match super::protocol_parser::untagged_responses(line.as_bytes()).to_full_result() { Ok(None) | Err(_) => { return Ok(false); } Ok(Some(r)) => r, }; match untagged_response { UntaggedResponse::Bye { reason } => { *self.uid_store.is_online.lock().unwrap() = (std::time::Instant::now(), Err(reason.into())); } UntaggedResponse::Expunge(n) => { debug!("expunge {}", n); } UntaggedResponse::Exists(n) => { /* UID FETCH ALL UID, cross-ref, then FETCH difference headers * */ let mut prev_exists = mailbox.exists.lock().unwrap(); debug!("exists {}", n); if n > *prev_exists { try_fail!( mailbox_hash, self.send_command( &[ b"FETCH", format!("{}:{}", *prev_exists + 1, n).as_bytes(), b"(UID FLAGS RFC822)", ] .join(&b' '), ) self.read_response(&mut response, RequiredResponses::FETCH_REQUIRED) ); match super::protocol_parser::uid_fetch_responses(&response) { Ok((_, v, _)) => { 'fetch_responses: for UidFetchResponse { uid, flags, body, .. } in v { if self.uid_store.uid_index.lock().unwrap().contains_key(&uid) { continue 'fetch_responses; } if let Ok(mut env) = Envelope::from_bytes( body.unwrap(), flags.as_ref().map(|&(f, _)| f), ) { self.uid_store .hash_index .lock() .unwrap() .insert(env.hash(), (uid, mailbox_hash)); self.uid_store .uid_index .lock() .unwrap() .insert(uid, env.hash()); if let Some((_, keywords)) = flags { let mut tag_lck = self.uid_store.tag_index.write().unwrap(); for f in keywords { let hash = tag_hash!(f); if !tag_lck.contains_key(&hash) { tag_lck.insert(hash, f); } env.labels_mut().push(hash); } } debug!( "Create event {} {} {}", env.hash(), env.subject(), mailbox.path(), ); if !env.is_seen() { *mailbox.unseen.lock().unwrap() += 1; } self.uid_store.refresh_events.lock().unwrap().push( RefreshEvent { account_hash: self.uid_store.account_hash, mailbox_hash, kind: Create(Box::new(env)), }, ); } } } Err(e) => { debug!(e); } } *prev_exists = n; } else if n < *prev_exists { *prev_exists = n; } } UntaggedResponse::Recent(_) => { try_fail!( mailbox_hash, self.send_command(b"UID SEARCH RECENT") self.read_response(&mut response, RequiredResponses::SEARCH) ); match super::protocol_parser::search_results_raw(response.as_bytes()) .to_full_result() .map_err(MeliError::from) { Ok(&[]) => { debug!("UID SEARCH RECENT returned no results"); } Ok(v) => { try_fail!( mailbox_hash, self.send_command( &[b"UID FETCH", v, b"(FLAGS RFC822)"] .join(&b' '), ) self.read_response(&mut response, RequiredResponses::FETCH_REQUIRED) ); debug!(&response); match super::protocol_parser::uid_fetch_responses(&response) { Ok((_, v, _)) => { for UidFetchResponse { uid, flags, body, .. } in v { *mailbox.exists.lock().unwrap() += 1; if !self.uid_store.uid_index.lock().unwrap().contains_key(&uid) { if let Ok(mut env) = Envelope::from_bytes( body.unwrap(), flags.as_ref().map(|&(f, _)| f), ) { self.uid_store .hash_index .lock() .unwrap() .insert(env.hash(), (uid, mailbox_hash)); self.uid_store .uid_index .lock() .unwrap() .insert(uid, env.hash()); debug!( "Create event {} {} {}", env.hash(), env.subject(), mailbox.path(), ); if let Some((_, keywords)) = flags { let mut tag_lck = self.uid_store.tag_index.write().unwrap(); for f in keywords { let hash = tag_hash!(f); if !tag_lck.contains_key(&hash) { tag_lck.insert(hash, f); } env.labels_mut().push(hash); } } if !env.is_seen() { *mailbox.unseen.lock().unwrap() += 1; } self.uid_store.refresh_events.lock().unwrap().push( RefreshEvent { account_hash: self.uid_store.account_hash, mailbox_hash, kind: Create(Box::new(env)), }, ); } } } } Err(e) => { debug!(e); } } } Err(e) => { debug!( "UID SEARCH RECENT err: {}\nresp: {}", e.to_string(), &response ); } } } UntaggedResponse::Fetch(msg_seq, flags) => { /* a * {msg_seq} FETCH (FLAGS ({flags})) was received, so find out UID from msg_seq * and send update */ debug!("fetch {} {:?}", msg_seq, flags); try_fail!( mailbox_hash, self.send_command( &[ b"UID SEARCH ", format!("{}", msg_seq).as_bytes(), ] .join(&b' '), ) self.read_response(&mut response, RequiredResponses::SEARCH) ); debug!(&response); match super::protocol_parser::search_results( response.split_rn().next().unwrap_or("").as_bytes(), ) .to_full_result() { Ok(mut v) => { if let Some(uid) = v.pop() { if let Some(env_hash) = self.uid_store.uid_index.lock().unwrap().get(&uid) { self.uid_store .refresh_events .lock() .unwrap() .push(RefreshEvent { account_hash: self.uid_store.account_hash, mailbox_hash, kind: NewFlags(*env_hash, flags), }); } } } Err(e) => { debug!(&response); debug!(e); } } } } Ok(true) } }