imap: add support for imaps connections
Take port value and a `use_starttls` flag from the configuration file.embed
parent
9563007069
commit
ee82ae175a
|
@ -55,15 +55,22 @@ pub struct EnvelopeCache {
|
||||||
flags: Option<Flag>,
|
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>>;
|
type Capabilities = FnvHashSet<Vec<u8>>;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ImapType {
|
pub struct ImapType {
|
||||||
account_name: String,
|
account_name: String,
|
||||||
server_hostname: String,
|
|
||||||
server_username: String,
|
|
||||||
server_password: String,
|
|
||||||
danger_accept_invalid_certs: bool,
|
|
||||||
connection: Arc<Mutex<ImapConnection>>,
|
connection: Arc<Mutex<ImapConnection>>,
|
||||||
|
server_conf: ImapServerConf,
|
||||||
|
|
||||||
folders: FnvHashMap<FolderHash, ImapFolder>,
|
folders: FnvHashMap<FolderHash, ImapFolder>,
|
||||||
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
|
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
|
||||||
|
@ -180,12 +187,7 @@ impl MailBackend for ImapType {
|
||||||
.capabilities
|
.capabilities
|
||||||
.contains(&b"IDLE"[0..]);
|
.contains(&b"IDLE"[0..]);
|
||||||
let folders = self.folders.clone();
|
let folders = self.folders.clone();
|
||||||
let conn = ImapConnection::new_connection(
|
let conn = ImapConnection::new_connection(&self.server_conf);
|
||||||
self.server_hostname.clone(),
|
|
||||||
self.server_username.clone(),
|
|
||||||
self.server_password.clone(),
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
);
|
|
||||||
let main_conn = self.connection.clone();
|
let main_conn = self.connection.clone();
|
||||||
let hash_index = self.hash_index.clone();
|
let hash_index = self.hash_index.clone();
|
||||||
let uid_index = self.uid_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_hostname = get_conf_val!(s["server_hostname"]);
|
||||||
let server_username = get_conf_val!(s["server_username"]);
|
let server_username = get_conf_val!(s["server_username"]);
|
||||||
let server_password = get_conf_val!(s["server_password"]);
|
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 =
|
let danger_accept_invalid_certs: bool =
|
||||||
get_conf_val!(s["danger_accept_invalid_certs"], false);
|
get_conf_val!(s["danger_accept_invalid_certs"], false);
|
||||||
let connection = ImapConnection::new_connection(
|
let server_conf = ImapServerConf {
|
||||||
server_hostname.to_string(),
|
server_hostname: server_hostname.to_string(),
|
||||||
server_username.to_string(),
|
server_username: server_username.to_string(),
|
||||||
server_password.to_string(),
|
server_password: server_password.to_string(),
|
||||||
|
server_port,
|
||||||
|
use_starttls,
|
||||||
danger_accept_invalid_certs,
|
danger_accept_invalid_certs,
|
||||||
);
|
};
|
||||||
|
let connection = ImapConnection::new_connection(&server_conf);
|
||||||
|
|
||||||
let mut m = ImapType {
|
let mut m = ImapType {
|
||||||
account_name: s.name().to_string(),
|
account_name: s.name().to_string(),
|
||||||
server_hostname: get_conf_val!(s["server_hostname"]).to_string(),
|
server_conf,
|
||||||
server_username: get_conf_val!(s["server_username"]).to_string(),
|
|
||||||
server_password: get_conf_val!(s["server_password"]).to_string(),
|
|
||||||
folders: Default::default(),
|
folders: Default::default(),
|
||||||
connection: Arc::new(Mutex::new(connection)),
|
connection: Arc::new(Mutex::new(connection)),
|
||||||
danger_accept_invalid_certs,
|
|
||||||
hash_index: Default::default(),
|
hash_index: Default::default(),
|
||||||
uid_index: Default::default(),
|
uid_index: Default::default(),
|
||||||
byte_cache: Default::default(),
|
byte_cache: Default::default(),
|
||||||
|
@ -430,12 +441,7 @@ impl ImapType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shell(&mut self) {
|
pub fn shell(&mut self) {
|
||||||
let mut conn = ImapConnection::new_connection(
|
let mut conn = ImapConnection::new_connection(&self.server_conf);
|
||||||
self.server_hostname.clone(),
|
|
||||||
self.server_username.clone(),
|
|
||||||
self.server_password.clone(),
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
);
|
|
||||||
let mut res = String::with_capacity(8 * 1024);
|
let mut res = String::with_capacity(8 * 1024);
|
||||||
conn.read_response(&mut res).unwrap();
|
conn.read_response(&mut res).unwrap();
|
||||||
debug!("out: {}", &res);
|
debug!("out: {}", &res);
|
||||||
|
|
|
@ -30,7 +30,7 @@ use std::iter::FromIterator;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use super::protocol_parser;
|
use super::protocol_parser;
|
||||||
use super::Capabilities;
|
use super::{Capabilities, ImapServerConf};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ImapStream {
|
pub struct ImapStream {
|
||||||
|
@ -41,10 +41,7 @@ pub struct ImapStream {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ImapConnection {
|
pub struct ImapConnection {
|
||||||
pub stream: Result<ImapStream>,
|
pub stream: Result<ImapStream>,
|
||||||
server_hostname: String,
|
pub server_conf: ImapServerConf,
|
||||||
server_username: String,
|
|
||||||
server_password: String,
|
|
||||||
danger_accept_invalid_certs: bool,
|
|
||||||
pub capabilities: Capabilities,
|
pub capabilities: Capabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,23 +142,18 @@ impl ImapStream {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_connection(
|
pub fn new_connection(server_conf: &ImapServerConf) -> Result<(Capabilities, ImapStream)> {
|
||||||
server_hostname: &str,
|
|
||||||
server_username: &str,
|
|
||||||
server_password: &str,
|
|
||||||
danger_accept_invalid_certs: bool,
|
|
||||||
) -> Result<(Capabilities, ImapStream)> {
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
let path = &server_hostname;
|
let path = &server_conf.server_hostname;
|
||||||
|
|
||||||
let mut connector = TlsConnector::builder();
|
let mut connector = TlsConnector::builder();
|
||||||
if danger_accept_invalid_certs {
|
if server_conf.danger_accept_invalid_certs {
|
||||||
connector.danger_accept_invalid_certs(true);
|
connector.danger_accept_invalid_certs(true);
|
||||||
}
|
}
|
||||||
let connector = connector.build()?;
|
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
|
a
|
||||||
} else {
|
} else {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
|
@ -172,33 +164,35 @@ impl ImapStream {
|
||||||
|
|
||||||
let mut socket = TcpStream::connect(&addr)?;
|
let mut socket = TcpStream::connect(&addr)?;
|
||||||
let cmd_id = 0;
|
let cmd_id = 0;
|
||||||
socket.write_all(format!("M{} STARTTLS\r\n", cmd_id).as_bytes())?;
|
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 buf = vec![0; 1024];
|
||||||
let mut response = String::with_capacity(1024);
|
let mut response = String::with_capacity(1024);
|
||||||
let mut cap_flag = false;
|
let mut cap_flag = false;
|
||||||
loop {
|
loop {
|
||||||
let len = socket.read(&mut buf)?;
|
let len = socket.read(&mut buf)?;
|
||||||
response.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..len]) });
|
response.push_str(unsafe { std::str::from_utf8_unchecked(&buf[0..len]) });
|
||||||
if !cap_flag {
|
if !cap_flag {
|
||||||
if response.starts_with("* OK [CAPABILITY") {
|
if response.starts_with("* OK [CAPABILITY") {
|
||||||
cap_flag = true;
|
cap_flag = true;
|
||||||
if let Some(pos) = response.as_bytes().find(b"\r\n") {
|
if let Some(pos) = response.as_bytes().find(b"\r\n") {
|
||||||
response.drain(0..pos + 2);
|
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") {
|
if let Some(pos) = response.as_bytes().find(b"\r\n") {
|
||||||
response.drain(0..pos + 2);
|
response.drain(0..pos + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if response.starts_with("* OK [CAPABILITY") {
|
if cap_flag && response == "M0 OK Begin TLS negotiation now.\r\n" {
|
||||||
if let Some(pos) = response.as_bytes().find(b"\r\n") {
|
break;
|
||||||
response.drain(0..pos + 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cap_flag && response == "M0 OK Begin TLS negotiation now.\r\n" {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|
@ -228,7 +222,11 @@ impl ImapStream {
|
||||||
};
|
};
|
||||||
let mut ret = ImapStream { cmd_id, stream };
|
let mut ret = ImapStream { cmd_id, stream };
|
||||||
ret.send_command(
|
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);
|
let mut res = String::with_capacity(8 * 1024);
|
||||||
ret.read_lines(&mut res, String::new())?;
|
ret.read_lines(&mut res, String::new())?;
|
||||||
|
@ -240,18 +238,10 @@ impl ImapStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImapConnection {
|
impl ImapConnection {
|
||||||
pub fn new_connection(
|
pub fn new_connection(server_conf: &ImapServerConf) -> ImapConnection {
|
||||||
server_hostname: String,
|
|
||||||
server_username: String,
|
|
||||||
server_password: String,
|
|
||||||
danger_accept_invalid_certs: bool,
|
|
||||||
) -> ImapConnection {
|
|
||||||
ImapConnection {
|
ImapConnection {
|
||||||
stream: Err(MeliError::new("Offline".to_string())),
|
stream: Err(MeliError::new("Offline".to_string())),
|
||||||
server_hostname,
|
server_conf: server_conf.clone(),
|
||||||
server_username,
|
|
||||||
server_password,
|
|
||||||
danger_accept_invalid_certs,
|
|
||||||
capabilities: Capabilities::default(),
|
capabilities: Capabilities::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,12 +250,7 @@ impl ImapConnection {
|
||||||
if let Ok(ref mut stream) = self.stream {
|
if let Ok(ref mut stream) = self.stream {
|
||||||
return stream.read_response(ret);
|
return stream.read_response(ret);
|
||||||
}
|
}
|
||||||
let (capabilities, mut stream) = ImapStream::new_connection(
|
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
|
||||||
&self.server_hostname,
|
|
||||||
&self.server_username,
|
|
||||||
&self.server_password,
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
)?;
|
|
||||||
let ret = stream.read_response(ret);
|
let ret = stream.read_response(ret);
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
self.stream = Ok(stream);
|
self.stream = Ok(stream);
|
||||||
|
@ -278,12 +263,7 @@ impl ImapConnection {
|
||||||
if let Ok(ref mut stream) = self.stream {
|
if let Ok(ref mut stream) = self.stream {
|
||||||
return stream.read_lines(ret, termination_string);
|
return stream.read_lines(ret, termination_string);
|
||||||
}
|
}
|
||||||
let (capabilities, mut stream) = ImapStream::new_connection(
|
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
|
||||||
&self.server_hostname,
|
|
||||||
&self.server_username,
|
|
||||||
&self.server_password,
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
)?;
|
|
||||||
let ret = stream.read_lines(ret, termination_string);
|
let ret = stream.read_lines(ret, termination_string);
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
self.stream = Ok(stream);
|
self.stream = Ok(stream);
|
||||||
|
@ -296,12 +276,7 @@ impl ImapConnection {
|
||||||
if let Ok(ref mut stream) = self.stream {
|
if let Ok(ref mut stream) = self.stream {
|
||||||
return stream.wait_for_continuation_request();
|
return stream.wait_for_continuation_request();
|
||||||
}
|
}
|
||||||
let (capabilities, mut stream) = ImapStream::new_connection(
|
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
|
||||||
&self.server_hostname,
|
|
||||||
&self.server_username,
|
|
||||||
&self.server_password,
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
)?;
|
|
||||||
let ret = stream.wait_for_continuation_request();
|
let ret = stream.wait_for_continuation_request();
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
self.stream = Ok(stream);
|
self.stream = Ok(stream);
|
||||||
|
@ -314,12 +289,7 @@ impl ImapConnection {
|
||||||
if let Ok(ref mut stream) = self.stream {
|
if let Ok(ref mut stream) = self.stream {
|
||||||
return stream.send_command(command);
|
return stream.send_command(command);
|
||||||
}
|
}
|
||||||
let (capabilities, mut stream) = ImapStream::new_connection(
|
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
|
||||||
&self.server_hostname,
|
|
||||||
&self.server_username,
|
|
||||||
&self.server_password,
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
)?;
|
|
||||||
let ret = stream.send_command(command);
|
let ret = stream.send_command(command);
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
self.stream = Ok(stream);
|
self.stream = Ok(stream);
|
||||||
|
@ -332,12 +302,7 @@ impl ImapConnection {
|
||||||
if let Ok(ref mut stream) = self.stream {
|
if let Ok(ref mut stream) = self.stream {
|
||||||
return stream.send_literal(data);
|
return stream.send_literal(data);
|
||||||
}
|
}
|
||||||
let (capabilities, mut stream) = ImapStream::new_connection(
|
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
|
||||||
&self.server_hostname,
|
|
||||||
&self.server_username,
|
|
||||||
&self.server_password,
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
)?;
|
|
||||||
let ret = stream.send_literal(data);
|
let ret = stream.send_literal(data);
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
self.stream = Ok(stream);
|
self.stream = Ok(stream);
|
||||||
|
@ -350,12 +315,7 @@ impl ImapConnection {
|
||||||
if let Ok(ref mut stream) = self.stream {
|
if let Ok(ref mut stream) = self.stream {
|
||||||
return stream.send_raw(raw);
|
return stream.send_raw(raw);
|
||||||
}
|
}
|
||||||
let (capabilities, mut stream) = ImapStream::new_connection(
|
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
|
||||||
&self.server_hostname,
|
|
||||||
&self.server_username,
|
|
||||||
&self.server_password,
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let ret = stream.send_raw(raw);
|
let ret = stream.send_raw(raw);
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
|
@ -369,12 +329,7 @@ impl ImapConnection {
|
||||||
if let Ok(ref mut stream) = self.stream {
|
if let Ok(ref mut stream) = self.stream {
|
||||||
return stream.set_nonblocking(val);
|
return stream.set_nonblocking(val);
|
||||||
}
|
}
|
||||||
let (capabilities, mut stream) = ImapStream::new_connection(
|
let (capabilities, mut stream) = ImapStream::new_connection(&self.server_conf)?;
|
||||||
&self.server_hostname,
|
|
||||||
&self.server_username,
|
|
||||||
&self.server_password,
|
|
||||||
self.danger_accept_invalid_certs,
|
|
||||||
)?;
|
|
||||||
let ret = stream.set_nonblocking(val);
|
let ret = stream.set_nonblocking(val);
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
self.stream = Ok(stream);
|
self.stream = Ok(stream);
|
||||||
|
@ -423,12 +378,7 @@ impl Iterator for ImapBlockingConnection {
|
||||||
} = self;
|
} = self;
|
||||||
loop {
|
loop {
|
||||||
if conn.stream.is_err() {
|
if conn.stream.is_err() {
|
||||||
if let Ok((_, stream)) = ImapStream::new_connection(
|
if let Ok((_, stream)) = ImapStream::new_connection(&conn.server_conf) {
|
||||||
&conn.server_hostname,
|
|
||||||
&conn.server_username,
|
|
||||||
&conn.server_password,
|
|
||||||
conn.danger_accept_invalid_certs,
|
|
||||||
) {
|
|
||||||
conn.stream = Ok(stream);
|
conn.stream = Ok(stream);
|
||||||
} else {
|
} else {
|
||||||
debug!(&conn.stream);
|
debug!(&conn.stream);
|
||||||
|
|
Loading…
Reference in New Issue