From a9c1d6ed01f70c37a113538fd08a175553b11ecf Mon Sep 17 00:00:00 2001 From: Amanda Graven Date: Tue, 24 Nov 2020 16:11:27 +0100 Subject: [PATCH] Save and use authentication token --- Cargo.toml | 7 +++- src/main.rs | 17 ++++++++-- src/matrix.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++------ src/ui.rs | 51 +++++++++++++++++++++++----- 4 files changed, 147 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ab4805..8c4a013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,11 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0" +dirs-next = "2.0" iced = { git = "https://github.com/hecrj/iced", rev = "fc4270f", features = ["debug", "tokio"] } +hostname = "0.3" matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk", rev = "27c6f30" } -tokio = { version = "0.2", features = ["macros"] } +serde = { version = "1.0", features = ["derive"] } +tokio = "0.2" +toml = "0.5" diff --git a/src/main.rs b/src/main.rs index 0810076..2ac5c63 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,22 @@ +extern crate dirs_next as dirs; + +use std::fs::Permissions; +use std::os::unix::fs::PermissionsExt; + use iced::Application; pub mod matrix; pub mod ui; -#[tokio::main] -async fn main() { +fn main() -> Result<(), Box> { + let config_dir = dirs::config_dir().unwrap().join("retrix"); + // Make sure config dir exists and is not accessible by other users. + if !config_dir.is_dir() { + std::fs::create_dir(&config_dir)?; + std::fs::set_permissions(&config_dir, Permissions::from_mode(0o700))?; + } + ui::Retrix::run(iced::Settings::default()); + + Ok(()) } diff --git a/src/matrix.rs b/src/matrix.rs index 95d3e26..a2deb89 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1,6 +1,38 @@ -use matrix_sdk::{reqwest::Url, Client, Session, SyncSettings}; +use matrix_sdk::{ + identifiers::DeviceId, identifiers::UserId, reqwest::Url, Client, ClientConfig, Session, + SyncSettings, +}; +use serde::{Deserialize, Serialize}; -pub type Error = Box; +pub type Error = anyhow::Error; + +// Needed to be able to serialize `Session`s. Should be done with serde remote. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct SessionWrapper { + access_token: String, + user_id: UserId, + device_id: Box, +} + +impl From for Session { + fn from(s: SessionWrapper) -> Self { + Self { + access_token: s.access_token, + user_id: s.user_id, + device_id: s.device_id, + } + } +} + +impl From for SessionWrapper { + fn from(s: Session) -> Self { + Self { + access_token: s.access_token, + user_id: s.user_id, + device_id: s.device_id, + } + } +} pub async fn login( username: &str, @@ -8,17 +40,57 @@ pub async fn login( server: &str, ) -> Result<(Client, Session), Error> { let url = Url::parse(server)?; - let client = Client::new(url)?; + let config = ClientConfig::new().store_path(&dirs::config_dir().unwrap().join("retrix")); + let client = Client::new_with_config(url, config)?; - let response = client - .login(username, password, None, Some("retrix")) - .await?; - let session = Session { - access_token: response.access_token, - user_id: response.user_id, - device_id: response.device_id, + let session = match get_session()? { + Some(session) => { + client.restore_login(session.clone()).await?; + session + } + None => { + let response = client + .login(username, password, None, Some("retrix")) + .await?; + let session = Session { + access_token: response.access_token, + user_id: response.user_id, + device_id: response.device_id, + }; + write_session(session.clone())?; + session + } }; - client.sync(SyncSettings::new()).await; + client.sync_once(SyncSettings::new()).await?; Ok((client, session)) } + +/// File path to store session data in +fn session_path() -> std::path::PathBuf { + dirs::config_dir() + .unwrap() + .join("retrix") + .join("session.toml") +} + +/// Read session data from config file +fn get_session() -> Result, Error> { + let path = session_path(); + if !path.is_file() { + return Ok(None); + } + let session: SessionWrapper = toml::from_slice(&std::fs::read(path)?)?; + Ok(Some(session.into())) +} + +/// Save session data to config file +fn write_session(session: Session) -> Result<(), Error> { + let session: SessionWrapper = session.into(); + let path = session_path(); + + let serialized = toml::to_string(&session)?; + std::fs::write(path, serialized)?; + + Ok(()) +} diff --git a/src/ui.rs b/src/ui.rs index 29a03aa..afd5be3 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,6 +1,6 @@ use iced::{ text_input::{self, TextInput}, - Application, Button, Column, Command, Container, Element, Text, + Application, Button, Column, Command, Container, Element, Length, Row, Text, }; use crate::matrix; @@ -21,17 +21,23 @@ pub enum Retrix { LoggedIn { client: matrix_sdk::Client, session: matrix_sdk::Session, + + rooms: Vec, }, } #[derive(Debug, Clone)] pub enum Message { + // Login form messages SetUser(String), SetPassword(String), SetServer(String), Login, LoggedIn(matrix_sdk::Client, matrix_sdk::Session), SetError(String), + + // Main state messages + ResetRooms(Vec), } impl Application for Retrix { @@ -83,10 +89,32 @@ impl Application for Retrix { }, ); } - Message::LoggedIn(client, session) => *self = Retrix::LoggedIn { client, session }, + Message::LoggedIn(client, session) => { + *self = Retrix::LoggedIn { + client: client.clone(), + session, + rooms: Vec::new(), + }; + let client = client.clone(); + Command::perform( + async move { + let mut list = Vec::new(); + for (id, room) in client.joined_rooms().read().await.iter() { + let room = room.read().await; + list.push(room.clone()); + } + list + }, + |rooms| Message::ResetRooms(rooms), + ); + } + _ => (), }, - _ => (), - } + Retrix::LoggedIn { ref mut rooms, .. } => match message { + Message::ResetRooms(r) => *rooms = r, + _ => (), + }, + }; Command::none() } @@ -102,6 +130,7 @@ impl Application for Retrix { ref server, ref error, } => { + // Login form let mut content = Column::new() .width(500.into()) .push(Text::new("Username")) @@ -136,10 +165,16 @@ impl Application for Retrix { .height(iced::Length::Fill) .into() } - Retrix::LoggedIn { - ref client, - ref session, - } => Text::new(format!("Logged in to {}", session.user_id)).into(), + Retrix::LoggedIn { ref rooms, .. } => { + //let mut root_row = Row::new().width(Length::Fill).height(Length::Fill); + let mut room_col = Column::new().width(400.into()).height(Length::Fill); + for room in rooms { + room_col = room_col.push(Text::new(room.display_name())); + } + room_col.into() + //root_row = root_row.push(room_col); + //root_row.into() + } } } }