Browse Source
imap: add managesieve connection
imap: add managesieve connection
So far only the connection is implemented, and using the testing/manage_sieve binary you can get a shell to a managesieve server. The managesieve interface will be used in the UI from a plugin, but the plugin's interface isn't implemented yet.async
6 changed files with 301 additions and 24 deletions
-
4melib/src/backends/imap.rs
-
116melib/src/backends/imap/connection.rs
-
130melib/src/backends/imap/managesieve.rs
-
2melib/src/backends/imap/protocol_parser.rs
-
4testing/Cargo.toml
-
69testing/src/managesieve.rs
@ -0,0 +1,130 @@ |
|||
/*
|
|||
* meli - managesieve
|
|||
*
|
|||
* 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 <http://www.gnu.org/licenses/>.
|
|||
*/
|
|||
|
|||
use super::{ImapConnection, ImapProtocol, ImapServerConf};
|
|||
use crate::conf::AccountSettings;
|
|||
use crate::error::{MeliError, Result};
|
|||
use crate::get_conf_val;
|
|||
use nom::IResult;
|
|||
use std::str::FromStr;
|
|||
use std::sync::{Arc, Mutex};
|
|||
use std::time::Instant;
|
|||
|
|||
named!(
|
|||
pub managesieve_capabilities<Vec<(&[u8], &[u8])>>,
|
|||
do_parse!(
|
|||
ret: separated_nonempty_list_complete!(tag!(b"\r\n"), alt_complete!(separated_pair!(quoted_raw, tag!(b" "), quoted_raw) | map!(quoted_raw, |q| (q, &b""[..]))))
|
|||
>> opt!(tag!("\r\n"))
|
|||
>> ({ ret })
|
|||
)
|
|||
);
|
|||
|
|||
#[test]
|
|||
fn test_managesieve_capabilities() {
|
|||
assert_eq!(managesieve_capabilities(b"\"IMPLEMENTATION\" \"Dovecot Pigeonhole\"\r\n\"SIEVE\" \"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext\"\r\n\"NOTIFY\" \"mailto\"\r\n\"SASL\" \"PLAIN\"\r\n\"STARTTLS\"\r\n\"VERSION\" \"1.0\"\r\n").to_full_result(), Ok(vec![
|
|||
(&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]),
|
|||
(&b"SIEVE"[..],&b"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"[..]),
|
|||
(&b"NOTIFY"[..],&b"mailto"[..]),
|
|||
(&b"SASL"[..],&b"PLAIN"[..]),
|
|||
(&b"STARTTLS"[..], &b""[..]),
|
|||
(&b"VERSION"[..],&b"1.0"[..])])
|
|||
|
|||
);
|
|||
}
|
|||
|
|||
// Return a byte sequence surrounded by "s and decoded if necessary
|
|||
pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
|||
if input.is_empty() || input[0] != b'"' {
|
|||
return IResult::Error(nom::ErrorKind::Custom(0));
|
|||
}
|
|||
|
|||
let mut i = 1;
|
|||
while i < input.len() {
|
|||
if input[i] == b'\"' && input[i - 1] != b'\\' {
|
|||
return IResult::Done(&input[i + 1..], &input[1..i]);
|
|||
}
|
|||
i += 1;
|
|||
}
|
|||
|
|||
return IResult::Error(nom::ErrorKind::Custom(0));
|
|||
}
|
|||
|
|||
pub trait ManageSieve {
|
|||
fn havespace(&mut self) -> Result<()>;
|
|||
fn putscript(&mut self) -> Result<()>;
|
|||
|
|||
fn listscripts(&mut self) -> Result<()>;
|
|||
fn setactive(&mut self) -> Result<()>;
|
|||
|
|||
fn getscript(&mut self) -> Result<()>;
|
|||
|
|||
fn deletescript(&mut self) -> Result<()>;
|
|||
fn renamescript(&mut self) -> Result<()>;
|
|||
}
|
|||
|
|||
pub fn new_managesieve_connection(s: &AccountSettings) -> Result<ImapConnection> {
|
|||
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"], 4190)?;
|
|||
let danger_accept_invalid_certs: bool = get_conf_val!(s["danger_accept_invalid_certs"], false)?;
|
|||
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: true,
|
|||
danger_accept_invalid_certs,
|
|||
protocol: ImapProtocol::ManageSieve,
|
|||
};
|
|||
let online = Arc::new(Mutex::new((
|
|||
Instant::now(),
|
|||
Err(MeliError::new("Account is uninitialised.")),
|
|||
)));
|
|||
Ok(ImapConnection::new_connection(&server_conf, online))
|
|||
}
|
|||
|
|||
impl ManageSieve for ImapConnection {
|
|||
fn havespace(&mut self) -> Result<()> {
|
|||
Ok(())
|
|||
}
|
|||
fn putscript(&mut self) -> Result<()> {
|
|||
Ok(())
|
|||
}
|
|||
|
|||
fn listscripts(&mut self) -> Result<()> {
|
|||
Ok(())
|
|||
}
|
|||
fn setactive(&mut self) -> Result<()> {
|
|||
Ok(())
|
|||
}
|
|||
|
|||
fn getscript(&mut self) -> Result<()> {
|
|||
Ok(())
|
|||
}
|
|||
|
|||
fn deletescript(&mut self) -> Result<()> {
|
|||
Ok(())
|
|||
}
|
|||
fn renamescript(&mut self) -> Result<()> {
|
|||
Ok(())
|
|||
}
|
|||
}
|
@ -0,0 +1,69 @@ |
|||
extern crate melib;
|
|||
|
|||
use melib::backends::imap::managesieve::new_managesieve_connection;
|
|||
use melib::AccountSettings;
|
|||
use melib::Result;
|
|||
|
|||
/// Opens an interactive shell on a managesieve server. Suggested use is with rlwrap(1)
|
|||
///
|
|||
/// # Example invocation:
|
|||
/// ```sh
|
|||
/// ./manage_sieve server_hostname server_username server_password server_port");
|
|||
/// ```
|
|||
///
|
|||
/// `danger_accept_invalid_certs` is turned on by default, so no certificate validation is performed.
|
|||
|
|||
fn main() -> Result<()> {
|
|||
let mut args = std::env::args().skip(1).collect::<Vec<String>>();
|
|||
if args.len() != 4 {
|
|||
eprintln!(
|
|||
"Usage: manage_sieve server_hostname server_username server_password server_port"
|
|||
);
|
|||
std::process::exit(1);
|
|||
}
|
|||
|
|||
let (a, b, c, d) = (
|
|||
std::mem::replace(&mut args[0], String::new()),
|
|||
std::mem::replace(&mut args[1], String::new()),
|
|||
std::mem::replace(&mut args[2], String::new()),
|
|||
std::mem::replace(&mut args[3], String::new()),
|
|||
);
|
|||
let set = AccountSettings {
|
|||
extra: [
|
|||
("server_hostname".to_string(), a),
|
|||
("server_username".to_string(), b),
|
|||
("server_password".to_string(), c),
|
|||
("server_port".to_string(), d),
|
|||
(
|
|||
"danger_accept_invalid_certs".to_string(),
|
|||
"true".to_string(),
|
|||
),
|
|||
]
|
|||
.iter()
|
|||
.cloned()
|
|||
.collect(),
|
|||
..Default::default()
|
|||
};
|
|||
let mut conn = new_managesieve_connection(&set)?;
|
|||
conn.connect()?;
|
|||
let mut res = String::with_capacity(8 * 1024);
|
|||
|
|||
let mut input = String::new();
|
|||
loop {
|
|||
use std::io;
|
|||
input.clear();
|
|||
match io::stdin().read_line(&mut input) {
|
|||
Ok(_) => {
|
|||
if input.trim().eq_ignore_ascii_case("logout") {
|
|||
break;
|
|||
}
|
|||
conn.send_command(input.as_bytes()).unwrap();
|
|||
conn.read_lines(&mut res, String::new()).unwrap();
|
|||
println!("out: {}", &res);
|
|||
}
|
|||
Err(error) => println!("error: {}", error),
|
|||
}
|
|||
}
|
|||
|
|||
Ok(())
|
|||
}
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue