fuck everything
This commit is contained in:
parent
a53f6d7e28
commit
8b7888d985
8 changed files with 94 additions and 69 deletions
|
@ -1,7 +1,7 @@
|
||||||
panorama
|
panorama
|
||||||
========
|
========
|
||||||
|
|
||||||
[![](https://tokei.rs/b1/github/iptq/panorama?category=lines)](https://github.com/XAMPPRocky/tokei).
|
[![](https://tokei.rs/b1/github/iptq/panorama?category=lines)](https://github.com/XAMPPRocky/tokei)
|
||||||
|
|
||||||
Panorama is a terminal Personal Information Manager (PIM).
|
Panorama is a terminal Personal Information Manager (PIM).
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
*
|
src/builders
|
||||||
|
src/parser
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
use panorama_imap::Response;
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
|
||||||
loop {
|
|
||||||
let line = {
|
|
||||||
print!("Enter IMAP4REV1 response: ");
|
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
|
|
||||||
let mut line = String::new();
|
|
||||||
std::io::stdin().read_line(&mut line)?;
|
|
||||||
line
|
|
||||||
};
|
|
||||||
|
|
||||||
match Response::from_bytes(line.replace("\n", "\r\n").as_bytes()) {
|
|
||||||
Ok((remaining, command)) => {
|
|
||||||
println!("{:#?}", command);
|
|
||||||
|
|
||||||
if !remaining.is_empty() {
|
|
||||||
println!("Remaining data in buffer: {:?}", remaining);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
println!("Error parsing the response. Is it correct? Exiting.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ use std::task::{Context, Poll};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::future::{Future, FutureExt};
|
use futures::future::{Future, FutureExt};
|
||||||
use panorama_strings::{StringEntry, StringStore};
|
use panorama_strings::{StringEntry, StringStore};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::{Mutex, RwLock};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{
|
io::{
|
||||||
self, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader,
|
self, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader,
|
||||||
|
@ -16,6 +16,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::command::Command;
|
use crate::command::Command;
|
||||||
|
use crate::response::Response;
|
||||||
|
|
||||||
pub type BoxedFunc = Box<dyn Fn()>;
|
pub type BoxedFunc = Box<dyn Fn()>;
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ pub struct Client<C> {
|
||||||
symbols: StringStore,
|
symbols: StringStore,
|
||||||
|
|
||||||
id: usize,
|
id: usize,
|
||||||
handlers: Arc<RwLock<HashMap<usize, bool>>>,
|
results: ResultMap,
|
||||||
|
|
||||||
/// Cached capabilities that shouldn't change between
|
/// Cached capabilities that shouldn't change between
|
||||||
caps: Vec<StringEntry>,
|
caps: Vec<StringEntry>,
|
||||||
|
@ -39,40 +40,49 @@ where
|
||||||
/// Creates a new client that wraps a connection
|
/// Creates a new client that wraps a connection
|
||||||
pub fn new(conn: C) -> Self {
|
pub fn new(conn: C) -> Self {
|
||||||
let (read_half, write_half) = io::split(conn);
|
let (read_half, write_half) = io::split(conn);
|
||||||
let listen_fut = tokio::spawn(listen(read_half));
|
let results = Arc::new(RwLock::new(HashMap::new()));
|
||||||
|
let listen_fut = tokio::spawn(listen(read_half, results.clone()));
|
||||||
|
|
||||||
Client {
|
Client {
|
||||||
conn: write_half,
|
conn: write_half,
|
||||||
symbols: StringStore::new(256),
|
symbols: StringStore::new(256),
|
||||||
id: 0,
|
id: 0,
|
||||||
handlers: Arc::new(RwLock::new(HashMap::new())),
|
results,
|
||||||
caps: Vec::new(),
|
caps: Vec::new(),
|
||||||
handle: listen_fut,
|
handle: listen_fut,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a command to the server and returns a handle to retrieve the result
|
/// Sends a command to the server and returns a handle to retrieve the result
|
||||||
pub async fn execute(&mut self, cmd: Command) -> Result<()> {
|
pub async fn execute(&mut self, cmd: Command) -> Result<Response> {
|
||||||
|
debug!("executing command {:?}", cmd);
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.id += 1;
|
self.id += 1;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut handlers = self.handlers.write();
|
let mut handlers = self.results.write();
|
||||||
handlers.insert(id, false);
|
handlers.insert(id, (None, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let cmd_str = cmd.to_string();
|
let cmd_str = format!("pano{} {}\n", id, cmd);
|
||||||
|
debug!("[{}] writing to socket: {:?}", id, cmd_str);
|
||||||
self.conn.write_all(cmd_str.as_bytes()).await?;
|
self.conn.write_all(cmd_str.as_bytes()).await?;
|
||||||
|
debug!("[{}] written.", id);
|
||||||
|
|
||||||
ExecHandle(self, id).await;
|
ExecHandle(self, id).await;
|
||||||
Ok(())
|
let resp = {
|
||||||
|
let mut handlers = self.results.write();
|
||||||
|
handlers.remove(&id).unwrap().0.unwrap()
|
||||||
|
};
|
||||||
|
Ok(Response(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes the CAPABILITY command
|
/// Executes the CAPABILITY command
|
||||||
pub async fn supports(&mut self) {
|
pub async fn supports(&mut self) -> Result<()> {
|
||||||
let cmd = Command::Capability;
|
let cmd = Command::Capability;
|
||||||
let result = self.execute(cmd).await;
|
debug!("sending: {:?} {:?}", cmd, cmd.to_string());
|
||||||
debug!("poggers {:?}", result);
|
let result = self.execute(cmd).await?;
|
||||||
|
debug!("result from supports: {:?}", result);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,28 +90,48 @@ pub struct ExecHandle<'a, C>(&'a Client<C>, usize);
|
||||||
|
|
||||||
impl<'a, C> Future for ExecHandle<'a, C> {
|
impl<'a, C> Future for ExecHandle<'a, C> {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
let state = {
|
let mut handlers = self.0.results.write();
|
||||||
let handlers = self.0.handlers.read();
|
let mut state = handlers.get_mut(&self.1);
|
||||||
handlers.get(&self.1).cloned()
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: handle the None case here
|
// TODO: handle the None case here
|
||||||
let state = state.unwrap();
|
debug!("f[{}] {:?}", self.1, state);
|
||||||
|
let (result, waker) = state.unwrap();
|
||||||
|
|
||||||
match state {
|
match result {
|
||||||
true => Poll::Ready(()),
|
Some(_) => Poll::Ready(()),
|
||||||
false => Poll::Pending,
|
None => {
|
||||||
|
*waker = Some(cx.waker().clone());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn listen(conn: impl AsyncRead + Unpin) -> Result<()> {
|
use std::task::Waker;
|
||||||
|
pub type ResultMap = Arc<RwLock<HashMap<usize, (Option<String>, Option<Waker>)>>>;
|
||||||
|
|
||||||
|
async fn listen(conn: impl AsyncRead + Unpin, results: ResultMap) -> Result<()> {
|
||||||
debug!("amogus");
|
debug!("amogus");
|
||||||
let mut reader = BufReader::new(conn);
|
let mut reader = BufReader::new(conn);
|
||||||
loop {
|
loop {
|
||||||
let mut next_line = String::new();
|
let mut next_line = String::new();
|
||||||
reader.read_line(&mut next_line).await?;
|
reader.read_line(&mut next_line).await?;
|
||||||
debug!("line: {:?}", next_line);
|
|
||||||
|
// debug!("line: {:?}", next_line);
|
||||||
|
let parts = next_line.split(" ").collect::<Vec<_>>();
|
||||||
|
let tag = parts[0];
|
||||||
|
if tag == "*" {
|
||||||
|
debug!("UNTAGGED {:?}", next_line);
|
||||||
|
} else if tag.starts_with("pano") {
|
||||||
|
let id = tag.trim_start_matches("pano").parse::<usize>()?;
|
||||||
|
debug!("set {} to {:?}", id, next_line);
|
||||||
|
let mut results = results.write();
|
||||||
|
if let Some((c, w)) = results.get_mut(&id) {
|
||||||
|
*c = Some(next_line);
|
||||||
|
let w = w.take().unwrap();
|
||||||
|
w.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
//! The IMAP client in this module is implemented as a state machine in the type system: methods
|
//! The IMAP client in this module is implemented as a state machine in the type system: methods
|
||||||
//! that are not supported in a particular state (ex. fetch in an unauthenticated state) cannot be
|
//! that are not supported in a particular state (ex. fetch in an unauthenticated state) cannot be
|
||||||
//! expressed in the type system entirely.
|
//! expressed in the type system entirely.
|
||||||
|
//!
|
||||||
|
//! Because there's many client types for the different types of clients, you'll want to start
|
||||||
|
//! here:
|
||||||
|
//!
|
||||||
|
//! - [ClientBuilder][self::ClientBuilder] : Constructs the config for the IMAP client
|
||||||
|
|
||||||
mod inner;
|
mod inner;
|
||||||
|
|
||||||
|
@ -18,6 +23,13 @@ use tokio_rustls::{client::TlsStream, rustls::ClientConfig, webpki::DNSNameRef,
|
||||||
|
|
||||||
use self::inner::Client;
|
use self::inner::Client;
|
||||||
|
|
||||||
|
/// Struct used to start building the config for a client.
|
||||||
|
///
|
||||||
|
/// Call [`.build`][1] to _build_ the config, then run [`.connect`][2] to actually start opening
|
||||||
|
/// the connection to the server.
|
||||||
|
///
|
||||||
|
/// [1]: self::ClientNotConnectedBuilder::build
|
||||||
|
/// [2]: self::ClientNotConnected::connect
|
||||||
pub type ClientBuilder = ClientNotConnectedBuilder;
|
pub type ClientBuilder = ClientNotConnectedBuilder;
|
||||||
|
|
||||||
/// An IMAP client that hasn't been connected yet.
|
/// An IMAP client that hasn't been connected yet.
|
||||||
|
@ -52,37 +64,45 @@ impl ClientNotConnected {
|
||||||
|
|
||||||
let inner = Client::new(conn);
|
let inner = Client::new(conn);
|
||||||
return Ok(ClientUnauthenticated::Encrypted(
|
return Ok(ClientUnauthenticated::Encrypted(
|
||||||
ClientEncryptedUnauthenticated { inner },
|
ClientUnauthenticatedEncrypted { inner },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner = Client::new(conn);
|
let inner = Client::new(conn);
|
||||||
return Ok(ClientUnauthenticated::Unencrypted(
|
return Ok(ClientUnauthenticated::Unencrypted(
|
||||||
ClientUnencryptedUnauthenticated { inner },
|
ClientUnauthenticatedUnencrypted { inner },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ClientUnauthenticated {
|
pub enum ClientUnauthenticated {
|
||||||
Encrypted(ClientEncryptedUnauthenticated),
|
Encrypted(ClientUnauthenticatedEncrypted),
|
||||||
Unencrypted(ClientUnencryptedUnauthenticated),
|
Unencrypted(ClientUnauthenticatedUnencrypted),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientUnauthenticated {}
|
impl ClientUnauthenticated {
|
||||||
|
pub async fn supports(&mut self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
ClientUnauthenticated::Encrypted(e) => e.inner.supports().await?,
|
||||||
|
ClientUnauthenticated::Unencrypted(e) => e.inner.supports().await?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ClientUnencryptedUnauthenticated {
|
pub struct ClientUnauthenticatedUnencrypted {
|
||||||
/// Connection to the remote server
|
/// Connection to the remote server
|
||||||
inner: Client<TcpStream>,
|
inner: Client<TcpStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientUnencryptedUnauthenticated {
|
impl ClientUnauthenticatedUnencrypted {
|
||||||
pub async fn upgrade(&self) {}
|
pub async fn upgrade(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An IMAP client that isn't authenticated.
|
/// An IMAP client that isn't authenticated.
|
||||||
pub struct ClientEncryptedUnauthenticated {
|
pub struct ClientUnauthenticatedEncrypted {
|
||||||
/// Connection to the remote server
|
/// Connection to the remote server
|
||||||
inner: Client<TlsStream<TcpStream>>,
|
inner: Client<TlsStream<TcpStream>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientEncryptedUnauthenticated {}
|
impl ClientUnauthenticatedEncrypted {}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Commands, without the tag part.
|
/// Commands, without the tag part.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Capability,
|
Capability,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub struct Response {}
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Response(pub String);
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub async fn run_mail(
|
||||||
let handle = tokio::spawn(async {
|
let handle = tokio::spawn(async {
|
||||||
for acct in config.mail_accounts.into_iter() {
|
for acct in config.mail_accounts.into_iter() {
|
||||||
debug!("opening imap connection for {:?}", acct);
|
debug!("opening imap connection for {:?}", acct);
|
||||||
osu(acct).await;
|
osu(acct).await.unwrap();
|
||||||
// open_imap_connection(acct.imap).await.unwrap();
|
// open_imap_connection(acct.imap).await.unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -68,7 +68,10 @@ async fn osu(acct: MailAccountConfig) -> Result<()> {
|
||||||
.map_err(|err| anyhow!("err: {}", err))?;
|
.map_err(|err| anyhow!("err: {}", err))?;
|
||||||
|
|
||||||
debug!("connecting to {}:{}", &acct.imap.server, acct.imap.port);
|
debug!("connecting to {}:{}", &acct.imap.server, acct.imap.port);
|
||||||
let unauth = builder.connect().await;
|
let mut unauth = builder.connect().await?;
|
||||||
|
|
||||||
|
debug!("sending CAPABILITY");
|
||||||
|
unauth.supports().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue