Browse Source

imap: add support for imaps connections

Take port value and a `use_starttls` flag from the configuration file.
embed
Manos Pitsidianakis 2 years ago
parent
commit
ee82ae175a
Signed by: epilys GPG Key ID: 73627C2F690DF710
  1. 56
      melib/src/backends/imap.rs
  2. 136
      melib/src/backends/imap/connection.rs

56
melib/src/backends/imap.rs

@ -55,15 +55,22 @@ pub struct EnvelopeCache {
flags: Option<Flag>,
}
#[derive(Debug, Clone)]
pub struct ImapServerConf {
pub server_hostname: String,
pub server_username: String,
pub server_password: String,
pub server_port: u16,
pub use_starttls: bool,
pub danger_accept_invalid_certs: bool,
}
type Capabilities = FnvHashSet<Vec<u8>>;
#[derive(Debug)]
pub struct ImapType {
account_name: String,
server_hostname: String,
server_username: String,
server_password: String,
danger_accept_invalid_certs: bool,
connection: Arc<Mutex<ImapConnection>>,
server_conf: ImapServerConf,
folders: FnvHashMap<FolderHash, ImapFolder>,
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
@ -180,12 +187,7 @@ impl MailBackend for ImapType {
.capabilities
.contains(&b"IDLE"[0..]);
let folders = self.folders.clone();
let conn = ImapConnection::new_connection(
self.server_hostname.clone(),
self.server_username.clone(),
self.server_password.clone(),
self.danger_accept_invalid_certs,
);
let conn = ImapConnection::new_connection(&self.server_conf);
let main_conn = self.connection.clone();
let hash_index = self.hash_index.clone();
let uid_index = self.uid_index.clone();
@ -394,23 +396,32 @@ impl ImapType {
let server_hostname = get_conf_val!(s["server_hostname"]);
let server_username = get_conf_val!(s["server_username"]);
let server_password = get_conf_val!(s["server_password"]);
let server_port = get_conf_val!(s["server_port"], 143);
let use_starttls = get_conf_val!(s["use_starttls"], {
if server_port == 993 {
false
} else {
true
}
});
let danger_accept_invalid_certs: bool =
get_conf_val!(s["danger_accept_invalid_certs"], false);
let connection = ImapConnection::new_connection(
server_hostname.to_string(),
server_username.to_string(),
server_password.to_string(),
let server_conf = ImapServerConf {
server_hostname: server_hostname.to_string(),
server_username: server_username.to_string(),
server_password: server_password.to_string(),
server_port,
use_starttls,
danger_accept_invalid_certs,
);
};
let connection = ImapConnection::new_connection(&server_conf);
let mut m = ImapType {
account_name: s.name().to_string(),
server_hostname: get_conf_val!(s["server_hostname"]).to_string(),
server_username: get_conf_val!(s["server_username"]).to_string(),
server_password: get_conf_val!(s["server_password"]).to_string(),
server_conf,
folders: Default::default(),
connection: Arc::new(Mutex::new(connection)),
danger_accept_invalid_certs,
hash_index: Default::default(),
uid_index: Default::default(),
byte_cache: Default::default(),
@ -430,12 +441,7 @@ impl ImapType {
}
pub fn shell(&mut self) {
let mut conn = ImapConnection::new_connection(
self.server_hostname.clone(),
self.server_username.clone(),
self.server_password.clone(),
self.danger_accept_invalid_certs,
);
let mut conn = ImapConnection::new_connection(&self.server_conf);
let mut res = String::with_capacity(8 * 1024);
conn.read_response(&mut res).unwrap();
debug!("out: {}", &res);

136
melib/src/backends/imap/connection.rs

@ -30,7 +30,7 @@ use std::iter::FromIterator;
use std::net::SocketAddr;
use super::protocol_parser;
use super::Capabilities;
use super::{Capabilities, ImapServerConf};
#[derive(Debug)]
pub struct ImapStream {
@ -41,10 +41,7 @@ pub struct ImapStream {
#[derive(Debug)]
pub struct ImapConnection {
pub stream: Result<ImapStream>,
server_hostname: String,
server_username: String,
server_password: String,
danger_accept_invalid_certs: bool,
pub server_conf: ImapServerConf,
pub capabilities: Capabilities,
}
@ -145,23 +142,18 @@ impl ImapStream {
Ok(())
}
pub fn new_connection(
server_hostname: &str,
server_username: &str,
server_password: &str,
danger_accept_invalid_certs: bool,
) -> Result<(Capabilities, ImapStream)> {
pub fn new_connection(server_conf: &ImapServerConf) -> Result<(Capabilities, ImapStream)> {
use std::io::prelude::*;
use std::net::TcpStream;
let path = &server_hostname;
let path = &server_conf.server_hostname;
let mut connector = TlsConnector::builder();
if danger_accept_invalid_certs {
if server_conf.danger_accept_invalid_certs {
connector.danger_accept_invalid_certs(true);
}
let connector = connector.build()?;
let addr = if let Ok(a) = lookup_ipv4(path, 143) {
let addr = if let Ok(a) = lookup_ipv4(path, server_conf.server_port) {
a
} else {
return Err(MeliError::new(format!(
@ -172,33 +164,35 @@ impl ImapStream {
let mut socket = TcpStream::connect(&addr)?;
let cmd_id = 0;
socket.write_all(format!("M{} STARTTLS\r\n", cmd_id).as_bytes())?;
let mut buf = vec![0; 1024];
let mut response = String::with_capacity(1024);
let mut cap_flag = false;
loop {
let len = socket.read(&mut buf)?;
response.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..len]) });
if !cap_flag {
if response.starts_with("* OK [CAPABILITY") {
cap_flag = true;
if let Some(pos) = response.as_bytes().find(b"\r\n") {
response.drain(0..pos + 2);
if server_conf.use_starttls {
socket.write_all(format!("M{} STARTTLS\r\n", cmd_id).as_bytes())?;
let mut buf = vec![0; 1024];
let mut response = String::with_capacity(1024);
let mut cap_flag = false;
loop {
let len = socket.read(&mut buf)?;
response.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..len]) });
if !cap_flag {
if response.starts_with("* OK [CAPABILITY") {
cap_flag = true;
if let Some(pos) = response.as_bytes().find(b"\r\n") {
response.drain(0..pos + 2);
}
} else if response.starts_with("* OK ") && response.find("\r\n").is_some() {
if let Some(pos) = response.as_bytes().find(b"\r\n") {
response.drain(0..pos + 2);
}
}
} else if response.starts_with("* OK ") && response.find("\r\n").is_some() {
} else if response.starts_with("* OK [CAPABILITY") {
if let Some(pos) = response.as_bytes().find(b"\r\n") {
response.drain(0..pos + 2);
}
}
} else if response.starts_with("* OK [CAPABILITY") {
if let Some(pos) = response.as_bytes().find(b"\r\n") {
response.drain(0..pos + 2);
if cap_flag && response == "M0 OK Begin TLS negotiation now.\r\n" {
break;
}
}
if cap_flag && response == "M0 OK Begin TLS negotiation now.\r\n" {
break;
}
}
socket
@ -228,7 +222,11 @@ impl ImapStream {
};
let mut ret = ImapStream { cmd_id, stream };
ret.send_command(
format!("LOGIN \"{}\" \"{}\"", &server_username, &server_password).as_bytes(),
format!(
"LOGIN \"{}\" \"{}\"",
&server_conf.server_username, &server_conf.server_password
)
.as_bytes(),
)?;
let mut res = String::with_capacity(8 * 1024);
ret.read_lines(&mut res, String::new())?;
@ -240,18 +238,10 @@ impl ImapStream {
}
impl ImapConnection {
pub fn new_connection(
server_hostname: String,
server_username: String,
server_password: String,
danger_accept_invalid_certs: bool,
) -> ImapConnection {
pub fn new_connection(server_conf: &ImapServerConf) -> ImapConnection {
ImapConnection {
stream: Err(MeliError::new("Offline".to_string())),
server_hostname,
server_username,
server_password,
danger_accept_invalid_certs,
server_conf: server_conf.clone(),
capabilities: Capabilities::default(),
}
}
@ -260,12 +250,7 @@ impl ImapConnection {
if let Ok(ref mut stream) = self.stream {
return stream.read_response(ret);
}
let (capabilities, mut stream) = ImapStream::new_connection(
&self.server_hostname,
&self.server_username,
&self.server_password,
self.danger_accept_invalid_certs,
)?;
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
let ret = stream.read_response(ret);
if ret.is_ok() {
self.stream = Ok(stream);
@ -278,12 +263,7 @@ impl ImapConnection {
if let Ok(ref mut stream) = self.stream {
return stream.read_lines(ret, termination_string);
}
let (capabilities, mut stream) = ImapStream::new_connection(
&self.server_hostname,
&self.server_username,
&self.server_password,
self.danger_accept_invalid_certs,
)?;
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
let ret = stream.read_lines(ret, termination_string);
if ret.is_ok() {
self.stream = Ok(stream);
@ -296,12 +276,7 @@ impl ImapConnection {
if let Ok(ref mut stream) = self.stream {
return stream.wait_for_continuation_request();
}
let (capabilities, mut stream) = ImapStream::new_connection(
&self.server_hostname,
&self.server_username,
&self.server_password,
self.danger_accept_invalid_certs,
)?;
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
let ret = stream.wait_for_continuation_request();
if ret.is_ok() {
self.stream = Ok(stream);
@ -314,12 +289,7 @@ impl ImapConnection {
if let Ok(ref mut stream) = self.stream {
return stream.send_command(command);
}
let (capabilities, mut stream) = ImapStream::new_connection(
&self.server_hostname,
&self.server_username,
&self.server_password,
self.danger_accept_invalid_certs,
)?;
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
let ret = stream.send_command(command);
if ret.is_ok() {
self.stream = Ok(stream);
@ -332,12 +302,7 @@ impl ImapConnection {
if let Ok(ref mut stream) = self.stream {
return stream.send_literal(data);
}
let (capabilities, mut stream) = ImapStream::new_connection(
&self.server_hostname,
&self.server_username,
&self.server_password,
self.danger_accept_invalid_certs,
)?;
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
let ret = stream.send_literal(data);
if ret.is_ok() {
self.stream = Ok(stream);
@ -350,12 +315,7 @@ impl ImapConnection {
if let Ok(ref mut stream) = self.stream {
return stream.send_raw(raw);
}
let (capabilities, mut stream) = ImapStream::new_connection(
&self.server_hostname,
&self.server_username,
&self.server_password,
self.danger_accept_invalid_certs,
)?;
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
let ret = stream.send_raw(raw);
if ret.is_ok() {
@ -369,12 +329,7 @@ impl ImapConnection {
if let Ok(ref mut stream) = self.stream {
return stream.set_nonblocking(val);
}
let (capabilities, mut stream) = ImapStream::new_connection(
&self.server_hostname,
&self.server_username,
&self.server_password,
self.danger_accept_invalid_certs,
)?;
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
let ret = stream.set_nonblocking(val);
if ret.is_ok() {
self.stream = Ok(stream);
@ -423,12 +378,7 @@ impl Iterator for ImapBlockingConnection {
} = self;
loop {
if conn.stream.is_err() {
if let Ok((_, stream)) = ImapStream::new_connection(
&conn.server_hostname,
&conn.server_username,
&conn.server_password,
conn.danger_accept_invalid_certs,
) {
if let Ok((_, stream)) = ImapStream::new_connection(&conn.server_conf) {
conn.stream = Ok(stream);
} else {
debug!(&conn.stream);

Loading…
Cancel
Save