gtk somewhat working
This commit is contained in:
@@ -0,0 +1 @@
|
||||
pub mod run;
|
||||
@@ -0,0 +1,38 @@
|
||||
use log::{info, trace};
|
||||
use sieverman::ConnectionInfo;
|
||||
|
||||
use crate::protocol::BackToFront;
|
||||
|
||||
pub(crate) async fn run(
|
||||
to_frontend_tx: tokio::sync::mpsc::Sender<BackToFront>,
|
||||
) -> anyhow::Result<()> {
|
||||
trace!("Starting up sieverman…");
|
||||
let info = ConnectionInfo::new("teilgedanken.de", 4190);
|
||||
let mut connected = info.connect().await.unwrap();
|
||||
info!("connected to the server");
|
||||
loop {
|
||||
match connected.read_introduction().await {
|
||||
Ok(sieverman::IsComplete::Yes(c)) => {
|
||||
connected = c;
|
||||
break;
|
||||
}
|
||||
Ok(sieverman::IsComplete::No(c)) => {
|
||||
trace!("Incomplete data waiting for some time…");
|
||||
connected = c
|
||||
}
|
||||
Err(e) => return Err(anyhow::anyhow!("something went wrong: {}", e)),
|
||||
};
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
connected.log_buffer().await;
|
||||
}
|
||||
trace!("Fully read the introduction:");
|
||||
connected.log_buffer().await;
|
||||
connected.log_server_settings().await;
|
||||
to_frontend_tx
|
||||
.send(BackToFront::ServerConnected(
|
||||
connected.get_greeting().unwrap(),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use gtk4::{
|
||||
glib,
|
||||
prelude::{ApplicationExt, GtkWindowExt, WidgetExt},
|
||||
Application, ApplicationWindow, Builder, Label,
|
||||
};
|
||||
use log::trace;
|
||||
|
||||
use crate::protocol::BackToFront;
|
||||
|
||||
pub(crate) fn get_app(
|
||||
from_backend_rx: Rc<RefCell<Option<tokio::sync::mpsc::Receiver<BackToFront>>>>,
|
||||
) -> Application {
|
||||
let app = Application::builder()
|
||||
.application_id("de.teilgedanken.sieverman")
|
||||
.build();
|
||||
|
||||
app.connect_activate(move |app| build_ui(app, from_backend_rx.clone()));
|
||||
|
||||
app
|
||||
}
|
||||
|
||||
fn build_ui(
|
||||
app: >k4::Application,
|
||||
from_backend_rx: Rc<RefCell<Option<tokio::sync::mpsc::Receiver<BackToFront>>>>,
|
||||
) {
|
||||
let glade_src = include_str!("../../../gui/sieverman.ui");
|
||||
let builder = Builder::from_string(glade_src);
|
||||
let main_window: ApplicationWindow = builder.object("sieverman").expect("get main-window");
|
||||
main_window.set_height_request(400);
|
||||
main_window.set_application(Some(app));
|
||||
main_window.present();
|
||||
|
||||
let future = {
|
||||
let mut data_event_receiver = from_backend_rx.take().expect("data_event_reciver");
|
||||
async move {
|
||||
while let Some(event) = data_event_receiver.recv().await {
|
||||
trace!("data event: {:?}", event);
|
||||
match event {
|
||||
BackToFront::ServerConnected(message) => {
|
||||
trace!("Received {}", message);
|
||||
let status_label: Label = builder.object("serverstatus").unwrap();
|
||||
status_label.set_text(&message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let c = glib::MainContext::default();
|
||||
c.spawn_local(future);
|
||||
main_window.present();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod main_window;
|
||||
+5
-84
@@ -1,98 +1,17 @@
|
||||
use log::info;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::streaming::is_not;
|
||||
use nom::bytes::complete::is_not;
|
||||
use nom::character::is_space;
|
||||
use nom::character::streaming::{
|
||||
alpha1, digit1, line_ending, multispace0, multispace1, newline, none_of, not_line_ending,
|
||||
};
|
||||
use nom::multi::many_m_n;
|
||||
use nom::sequence::{delimited, preceded, separated_pair};
|
||||
use nom::{bytes::streaming::tag, IResult};
|
||||
use nom::{bytes::complete::tag, IResult};
|
||||
use nom::{character, number};
|
||||
use sieverman::parser::parse_server_config;
|
||||
use sieverman::{Methods, ServerSettings};
|
||||
use std::error::Error;
|
||||
pub fn do_nothing_parser(input: &str) -> IResult<&str, &str> {
|
||||
Ok((input, ""))
|
||||
}
|
||||
|
||||
fn quoted(input: &str) -> IResult<&str, &str> {
|
||||
preceded(multispace0, delimited(tag("\""), is_not("\n\""), tag("\"")))(input)
|
||||
}
|
||||
|
||||
fn key_value(input: &str) -> IResult<&str, (&str, &str)> {
|
||||
// note that this is really creating a function, the parser for abc
|
||||
// vvvvv
|
||||
// which is then called here, returning an IResult<&str, &str>
|
||||
// vvvvv
|
||||
preceded(multispace0, separated_pair(quoted, multispace1, quoted))(input)
|
||||
}
|
||||
|
||||
fn parse_version(input: &str) -> IResult<&str, Vec<i32>> {
|
||||
many_m_n(
|
||||
1,
|
||||
5,
|
||||
alt((
|
||||
character::complete::i32,
|
||||
preceded(character::complete::char('.'), character::complete::i32),
|
||||
)),
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_methods(input: &str) -> IResult<&str, Vec<Methods>> {
|
||||
let (rem, res) = many_m_n(1, 10, alt((alpha1, preceded(multispace1, alpha1))))(input)?;
|
||||
let methods = res
|
||||
.into_iter()
|
||||
.map(|m| match m {
|
||||
"PLAIN" => Methods::Plain,
|
||||
"OAUTHBEARER" => Methods::OAuthbearer,
|
||||
any => todo!("That method is not yet implemented: {}", any),
|
||||
})
|
||||
.collect();
|
||||
Ok((rem, methods))
|
||||
}
|
||||
|
||||
fn parse_extensions(input: &str) -> IResult<&str, Vec<String>> {
|
||||
let (rem, res) = many_m_n(1, 200, alt((is_not(" "), preceded(tag(" "), is_not(" ")))))(input)?;
|
||||
let extensions = res.into_iter().map(|m| m.to_string()).collect();
|
||||
Ok((rem, extensions))
|
||||
}
|
||||
|
||||
fn parse_server_config(input: &str) -> IResult<&str, ServerSettings> {
|
||||
let (remain, (key, implementation)) = key_value(input)?;
|
||||
info!("Implementation:# {}", implementation);
|
||||
let (remain, (key, version)) = key_value(remain)?;
|
||||
info!("{}", version);
|
||||
let (_, version) = parse_version(version)?;
|
||||
info!("Version:# {:?}", version);
|
||||
let (remain, (key, sasl)) = key_value(remain)?;
|
||||
info!("{}", sasl);
|
||||
let (_, sasl) = parse_methods(sasl)?;
|
||||
info!("SASL:# {:?}", sasl);
|
||||
let (remain, (key, capabilities)) = key_value(remain)?;
|
||||
info!("{}", capabilities);
|
||||
let (_, capabilities) = parse_extensions(capabilities)?;
|
||||
info!("Extensions:# {:?}", &capabilities);
|
||||
let (remain, (key, notify)) = key_value(remain)?;
|
||||
info!("{}", notify);
|
||||
let (_, notify) = parse_extensions(remain)?;
|
||||
info!("Notify:# {:?}", ¬ify);
|
||||
let (remain, (key, max_redirects)) = key_value(remain)?;
|
||||
info!("{}", max_redirects);
|
||||
let (_, max_redirects) = character::complete::u32(max_redirects)?;
|
||||
info!("Max Redirects:# {}", max_redirects);
|
||||
|
||||
IResult::Ok((
|
||||
remain,
|
||||
ServerSettings {
|
||||
implementation: implementation.to_string(),
|
||||
version,
|
||||
sasl,
|
||||
capabilities,
|
||||
notify,
|
||||
max_redirects,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
env_logger::init();
|
||||
@@ -105,5 +24,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
"MAXREDIRECTS" "1"
|
||||
"#;
|
||||
let (remaining_input, output) = parse_server_config(input)?;
|
||||
|
||||
dbg!(output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum BackToFront {
|
||||
ServerConnected(String),
|
||||
}
|
||||
+21
-104
@@ -1,108 +1,25 @@
|
||||
/// This is the simplest possible client using rustls that does something useful:
|
||||
/// it accepts the default configuration, loads some root certs, and then connects
|
||||
/// to google.com and issues a basic HTTP request. The response is printed to stdout.
|
||||
///
|
||||
/// It makes use of rustls::Stream to treat the underlying TLS connection as a basic
|
||||
/// bi-directional stream -- the underlying IO is performed transparently.
|
||||
///
|
||||
/// Note that `unwrap()` is used to deal with networking errors; this is not something
|
||||
/// that is sensible outside of example code.
|
||||
use std::{os::unix::net::SocketAddr, sync::Arc};
|
||||
mod backend;
|
||||
mod gui;
|
||||
pub mod protocol;
|
||||
use std::{cell::RefCell, rc::Rc, thread};
|
||||
|
||||
use log::{info, trace};
|
||||
use std::net::ToSocketAddrs;
|
||||
use tokio::{
|
||||
io::{self, split, AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf},
|
||||
net::TcpStream,
|
||||
};
|
||||
use tokio_rustls::{
|
||||
rustls::{ClientConfig, KeyLogFile, RootCertStore},
|
||||
TlsConnector,
|
||||
};
|
||||
use gtk4::{glib, prelude::ApplicationExtManual as _};
|
||||
|
||||
async fn run() -> io::Result<()> {
|
||||
let mut root_store = RootCertStore::empty();
|
||||
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||
let mut config = ClientConfig::builder()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
// Allow using SSLKEYLOGFILE.
|
||||
config.key_log = Arc::new(KeyLogFile::new());
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
let server_address = ("teilgedanken.de", 4190).to_socket_addrs()?.next().unwrap();
|
||||
let stream = TcpStream::connect(&server_address).await?;
|
||||
let domain = pki_types::ServerName::try_from("teilgedanken.de").unwrap();
|
||||
let stream = connector.connect(domain, stream).await?;
|
||||
let (mut reader, mut writer) = split(stream);
|
||||
let mut dst = String::new();
|
||||
|
||||
reader
|
||||
.read_to_string(&mut dst)
|
||||
.await
|
||||
.expect("Failed to read");
|
||||
info!("{:?}", &dst);
|
||||
//writer.write(src)
|
||||
/* {
|
||||
let mut reader = BufReader::new(stream);
|
||||
let mut read_all = String::new();
|
||||
/* loop {
|
||||
trace!("reading max {} bytes", buffer.len());
|
||||
let bytes_read = rea.read(&mut buffer);
|
||||
if let Ok(bytes_read) = bytes_read {
|
||||
trace!("read {:?} bytes", bytes_read);
|
||||
if bytes_read == buffer.len() {
|
||||
let cur = String::from_utf8_lossy(&buffer[..bytes_read]);
|
||||
read_all += &cur;
|
||||
trace!("Received: \n{}", cur);
|
||||
} else {
|
||||
let cur = String::from_utf8_lossy(&buffer[..bytes_read]);
|
||||
read_all += &cur;
|
||||
trace!("Last: \n{}", cur);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
trace!("Error after reading continue");
|
||||
break;
|
||||
}
|
||||
} */
|
||||
reader.read_to_string(&mut read_all);
|
||||
info!("Read all:\n{}", read_all);
|
||||
}
|
||||
let ciphersuite = stream.conn.negotiated_cipher_suite().unwrap();
|
||||
writeln!(
|
||||
&mut std::io::stderr(),
|
||||
"Current ciphersuite: {:?}",
|
||||
ciphersuite.suite()
|
||||
)
|
||||
.unwrap();
|
||||
/* stream
|
||||
.clone()
|
||||
.write_all(concat!("UNAUTHENTICATE\r\n",).as_bytes())
|
||||
.unwrap();
|
||||
loop {
|
||||
let bytes_read = stream.read(&mut buffer);
|
||||
if let Ok(bytes_read) = bytes_read {
|
||||
if bytes_read > 0 {
|
||||
trace!(
|
||||
"Received: {:?}",
|
||||
String::from_utf8_lossy(&buffer[..bytes_read])
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} */ */
|
||||
Ok(())
|
||||
}
|
||||
fn main() {
|
||||
fn main() -> glib::ExitCode {
|
||||
env_logger::init();
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(run())
|
||||
.expect("Failed to run or interrupted");
|
||||
gtk4::init().expect("Failed to initialize GTK");
|
||||
let (to_frontent_tx, from_backend_rx) = tokio::sync::mpsc::channel(5);
|
||||
let _handle = thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
.enable_time()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(backend::run::run(to_frontent_tx))
|
||||
.expect("Failed to run or interrupted");
|
||||
});
|
||||
|
||||
let app = gui::main_window::get_app(Rc::new(RefCell::new(Some(from_backend_rx))));
|
||||
|
||||
app.run()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/// This is the simplest possible client using rustls that does something useful:
|
||||
/// it accepts the default configuration, loads some root certs, and then connects
|
||||
/// to google.com and issues a basic HTTP request. The response is printed to stdout.
|
||||
///
|
||||
/// It makes use of rustls::Stream to treat the underlying TLS connection as a basic
|
||||
/// bi-directional stream -- the underlying IO is performed transparently.
|
||||
///
|
||||
/// Note that `unwrap()` is used to deal with networking errors; this is not something
|
||||
/// that is sensible outside of example code.
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::{error, info, trace};
|
||||
use managesieve::{Capability, MSResult, Response};
|
||||
use nom::{error::ParseError, IResult};
|
||||
use sieverman::parser::parse_server_config;
|
||||
use std::fmt::Debug;
|
||||
use std::net::ToSocketAddrs;
|
||||
use tokio::{
|
||||
io::{self, split, AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
net::TcpStream,
|
||||
};
|
||||
use tokio_rustls::{
|
||||
rustls::{ClientConfig, KeyLogFile, RootCertStore},
|
||||
TlsConnector,
|
||||
};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! parse_response {
|
||||
($reader:expr, $parser:expr, $dst:expr) => {{
|
||||
let mut buf = String::new();
|
||||
let capabilities = 'read_more: loop {
|
||||
trace!("reading");
|
||||
let read = $reader.read_line(&mut buf).await;
|
||||
if let Ok(num) = read {
|
||||
trace!("Read {} bytes", num);
|
||||
trace!("Read Buffer: {:?}", &read);
|
||||
trace!("Read Buffer: {:?}", &buf);
|
||||
$dst += &buf;
|
||||
buf.clear();
|
||||
};
|
||||
match $parser(&$dst) {
|
||||
read @ Ok((_, _)) => {
|
||||
//trace!("{:?}", &response);
|
||||
break 'read_more read;
|
||||
}
|
||||
Err(_e) => continue,
|
||||
Err(e) => {
|
||||
error!("invalid response");
|
||||
break Err(e);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("invalid input");
|
||||
break Err(e);
|
||||
}
|
||||
};
|
||||
};
|
||||
capabilities
|
||||
}};
|
||||
}
|
||||
|
||||
async fn run() -> io::Result<()> {
|
||||
let mut root_store = RootCertStore::empty();
|
||||
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||
let mut config = ClientConfig::builder()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
// Allow using SSLKEYLOGFILE.
|
||||
config.key_log = Arc::new(KeyLogFile::new());
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
let server_address = ("teilgedanken.de", 4190).to_socket_addrs()?.next().unwrap();
|
||||
let stream = TcpStream::connect(&server_address).await?;
|
||||
let domain = pki_types::ServerName::try_from("teilgedanken.de").unwrap();
|
||||
let stream = connector.connect(domain, stream).await?;
|
||||
let (reader, mut writer) = split(stream);
|
||||
let mut reader = BufReader::new(reader);
|
||||
let mut dst = String::new();
|
||||
let mut buf = String::new();
|
||||
let capabilities: Option<(&str, Vec<Capability>)> = 'read_more: loop {
|
||||
trace!("reading");
|
||||
let read = reader.read_line(&mut buf).await;
|
||||
if let Ok(num) = read {
|
||||
trace!("Read {} bytes", num);
|
||||
trace!("Read Buffer: {:?}", &read);
|
||||
trace!("Read Buffer: {:?}", &buf);
|
||||
dst += &buf;
|
||||
buf.clear();
|
||||
};
|
||||
match managesieve::response_capability(&dst) {
|
||||
Ok((rest, capability, response)) => {
|
||||
trace!("{:?}", response);
|
||||
break 'read_more Some((rest, capability));
|
||||
}
|
||||
Err(managesieve::MSError::IncompleteResponse) => continue,
|
||||
Err(managesieve::MSError::InvalidResponse) => {
|
||||
error!("invalid response");
|
||||
break None;
|
||||
}
|
||||
Err(managesieve::MSError::InvalidInput) => {
|
||||
error!("invalid input");
|
||||
break None;
|
||||
}
|
||||
};
|
||||
};
|
||||
if let Some((rest, capability)) = capabilities {
|
||||
trace!("Parsed: \n{}", dst);
|
||||
dst = rest.to_string();
|
||||
info!("Rest content:\n{:?}", &dst);
|
||||
info!("The server config: {:?}", capability);
|
||||
let have_space_command = managesieve::Command::have_space("loser.test", 249)
|
||||
.expect("failed to create have_space command");
|
||||
writer
|
||||
.write_all(have_space_command.to_string().as_bytes())
|
||||
.await?;
|
||||
let response_parser = managesieve::response_havespace;
|
||||
let result = parse_response!(reader, response_parser, dst);
|
||||
dbg!(result);
|
||||
let auth_command = managesieve::Command::authenticate();
|
||||
writer
|
||||
.write_all(auth_command.to_string().as_bytes())
|
||||
.await?;
|
||||
let response_parser = managesieve::response_authenticate;
|
||||
let result = parse_response!(reader, response_parser, dst);
|
||||
dbg!(result);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_until_complete<'a, Reader, Target>(
|
||||
mut reader: Reader,
|
||||
mut parser: impl FnMut(&str) -> IResult<&'a str, (Target, Response)>,
|
||||
) -> IResult<&'a str, Target>
|
||||
where
|
||||
Reader: AsyncBufReadExt + Unpin,
|
||||
Target: Debug,
|
||||
{
|
||||
let mut dst = String::new();
|
||||
let mut buf = String::new();
|
||||
let capabilities: MSResult<Target> = 'read_more: loop {
|
||||
trace!("reading");
|
||||
let read = reader.read_line(&mut buf).await;
|
||||
if let Ok(num) = read {
|
||||
trace!("Read {} bytes", num);
|
||||
trace!("Read Buffer: {:?}", &read);
|
||||
trace!("Read Buffer: {:?}", &buf);
|
||||
dst += &buf;
|
||||
buf.clear();
|
||||
};
|
||||
match parser(&dst) {
|
||||
read @ Ok((_, _)) => {
|
||||
//trace!("{:?}", &response);
|
||||
break 'read_more read;
|
||||
}
|
||||
Err(_e) => continue,
|
||||
Err(e) => {
|
||||
error!("invalid response");
|
||||
break Err(e);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("invalid input");
|
||||
break Err(e);
|
||||
}
|
||||
};
|
||||
};
|
||||
if let Ok((rest, (target, response))) = capabilities {
|
||||
trace!("Parsed: \n{}", dst);
|
||||
dst = rest.to_string();
|
||||
trace!("Rest content:\n{:?}", &dst);
|
||||
trace!("Response content: {:?}", &response);
|
||||
info!("The server config: {:?}", target);
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(run())
|
||||
.expect("Failed to run or interrupted");
|
||||
}
|
||||
+165
@@ -1,3 +1,168 @@
|
||||
use std::{any, net::SocketAddr, sync::Arc};
|
||||
|
||||
use log::{error, info, trace};
|
||||
use managesieve::Capability;
|
||||
use std::net::ToSocketAddrs;
|
||||
use thiserror::Error;
|
||||
use tokio::{
|
||||
io::{split, AsyncBufReadExt, BufReader, ReadHalf, WriteHalf},
|
||||
net::TcpStream,
|
||||
sync::Mutex,
|
||||
task::JoinHandle,
|
||||
};
|
||||
use tokio_rustls::{
|
||||
client::TlsStream,
|
||||
rustls::{ClientConfig, KeyLogFile, RootCertStore},
|
||||
TlsConnector,
|
||||
};
|
||||
|
||||
pub mod parser;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConnectionInfo {
|
||||
domain: String,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
impl ConnectionInfo {
|
||||
pub fn new(domain: &str, port: u16) -> Self {
|
||||
Self {
|
||||
domain: domain.to_string(),
|
||||
port,
|
||||
}
|
||||
}
|
||||
pub async fn connect(self) -> Result<ConnectionConnected, anyhow::Error> {
|
||||
trace!("Building Cert Store");
|
||||
let mut root_store = RootCertStore::empty();
|
||||
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||
let mut config = ClientConfig::builder()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
// Allow using SSLKEYLOGFILE.
|
||||
config.key_log = Arc::new(KeyLogFile::new());
|
||||
trace!("Creating connector");
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
trace!("Connecting to the server.");
|
||||
let stream = TcpStream::connect((self.domain.clone(), self.port)).await?;
|
||||
let domain = pki_types::ServerName::try_from(self.domain.clone())?.to_owned();
|
||||
trace!("Attach the connector");
|
||||
let stream = connector.connect(domain, stream).await?;
|
||||
let (reader, mut writer) = split(stream);
|
||||
let buffer = Arc::new(Mutex::new(String::new()));
|
||||
let spawned_buffer = buffer.clone();
|
||||
|
||||
let join_handle = tokio::spawn(async move {
|
||||
let mut buf = String::new();
|
||||
let mut reader = BufReader::new(reader);
|
||||
loop {
|
||||
trace!("Waiting for more data to read…");
|
||||
let read = reader.read_line(&mut buf).await;
|
||||
if let Ok(num) = read {
|
||||
trace!("Read {} bytes", num);
|
||||
trace!("Read Buffer: {:?}", &buf);
|
||||
{
|
||||
let mut s = spawned_buffer.lock().await;
|
||||
s.push_str(&buf);
|
||||
}
|
||||
buf.clear();
|
||||
};
|
||||
}
|
||||
});
|
||||
Ok(ConnectionConnected {
|
||||
info: self,
|
||||
writer: Arc::new(writer), //server_settings: todo!(),
|
||||
buffer,
|
||||
server_settings: Vec::new(), //join_handle: Arc::new(join_handle),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConnectionConnected {
|
||||
info: ConnectionInfo,
|
||||
//reader: Arc<ReadHalf<TlsStream<TcpStream>>>,
|
||||
writer: Arc<WriteHalf<TlsStream<TcpStream>>>,
|
||||
buffer: Arc<Mutex<String>>,
|
||||
//pub join_handle: Arc<JoinHandle<()>>,
|
||||
server_settings: Vec<Capability>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ReadError {
|
||||
#[error("The data entered was invalid")]
|
||||
InvalidInput(#[source] managesieve::Error),
|
||||
#[error("The data the server responded was invalid")]
|
||||
InvalidResponse(#[source] managesieve::Error),
|
||||
}
|
||||
|
||||
pub enum IsComplete<T> {
|
||||
Yes(T),
|
||||
No(T),
|
||||
}
|
||||
|
||||
impl ConnectionConnected {
|
||||
pub async fn log_buffer(&self) {
|
||||
trace!("Buffer is: \n{}", self.buffer.lock().await)
|
||||
}
|
||||
pub async fn log_server_settings(&self) {
|
||||
trace!("Serversettings:\n{:?}", self.server_settings)
|
||||
}
|
||||
pub async fn read_introduction(self) -> Result<IsComplete<Self>, anyhow::Error> {
|
||||
let Self {
|
||||
info,
|
||||
//reader: Arc<ReadHalf<TlsStream<TcpStream>>>,
|
||||
writer,
|
||||
buffer,
|
||||
server_settings,
|
||||
} = self;
|
||||
let mut bf = buffer.lock().await;
|
||||
let bf_str = bf.clone();
|
||||
match managesieve::response_capability(&bf_str) {
|
||||
Ok((rest, capability, response)) => {
|
||||
trace!("{:?}", response);
|
||||
bf.clear();
|
||||
bf.push_str(rest);
|
||||
drop(bf);
|
||||
Ok(IsComplete::Yes(Self {
|
||||
info,
|
||||
writer: writer.clone(),
|
||||
buffer: buffer.clone(),
|
||||
server_settings: capability,
|
||||
}))
|
||||
}
|
||||
Err(managesieve::Error::IncompleteResponse) => Ok(IsComplete::No(Self {
|
||||
info,
|
||||
writer,
|
||||
buffer: buffer.clone(),
|
||||
server_settings,
|
||||
})),
|
||||
Err(managesieve::Error::InvalidResponse) => {
|
||||
error!("invalid response");
|
||||
Err(managesieve::Error::InvalidResponse)?
|
||||
}
|
||||
Err(managesieve::Error::InvalidInput) => {
|
||||
error!("invalid input");
|
||||
Err(managesieve::Error::InvalidResponse)?
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn get_greeting(&self) -> Option<String> {
|
||||
for c in self.server_settings.iter() {
|
||||
if let Capability::Implementation(s) = c {
|
||||
return Some(s.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConnectionAuthenticated {
|
||||
info: ConnectionInfo,
|
||||
server_settings: ServerSettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServerSettings {
|
||||
pub implementation: String,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
mod server_config;
|
||||
mod utils;
|
||||
|
||||
pub use server_config::parse_server_config;
|
||||
@@ -0,0 +1,84 @@
|
||||
use log::{info, trace};
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::is_not,
|
||||
bytes::complete::tag,
|
||||
character::{
|
||||
self,
|
||||
complete::{alpha1, multispace1},
|
||||
},
|
||||
multi::many_m_n,
|
||||
sequence::preceded,
|
||||
IResult,
|
||||
};
|
||||
|
||||
use crate::{parser::utils::key_value, Methods, ServerSettings};
|
||||
|
||||
fn parse_version(input: &str) -> IResult<&str, Vec<i32>> {
|
||||
many_m_n(
|
||||
1,
|
||||
5,
|
||||
alt((
|
||||
character::complete::i32,
|
||||
preceded(character::complete::char('.'), character::complete::i32),
|
||||
)),
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_methods(input: &str) -> IResult<&str, Vec<Methods>> {
|
||||
let (rem, res) = many_m_n(1, 10, alt((alpha1, preceded(multispace1, alpha1))))(input)?;
|
||||
let methods = res
|
||||
.into_iter()
|
||||
.map(|m| match m {
|
||||
"PLAIN" => Methods::Plain,
|
||||
"OAUTHBEARER" => Methods::OAuthbearer,
|
||||
any => todo!("That method is not yet implemented: {}", any),
|
||||
})
|
||||
.collect();
|
||||
Ok((rem, methods))
|
||||
}
|
||||
|
||||
fn parse_extensions(input: &str) -> IResult<&str, Vec<String>> {
|
||||
let space_then_word = preceded(tag(" "), is_not(" "));
|
||||
let word = is_not(" ");
|
||||
let (rem, res) = many_m_n(1, 200, alt((word, space_then_word)))(input)?;
|
||||
let extensions = res.into_iter().map(|m| m.to_string()).collect();
|
||||
Ok((rem, extensions))
|
||||
}
|
||||
|
||||
pub fn parse_server_config(input: &str) -> IResult<&str, ServerSettings> {
|
||||
let (remain, (_key, implementation)) = key_value(input)?;
|
||||
trace!("Implementation:# {}", implementation);
|
||||
let (remain, (_key, version)) = key_value(remain)?;
|
||||
trace!("{}", version);
|
||||
let (_, version) = parse_version(version)?;
|
||||
trace!("Version:# {:?}", version);
|
||||
let (remain, (_key, sasl)) = key_value(remain)?;
|
||||
trace!("{}", sasl);
|
||||
let (_, sasl) = parse_methods(sasl)?;
|
||||
trace!("SASL:# {:?}", sasl);
|
||||
let (remain, (_key, capabilities)) = key_value(remain)?;
|
||||
trace!("{}", capabilities);
|
||||
let (_, capabilities) = parse_extensions(capabilities)?;
|
||||
trace!("Extensions:# {:?}", &capabilities);
|
||||
let (remain, (_key, notify)) = key_value(remain)?;
|
||||
trace!("{}", notify);
|
||||
let (_, notify) = parse_extensions(remain)?;
|
||||
trace!("Notify:# {:?}", ¬ify);
|
||||
let (remain, (_key, max_redirects)) = key_value(remain)?;
|
||||
trace!("{}", max_redirects);
|
||||
let (_, max_redirects) = character::complete::u32(max_redirects)?;
|
||||
trace!("Max Redirects:# {}", max_redirects);
|
||||
|
||||
IResult::Ok((
|
||||
remain,
|
||||
ServerSettings {
|
||||
implementation: implementation.to_string(),
|
||||
version,
|
||||
sasl,
|
||||
capabilities,
|
||||
notify,
|
||||
max_redirects,
|
||||
},
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
use nom::{
|
||||
bytes::complete::is_not,
|
||||
bytes::complete::tag,
|
||||
character::complete::{multispace0, multispace1},
|
||||
sequence::{delimited, preceded, separated_pair},
|
||||
IResult,
|
||||
};
|
||||
|
||||
pub(crate) fn quoted(input: &str) -> IResult<&str, &str> {
|
||||
preceded(multispace0, delimited(tag("\""), is_not("\n\""), tag("\"")))(input)
|
||||
}
|
||||
|
||||
pub(crate) fn key_value(input: &str) -> IResult<&str, (&str, &str)> {
|
||||
// note that this is really creating a function, the parser for abc
|
||||
// vvvvv
|
||||
// which is then called here, returning an IResult<&str, &str>
|
||||
// vvvvv
|
||||
preceded(multispace0, separated_pair(quoted, multispace1, quoted))(input)
|
||||
}
|
||||
Reference in New Issue
Block a user