Implemented BookmarkClient in AuthActivity

This commit is contained in:
ChristianVisintin
2020-12-16 16:01:29 +01:00
parent 10df5abae2
commit 14ddba022f
4 changed files with 173 additions and 293 deletions

View File

@@ -27,78 +27,31 @@
extern crate dirs; extern crate dirs;
// Locals // Locals
use super::{AuthActivity, Color, FileTransferProtocol, InputMode, PopupType, UserHosts}; use super::{AuthActivity, Color, DialogYesNoOption, InputMode, PopupType};
use crate::bookmarks::serializer::BookmarkSerializer; use crate::system::bookmarks_client::BookmarksClient;
use crate::bookmarks::Bookmark; use crate::system::environment;
use crate::utils::time_to_str;
// Ext // Ext
use std::path::PathBuf; use std::path::PathBuf;
use std::time::SystemTime;
impl AuthActivity { impl AuthActivity {
/// ### read_bookmarks
///
/// Read bookmarks from data file; Show popup if necessary
pub(super) fn read_bookmarks(&mut self) {
// Init bookmarks
if let Some(bookmark_file) = self.init_bookmarks() {
// Read
if self.context.is_some() {
match self
.context
.as_ref()
.unwrap()
.local
.open_file_read(bookmark_file.as_path())
{
Ok(reader) => {
// Read bookmarks
let deserializer: BookmarkSerializer = BookmarkSerializer {};
match deserializer.deserialize(Box::new(reader)) {
Ok(bookmarks) => self.bookmarks = Some(bookmarks),
Err(err) => {
self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Yellow,
format!(
"Could not read bookmarks from \"{}\": {}",
bookmark_file.display(),
err
),
))
}
}
}
Err(err) => {
self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Yellow,
format!(
"Could not read bookmarks from \"{}\": {}",
bookmark_file.display(),
err
),
))
}
}
}
}
}
/// ### del_bookmark /// ### del_bookmark
/// ///
/// Delete bookmark /// Delete bookmark
pub(super) fn del_bookmark(&mut self, idx: usize) { pub(super) fn del_bookmark(&mut self, idx: usize) {
if let Some(hosts) = self.bookmarks.as_mut() { if let Some(bookmarks_cli) = self.bookmarks_client.as_mut() {
// Iterate over kyes // Iterate over kyes
let mut name: Option<String> = None; let mut name: Option<String> = None;
for (i, key) in hosts.bookmarks.keys().enumerate() { for (i, key) in bookmarks_cli.iter_bookmarks().enumerate() {
if i == idx { if i == idx {
name = Some(key.clone()); name = Some(key.clone());
break; break;
} }
} }
if let Some(name) = name { if let Some(name) = name {
hosts.bookmarks.remove(name.as_str()); bookmarks_cli.del_bookmark(&name);
// Write bookmarks
self.write_bookmarks();
} }
} }
} }
@@ -107,20 +60,20 @@ impl AuthActivity {
/// ///
/// Load selected bookmark (at index) to input fields /// Load selected bookmark (at index) to input fields
pub(super) fn load_bookmark(&mut self, idx: usize) { pub(super) fn load_bookmark(&mut self, idx: usize) {
if let Some(hosts) = self.bookmarks.as_mut() { if let Some(bookmarks_cli) = self.bookmarks_client.as_ref() {
// Iterate over bookmarks // Iterate over bookmarks
for (i, bookmark) in hosts.bookmarks.values().enumerate() { for (i, key) in bookmarks_cli.iter_bookmarks().enumerate() {
if i == idx { if i == idx {
if let Some(bookmark) = bookmarks_cli.get_bookmark(&key) {
// Load parameters // Load parameters
self.address = bookmark.address.clone(); self.address = bookmark.0;
self.port = bookmark.port.to_string(); self.port = bookmark.1.to_string();
self.protocol = match bookmark.protocol.as_str().to_uppercase().as_str() { self.protocol = bookmark.2;
"FTP" => FileTransferProtocol::Ftp(false), self.username = bookmark.3;
"FTPS" => FileTransferProtocol::Ftp(true), if let Some(password) = bookmark.4 {
"SCP" => FileTransferProtocol::Scp, self.password = password;
_ => FileTransferProtocol::Sftp, // Default to SFTP }
}; }
self.username = bookmark.username.clone();
// Break // Break
break; break;
} }
@@ -132,29 +85,61 @@ impl AuthActivity {
/// ///
/// Save current input fields as a bookmark /// Save current input fields as a bookmark
pub(super) fn save_bookmark(&mut self, name: String) { pub(super) fn save_bookmark(&mut self, name: String) {
if let Ok(host) = self.make_user_host() { // Check port
if let Some(hosts) = self.bookmarks.as_mut() { let port: u16 = match self.port.parse::<usize>() {
hosts.bookmarks.insert(name, host); Ok(val) => {
// Write bookmarks if val > 65535 {
self.write_bookmarks(); self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Red,
String::from("Specified port must be in range 0-65535"),
));
return;
} }
val as u16
}
Err(_) => {
self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Red,
String::from("Specified port is not a number"),
));
return;
}
};
if let Some(bookmarks_cli) = self.bookmarks_client.as_mut() {
// Check if password must be saved
let password: Option<String> = match self.choice_opt {
DialogYesNoOption::Yes => Some(self.password.clone()),
DialogYesNoOption::No => None,
};
bookmarks_cli.add_bookmark(
name,
self.address.clone(),
port,
self.protocol,
self.username.clone(),
password,
);
// Save bookmarks
self.write_bookmarks();
} }
} }
/// ### del_recent /// ### del_recent
/// ///
/// Delete recent /// Delete recent
pub(super) fn del_recent(&mut self, idx: usize) { pub(super) fn del_recent(&mut self, idx: usize) {
if let Some(hosts) = self.bookmarks.as_mut() { if let Some(client) = self.bookmarks_client.as_mut() {
// Iterate over kyes // Iterate over kyes
let mut name: Option<String> = None; let mut name: Option<String> = None;
for (i, key) in hosts.recents.keys().enumerate() { for (i, key) in client.iter_recents().enumerate() {
if i == idx { if i == idx {
name = Some(key.clone()); name = Some(key.clone());
break; break;
} }
} }
if let Some(name) = name { if let Some(name) = name {
hosts.recents.remove(name.as_str()); client.del_recent(&name);
// Save bookmarks
self.write_bookmarks();
} }
} }
} }
@@ -163,71 +148,28 @@ impl AuthActivity {
/// ///
/// Load selected recent (at index) to input fields /// Load selected recent (at index) to input fields
pub(super) fn load_recent(&mut self, idx: usize) { pub(super) fn load_recent(&mut self, idx: usize) {
if let Some(hosts) = self.bookmarks.as_mut() { if let Some(client) = self.bookmarks_client.as_ref() {
// Iterate over bookmarks // Iterate over bookmarks
for (i, bookmark) in hosts.recents.values().enumerate() { for (i, key) in client.iter_recents().enumerate() {
if i == idx { if i == idx {
if let Some(bookmark) = client.get_recent(key) {
// Load parameters // Load parameters
self.address = bookmark.address.clone(); self.address = bookmark.0;
self.port = bookmark.port.to_string(); self.port = bookmark.1.to_string();
self.protocol = match bookmark.protocol.as_str().to_uppercase().as_str() { self.protocol = bookmark.2;
"FTP" => FileTransferProtocol::Ftp(false), self.username = bookmark.3;
"FTPS" => FileTransferProtocol::Ftp(true),
"SCP" => FileTransferProtocol::Scp,
_ => FileTransferProtocol::Sftp, // Default to SFTP
};
self.username = bookmark.username.clone();
// Break // Break
break; break;
} }
} }
} }
} }
}
/// ### save_recent /// ### save_recent
/// ///
/// Save current input fields as a "recent" /// Save current input fields as a "recent"
pub(super) fn save_recent(&mut self) { pub(super) fn save_recent(&mut self) {
if let Ok(host) = self.make_user_host() {
if let Some(hosts) = self.bookmarks.as_mut() {
// Check if duplicated
for recent_host in hosts.recents.values() {
if *recent_host == host {
// Don't save duplicates
return;
}
}
// If hosts size is bigger than 16; pop last
if hosts.recents.len() >= 16 {
let mut keys: Vec<String> = Vec::with_capacity(hosts.recents.len());
for key in hosts.recents.keys() {
keys.push(key.clone());
}
// Sort keys; NOTE: most recent is the last element
keys.sort();
// Delete keys starting from the last one
for key in keys.iter() {
let _ = hosts.recents.remove(key);
// If length is < 16; break
if hosts.recents.len() < 16 {
break;
}
}
}
// Create name
let name: String = time_to_str(SystemTime::now(), "ISO%Y%m%dT%H%M%S");
// Save host to recents
hosts.recents.insert(name, host);
// Write bookmarks
self.write_bookmarks();
}
}
}
/// ### make_user_host
///
/// Make user host from current input fields
fn make_user_host(&mut self) -> Result<Bookmark, ()> {
// Check port // Check port
let port: u16 = match self.port.parse::<usize>() { let port: u16 = match self.port.parse::<usize>() {
Ok(val) => { Ok(val) => {
@@ -236,7 +178,7 @@ impl AuthActivity {
Color::Red, Color::Red,
String::from("Specified port must be in range 0-65535"), String::from("Specified port must be in range 0-65535"),
)); ));
return Err(()); return;
} }
val as u16 val as u16
} }
@@ -245,59 +187,59 @@ impl AuthActivity {
Color::Red, Color::Red,
String::from("Specified port is not a number"), String::from("Specified port is not a number"),
)); ));
return Err(()); return;
} }
}; };
Ok(Bookmark { if let Some(bookmarks_cli) = self.bookmarks_client.as_mut() {
address: self.address.clone(), bookmarks_cli.add_recent(
self.address.clone(),
port, port,
protocol: match self.protocol { self.protocol,
FileTransferProtocol::Ftp(secure) => match secure { self.username.clone(),
true => String::from("FTPS"), );
false => String::from("FTP"), // Save bookmarks
}, self.write_bookmarks();
FileTransferProtocol::Scp => String::from("SCP"), }
FileTransferProtocol::Sftp => String::from("SFTP"),
},
username: self.username.clone(),
})
} }
/// ### write_bookmarks /// ### write_bookmarks
/// ///
/// Write bookmarks to file /// Write bookmarks to file
fn write_bookmarks(&mut self) { fn write_bookmarks(&mut self) {
if self.bookmarks.is_some() && self.context.is_some() { if let Some(bookmarks_cli) = self.bookmarks_client.as_ref() {
// Open file for write if let Err(err) = bookmarks_cli.write_bookmarks() {
if let Some(bookmarks_file) = self.init_bookmarks() {
match self
.context
.as_ref()
.unwrap()
.local
.open_file_write(bookmarks_file.as_path())
{
Ok(writer) => {
let serializer: BookmarkSerializer = BookmarkSerializer {};
if let Err(err) = serializer
.serialize(Box::new(writer), &self.bookmarks.as_ref().unwrap())
{
self.input_mode = InputMode::Popup(PopupType::Alert( self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Yellow, Color::Red,
format!( format!("Could not write bookmarks: {}", err),
"Could not write default bookmarks at \"{}\": {}",
bookmarks_file.display(),
err
),
)); ));
} }
} }
}
/// ### init_bookmarks_client
///
/// Initialize bookmarks client
pub(super) fn init_bookmarks_client(&mut self) {
// Get config dir
match environment::init_config_dir() {
Ok(path) => {
// If some configure client, otherwise do nothing; don't bother users telling them that bookmarks are not supported on their system.
if let Some(path) = path {
// Prepare paths
let mut bookmarks_file: PathBuf = path.clone();
bookmarks_file.push("bookmarks.toml");
let mut key_file: PathBuf = path.clone();
key_file.push(".bookmarks.key"); // key file is hidden
// Initialize client
match BookmarksClient::new(bookmarks_file.as_path(), key_file.as_path()) {
Ok(cli) => self.bookmarks_client = Some(cli),
Err(err) => { Err(err) => {
self.input_mode = InputMode::Popup(PopupType::Alert( self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Yellow, Color::Red,
format!( format!(
"Could not write default bookmarks at \"{}\": {}", "Could not initialize bookmarks (at \"{}\", \"{}\"): {}",
bookmarks_file.display(), bookmarks_file.display(),
key_file.display(),
err err
), ),
)) ))
@@ -305,96 +247,12 @@ impl AuthActivity {
} }
} }
} }
}
/// ### init_bookmarks
///
/// Initialize bookmarks directory
/// Returns bookmark path
fn init_bookmarks(&mut self) -> Option<PathBuf> {
// Get file
lazy_static! {
static ref CONF_DIR: Option<PathBuf> = dirs::config_dir();
}
if CONF_DIR.is_some() {
// Get path of bookmarks
let mut p: PathBuf = CONF_DIR.as_ref().unwrap().clone();
// Append termscp dir
p.push("termscp/");
// Mkdir if doesn't exist
if self.context.is_some() {
if let Err(err) = self
.context
.as_mut()
.unwrap()
.local
.mkdir_ex(p.as_path(), true)
{
// Show popup
self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Yellow,
format!(
"Could not create configuration directory at \"{}\": {}",
p.display(),
err
),
));
// Return None
return None;
}
}
// Append bookmarks.toml
p.push("bookmarks.toml");
// If bookmarks.toml doesn't exist, initializae it
if self.context.is_some()
&& !self
.context
.as_ref()
.unwrap()
.local
.file_exists(p.as_path())
{
// Write file
let default_hosts: UserHosts = Default::default();
match self
.context
.as_ref()
.unwrap()
.local
.open_file_write(p.as_path())
{
Ok(writer) => {
let serializer: BookmarkSerializer = BookmarkSerializer {};
// Serialize and write
if let Err(err) = serializer.serialize(Box::new(writer), &default_hosts) {
self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Yellow,
format!(
"Could not write default bookmarks at \"{}\": {}",
p.display(),
err
),
));
return None;
}
}
Err(err) => { Err(err) => {
self.input_mode = InputMode::Popup(PopupType::Alert( self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Yellow, Color::Red,
format!( format!("Could not initialize configuration directory: {}", err),
"Could not write default bookmarks at \"{}\": {}", ))
p.display(),
err
),
));
return None;
} }
} }
} }
// return path
Some(p)
} else {
None
}
}
} }

View File

@@ -234,14 +234,14 @@ impl AuthActivity {
// Move bookmarks index up // Move bookmarks index up
if self.bookmarks_idx > 0 { if self.bookmarks_idx > 0 {
self.bookmarks_idx -= 1; self.bookmarks_idx -= 1;
} else if let Some(hosts) = &self.bookmarks { } else if let Some(bookmarks_cli) = &self.bookmarks_client {
// Put to last index (wrap) // Put to last index (wrap)
self.bookmarks_idx = hosts.bookmarks.len() - 1; self.bookmarks_idx = bookmarks_cli.iter_bookmarks().count() - 1;
} }
} }
KeyCode::Down => { KeyCode::Down => {
if let Some(hosts) = &self.bookmarks { if let Some(bookmarks_cli) = &self.bookmarks_client {
let size: usize = hosts.bookmarks.len(); let size: usize = bookmarks_cli.iter_bookmarks().count();
// Check if can move down // Check if can move down
if self.bookmarks_idx + 1 >= size { if self.bookmarks_idx + 1 >= size {
// Move bookmarks index down // Move bookmarks index down
@@ -315,14 +315,14 @@ impl AuthActivity {
// Move bookmarks index up // Move bookmarks index up
if self.recents_idx > 0 { if self.recents_idx > 0 {
self.recents_idx -= 1; self.recents_idx -= 1;
} else if let Some(hosts) = &self.bookmarks { } else if let Some(bookmarks_cli) = &self.bookmarks_client {
// Put to last index (wrap) // Put to last index (wrap)
self.recents_idx = hosts.recents.len() - 1; self.recents_idx = bookmarks_cli.iter_recents().count() - 1;
} }
} }
KeyCode::Down => { KeyCode::Down => {
if let Some(hosts) = &self.bookmarks { if let Some(bookmarks_cli) = &self.bookmarks_client {
let size: usize = hosts.recents.len(); let size: usize = bookmarks_cli.iter_recents().count();
// Check if can move down // Check if can move down
if self.recents_idx + 1 >= size { if self.recents_idx + 1 >= size {
// Move bookmarks index down // Move bookmarks index down

View File

@@ -27,8 +27,6 @@ use super::{
AuthActivity, Context, DialogYesNoOption, FileTransferProtocol, InputField, InputForm, AuthActivity, Context, DialogYesNoOption, FileTransferProtocol, InputField, InputForm,
InputMode, PopupType, InputMode, PopupType,
}; };
use crate::bookmarks::Bookmark;
use crate::utils::align_text_center; use crate::utils::align_text_center;
use tui::{ use tui::{
@@ -273,21 +271,26 @@ impl AuthActivity {
/// ///
/// Draw local explorer list /// Draw local explorer list
pub(super) fn draw_bookmarks_tab(&self) -> Option<List> { pub(super) fn draw_bookmarks_tab(&self) -> Option<List> {
self.bookmarks.as_ref()?; self.bookmarks_client.as_ref()?;
let hosts: Vec<ListItem> = self let hosts: Vec<ListItem> = self
.bookmarks .bookmarks_client
.as_ref() .as_ref()
.unwrap() .unwrap()
.bookmarks .iter_bookmarks()
.iter() .map(|key: &String| {
.map(|(key, entry): (&String, &Bookmark)| { let entry: (String, u16, FileTransferProtocol, String, _) = self
.bookmarks_client
.as_ref()
.unwrap()
.get_bookmark(key)
.unwrap();
ListItem::new(Span::from(format!( ListItem::new(Span::from(format!(
"{} ({}://{}@{}:{})", "{} ({}://{}@{}:{})",
key, key,
entry.protocol.to_lowercase(), AuthActivity::protocol_to_str(entry.2),
entry.username, entry.3,
entry.address, entry.0,
entry.port entry.1
))) )))
}) })
.collect(); .collect();
@@ -316,20 +319,25 @@ impl AuthActivity {
/// ///
/// Draw local explorer list /// Draw local explorer list
pub(super) fn draw_recents_tab(&self) -> Option<List> { pub(super) fn draw_recents_tab(&self) -> Option<List> {
self.bookmarks.as_ref()?; self.bookmarks_client.as_ref()?;
let hosts: Vec<ListItem> = self let hosts: Vec<ListItem> = self
.bookmarks .bookmarks_client
.as_ref() .as_ref()
.unwrap() .unwrap()
.recents .iter_recents()
.values() .map(|key: &String| {
.map(|entry: &Bookmark| { let entry: (String, u16, FileTransferProtocol, String, _) = self
.bookmarks_client
.as_ref()
.unwrap()
.get_bookmark(key)
.unwrap();
ListItem::new(Span::from(format!( ListItem::new(Span::from(format!(
"{}://{}@{}:{}", "{}://{}@{}:{}",
entry.protocol.to_lowercase(), AuthActivity::protocol_to_str(entry.2),
entry.username, entry.3,
entry.address, entry.0,
entry.port entry.1
))) )))
}) })
.collect(); .collect();
@@ -538,4 +546,18 @@ impl AuthActivity {
) )
.start_corner(Corner::TopLeft) .start_corner(Corner::TopLeft)
} }
/// ### protocol_to_str
///
/// Convert protocol to str for layouts
fn protocol_to_str(proto: FileTransferProtocol) -> &'static str {
match proto {
FileTransferProtocol::Ftp(secure) => match secure {
true => "ftps",
false => "ftp",
},
FileTransferProtocol::Scp => "scp",
FileTransferProtocol::Sftp => "sftp",
}
}
} }

View File

@@ -36,8 +36,8 @@ extern crate unicode_width;
// locals // locals
use super::{Activity, Context}; use super::{Activity, Context};
use crate::bookmarks::UserHosts;
use crate::filetransfer::FileTransferProtocol; use crate::filetransfer::FileTransferProtocol;
use crate::system::bookmarks_client::BookmarksClient;
// Includes // Includes
use crossterm::event::Event as InputEvent; use crossterm::event::Event as InputEvent;
@@ -111,7 +111,7 @@ pub struct AuthActivity {
pub submit: bool, // becomes true after user has submitted fields pub submit: bool, // becomes true after user has submitted fields
pub quit: bool, // Becomes true if user has pressed esc pub quit: bool, // Becomes true if user has pressed esc
context: Option<Context>, context: Option<Context>,
bookmarks: Option<UserHosts>, bookmarks_client: Option<BookmarksClient>,
selected_field: InputField, // Selected field in AuthCredentials Form selected_field: InputField, // Selected field in AuthCredentials Form
input_mode: InputMode, input_mode: InputMode,
input_form: InputForm, input_form: InputForm,
@@ -143,7 +143,7 @@ impl AuthActivity {
submit: false, submit: false,
quit: false, quit: false,
context: None, context: None,
bookmarks: None, bookmarks_client: None,
selected_field: InputField::Address, selected_field: InputField::Address,
input_mode: InputMode::Form, input_mode: InputMode::Form,
input_form: InputForm::AuthCredentials, input_form: InputForm::AuthCredentials,
@@ -171,9 +171,9 @@ impl Activity for AuthActivity {
// Put raw mode on enabled // Put raw mode on enabled
let _ = enable_raw_mode(); let _ = enable_raw_mode();
self.input_mode = InputMode::Form; self.input_mode = InputMode::Form;
// Read bookmarks // Init bookmarks client
if self.bookmarks.is_none() { if self.bookmarks_client.is_none() {
self.read_bookmarks(); self.init_bookmarks_client();
} }
} }