diff --git a/Cargo.lock b/Cargo.lock index dfcf4ac..fa0a92d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,6 +155,16 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "cookie" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" +dependencies = [ + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -178,6 +188,7 @@ dependencies = [ "anyhow", "async-trait", "clap", + "cookie", "crossterm", "directories", "edit", diff --git a/Cargo.toml b/Cargo.toml index 4dd28e5..4c10e08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ thiserror = "1.0.31" tokio = { version = "1.20.0", features = ["full"] } unicode-segmentation = "1.9.0" unicode-width = "0.1.9" +cookie = "0.16.0" [dependencies.time] version = "0.3.11" diff --git a/src/vault.rs b/src/vault.rs index cddede3..a97e6c9 100644 --- a/src/vault.rs +++ b/src/vault.rs @@ -19,7 +19,7 @@ enum Request { #[derive(Debug, Clone)] pub struct Vault { - tx: mpsc::UnboundedSender, + pub(self) tx: mpsc::UnboundedSender, } impl Vault { @@ -37,7 +37,7 @@ impl Vault { pub fn euph(&self, room: String) -> EuphVault { EuphVault { - tx: self.tx.clone(), + vault: self.clone(), room, } } diff --git a/src/vault/euph.rs b/src/vault/euph.rs index 5c286ce..8bfe160 100644 --- a/src/vault/euph.rs +++ b/src/vault/euph.rs @@ -1,10 +1,12 @@ use std::mem; +use std::str::FromStr; use async_trait::async_trait; +use cookie::{Cookie, CookieJar}; use rusqlite::types::{FromSql, FromSqlError, ToSqlOutput, Value, ValueRef}; use rusqlite::{named_params, params, Connection, OptionalExtension, ToSql, Transaction}; use time::OffsetDateTime; -use tokio::sync::{mpsc, oneshot}; +use tokio::sync::oneshot; use toss::styled::Styled; use crate::euph; @@ -85,6 +87,19 @@ impl From for Request { } impl Vault { + pub async fn euph_cookies(&self) -> CookieJar { + // TODO vault::Error + let (tx, rx) = oneshot::channel(); + let request = EuphRequest::GetCookies { result: tx }; + let _ = self.tx.send(request.into()); + rx.await.unwrap() + } + + pub async fn set_euph_cookies(&self, cookies: CookieJar) { + let request = EuphRequest::SetCookies { cookies }; + let _ = self.tx.send(request.into()); + } + pub async fn euph_rooms(&self) -> Vec { // TODO vault::Error let (tx, rx) = oneshot::channel(); @@ -96,11 +111,15 @@ impl Vault { #[derive(Debug, Clone)] pub struct EuphVault { - pub(super) tx: mpsc::UnboundedSender, + pub(super) vault: Vault, pub(super) room: String, } impl EuphVault { + pub fn vault(&self) -> &Vault { + &self.vault + } + pub fn room(&self) -> &str { &self.room } @@ -110,12 +129,12 @@ impl EuphVault { room: self.room.clone(), time, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); } pub fn delete(self) { let request = EuphRequest::Delete { room: self.room }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); } pub fn add_message(&self, msg: Message, prev_msg: Option) { @@ -124,7 +143,7 @@ impl EuphVault { msg, prev_msg, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); } pub fn add_messages(&self, msgs: Vec, next_msg: Option) { @@ -133,7 +152,7 @@ impl EuphVault { msgs, next_msg, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); } pub async fn last_span(&self) -> Option<(Option, Option)> { @@ -143,7 +162,7 @@ impl EuphVault { room: self.room.clone(), result: tx, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); rx.await.unwrap() } } @@ -158,7 +177,7 @@ impl MsgStore for EuphVault { id: *id, result: tx, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); rx.await.unwrap() } @@ -170,7 +189,7 @@ impl MsgStore for EuphVault { root: *root, result: tx, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); rx.await.unwrap() } @@ -182,7 +201,7 @@ impl MsgStore for EuphVault { root: *root, result: tx, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); rx.await.unwrap() } @@ -194,7 +213,7 @@ impl MsgStore for EuphVault { root: *root, result: tx, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); rx.await.unwrap() } @@ -205,7 +224,7 @@ impl MsgStore for EuphVault { room: self.room.clone(), result: tx, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); rx.await.unwrap() } @@ -216,12 +235,22 @@ impl MsgStore for EuphVault { room: self.room.clone(), result: tx, }; - let _ = self.tx.send(request.into()); + let _ = self.vault.tx.send(request.into()); rx.await.unwrap() } } pub(super) enum EuphRequest { + ///////////// + // Cookies // + ///////////// + GetCookies { + result: oneshot::Sender, + }, + SetCookies { + cookies: CookieJar, + }, + /////////// // Rooms // /////////// @@ -286,6 +315,8 @@ pub(super) enum EuphRequest { impl EuphRequest { pub(super) fn perform(self, conn: &mut Connection) { let result = match self { + EuphRequest::GetCookies { result } => Self::get_cookies(conn, result), + EuphRequest::SetCookies { cookies } => Self::set_cookies(conn, cookies), EuphRequest::GetRooms { result } => Self::get_rooms(conn, result), EuphRequest::Join { room, time } => Self::join(conn, room, time), EuphRequest::Delete { room } => Self::delete(conn, room), @@ -324,6 +355,54 @@ impl EuphRequest { } } + fn get_cookies( + conn: &mut Connection, + result: oneshot::Sender, + ) -> rusqlite::Result<()> { + let cookies = conn + .prepare( + " + SELECT cookie + FROM euph_cookies + ", + )? + .query_map([], |row| { + let cookie_str: String = row.get(0)?; + Ok(Cookie::from_str(&cookie_str).expect("cookie in db is valid")) + })? + .collect::>>()?; + + let mut cookie_jar = CookieJar::new(); + for cookie in cookies { + cookie_jar.add_original(cookie); + } + + let _ = result.send(cookie_jar); + Ok(()) + } + + fn set_cookies(conn: &mut Connection, cookies: CookieJar) -> rusqlite::Result<()> { + let tx = conn.transaction()?; + + // Since euphoria sets all cookies on every response, we can just delete + // all previous cookies. + tx.execute_batch("DELETE FROM euph_cookies")?; + + let mut insert_cookie = tx.prepare( + " + INSERT INTO euph_cookies (cookie) + VALUES (?) + ", + )?; + for cookie in cookies.iter() { + insert_cookie.execute([format!("{cookie}")])?; + } + drop(insert_cookie); + + tx.commit()?; + Ok(()) + } + fn get_rooms( conn: &mut Connection, result: oneshot::Sender>, diff --git a/src/vault/migrate.rs b/src/vault/migrate.rs index 78fda14..ce52af8 100644 --- a/src/vault/migrate.rs +++ b/src/vault/migrate.rs @@ -16,7 +16,7 @@ pub fn migrate(conn: &mut Connection) -> rusqlite::Result<()> { tx.commit() } -const MIGRATIONS: [fn(&mut Transaction) -> rusqlite::Result<()>; 1] = [m1]; +const MIGRATIONS: [fn(&mut Transaction) -> rusqlite::Result<()>; 2] = [m1, m2]; fn m1(tx: &mut Transaction) -> rusqlite::Result<()> { tx.execute_batch( @@ -75,3 +75,13 @@ fn m1(tx: &mut Transaction) -> rusqlite::Result<()> { ", ) } + +fn m2(tx: &mut Transaction) -> rusqlite::Result<()> { + tx.execute_batch( + " + CREATE TABLE euph_cookies ( + cookie TEXT NOT NULL + ) STRICT; + ", + ) +}