2020-11-22 15:32:44 +01:00
|
|
|
//! ## ActivityManager
|
|
|
|
//!
|
|
|
|
//! `activity_manager` is the module which provides run methods and handling for activities
|
|
|
|
|
2021-03-26 22:25:10 +01:00
|
|
|
/**
|
|
|
|
* MIT License
|
|
|
|
*
|
|
|
|
* termscp - Copyright (c) 2021 Christian Visintin
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
|
|
* copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
|
|
|
*/
|
2020-11-22 15:32:44 +01:00
|
|
|
// Deps
|
2021-07-23 14:31:29 +02:00
|
|
|
use crate::filetransfer::{FileTransferParams, FileTransferProtocol};
|
2021-01-16 18:02:12 +01:00
|
|
|
use crate::host::{HostError, Localhost};
|
2021-03-08 12:01:40 +01:00
|
|
|
use crate::system::config_client::ConfigClient;
|
|
|
|
use crate::system::environment;
|
2021-07-04 11:50:32 +02:00
|
|
|
use crate::system::theme_provider::ThemeProvider;
|
2020-11-24 22:05:11 +01:00
|
|
|
use crate::ui::activities::{
|
2021-05-08 19:28:47 +02:00
|
|
|
auth::AuthActivity, filetransfer::FileTransferActivity, setup::SetupActivity, Activity,
|
|
|
|
ExitReason,
|
2020-11-24 22:05:11 +01:00
|
|
|
};
|
2021-07-23 14:31:29 +02:00
|
|
|
use crate::ui::context::Context;
|
2020-11-22 15:32:44 +01:00
|
|
|
|
2020-11-23 16:59:30 +01:00
|
|
|
// Namespaces
|
2021-03-27 11:41:47 +01:00
|
|
|
use std::path::{Path, PathBuf};
|
2020-11-23 16:59:30 +01:00
|
|
|
use std::thread::sleep;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
2020-11-22 15:32:44 +01:00
|
|
|
/// ### NextActivity
|
|
|
|
///
|
|
|
|
/// NextActivity identified the next identity to run once the current has ended
|
|
|
|
pub enum NextActivity {
|
|
|
|
Authentication,
|
|
|
|
FileTransfer,
|
2020-12-25 16:41:49 +01:00
|
|
|
SetupActivity,
|
2020-11-22 15:32:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// ### ActivityManager
|
|
|
|
///
|
|
|
|
/// The activity manager takes care of running activities and handling them until the application has ended
|
|
|
|
pub struct ActivityManager {
|
2020-11-29 11:33:59 +01:00
|
|
|
context: Option<Context>,
|
2020-11-23 16:59:30 +01:00
|
|
|
interval: Duration,
|
2021-05-06 22:16:38 +02:00
|
|
|
local_dir: PathBuf,
|
2020-11-22 15:32:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ActivityManager {
|
|
|
|
/// ### new
|
|
|
|
///
|
|
|
|
/// Initializes a new Activity Manager
|
2021-03-27 11:41:47 +01:00
|
|
|
pub fn new(local_dir: &Path, interval: Duration) -> Result<ActivityManager, HostError> {
|
2020-11-22 15:32:44 +01:00
|
|
|
// Prepare Context
|
2021-03-08 12:01:40 +01:00
|
|
|
// Initialize configuration client
|
2021-07-08 15:07:24 +02:00
|
|
|
let (config_client, error): (ConfigClient, Option<String>) =
|
2021-03-08 12:01:40 +01:00
|
|
|
match Self::init_config_client() {
|
2021-07-08 15:07:24 +02:00
|
|
|
Ok(cli) => (cli, None),
|
2021-05-16 15:09:17 +02:00
|
|
|
Err(err) => {
|
|
|
|
error!("Failed to initialize config client: {}", err);
|
2021-07-08 15:07:24 +02:00
|
|
|
(ConfigClient::degraded(), Some(err))
|
2021-05-16 15:09:17 +02:00
|
|
|
}
|
2021-03-08 12:01:40 +01:00
|
|
|
};
|
2021-07-04 11:50:32 +02:00
|
|
|
let theme_provider: ThemeProvider = Self::init_theme_provider();
|
|
|
|
let ctx: Context = Context::new(config_client, theme_provider, error);
|
2020-11-22 15:32:44 +01:00
|
|
|
Ok(ActivityManager {
|
2020-11-29 11:33:59 +01:00
|
|
|
context: Some(ctx),
|
2021-05-06 22:16:38 +02:00
|
|
|
local_dir: local_dir.to_path_buf(),
|
2020-12-12 12:14:51 +01:00
|
|
|
interval,
|
2020-11-22 15:32:44 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ### set_filetransfer_params
|
|
|
|
///
|
|
|
|
/// Set file transfer params
|
2021-07-23 14:31:29 +02:00
|
|
|
pub fn set_filetransfer_params(&mut self, params: FileTransferParams) {
|
2021-03-21 16:38:11 +01:00
|
|
|
// Put params into the context
|
2021-07-23 14:31:29 +02:00
|
|
|
self.context.as_mut().unwrap().set_ftparams(params);
|
2020-11-22 15:32:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// ### run
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// Loop for activity manager. You need to provide the activity to start with
|
|
|
|
/// Returns the exitcode
|
2020-11-23 16:59:30 +01:00
|
|
|
pub fn run(&mut self, launch_activity: NextActivity) {
|
|
|
|
let mut current_activity: Option<NextActivity> = Some(launch_activity);
|
|
|
|
loop {
|
|
|
|
current_activity = match current_activity {
|
|
|
|
Some(activity) => match activity {
|
|
|
|
NextActivity::Authentication => self.run_authentication(),
|
2020-11-24 22:05:11 +01:00
|
|
|
NextActivity::FileTransfer => self.run_filetransfer(),
|
2020-12-25 16:41:49 +01:00
|
|
|
NextActivity::SetupActivity => self.run_setup(),
|
2020-11-23 16:59:30 +01:00
|
|
|
},
|
|
|
|
None => break, // Exit
|
|
|
|
}
|
|
|
|
}
|
2020-12-06 10:55:15 +01:00
|
|
|
// Drop context
|
|
|
|
drop(self.context.take());
|
2020-11-23 16:59:30 +01:00
|
|
|
}
|
|
|
|
|
2021-03-08 12:01:40 +01:00
|
|
|
// -- Activity Loops
|
2020-11-23 16:59:30 +01:00
|
|
|
|
|
|
|
/// ### run_authentication
|
|
|
|
///
|
|
|
|
/// Loop for Authentication activity.
|
|
|
|
/// Returns when activity terminates.
|
|
|
|
/// Returns the next activity to run
|
|
|
|
fn run_authentication(&mut self) -> Option<NextActivity> {
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("Starting AuthActivity...");
|
2020-11-23 16:59:30 +01:00
|
|
|
// Prepare activity
|
2020-12-25 16:41:49 +01:00
|
|
|
let mut activity: AuthActivity = AuthActivity::default();
|
2020-11-23 16:59:30 +01:00
|
|
|
// Prepare result
|
2020-11-24 15:13:36 +01:00
|
|
|
let result: Option<NextActivity>;
|
2020-11-29 11:33:59 +01:00
|
|
|
// Get context
|
|
|
|
let ctx: Context = match self.context.take() {
|
|
|
|
Some(ctx) => ctx,
|
2021-05-16 15:09:17 +02:00
|
|
|
None => {
|
|
|
|
error!("Failed to start AuthActivity: context is None");
|
|
|
|
return None;
|
|
|
|
}
|
2020-11-29 11:33:59 +01:00
|
|
|
};
|
2020-11-23 16:59:30 +01:00
|
|
|
// Create activity
|
2020-11-29 11:33:59 +01:00
|
|
|
activity.on_create(ctx);
|
2020-11-23 16:59:30 +01:00
|
|
|
loop {
|
|
|
|
// Draw activity
|
2020-11-29 11:33:59 +01:00
|
|
|
activity.on_draw();
|
2020-11-23 16:59:30 +01:00
|
|
|
// Check if has to be terminated
|
2021-03-21 17:16:52 +01:00
|
|
|
if let Some(exit_reason) = activity.will_umount() {
|
|
|
|
match exit_reason {
|
|
|
|
ExitReason::Quit => {
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("AuthActivity terminated due to 'Quit'");
|
2021-03-21 17:16:52 +01:00
|
|
|
result = None;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ExitReason::EnterSetup => {
|
|
|
|
// User requested activity
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("AuthActivity terminated due to 'EnterSetup'");
|
2021-03-21 17:16:52 +01:00
|
|
|
result = Some(NextActivity::SetupActivity);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ExitReason::Connect => {
|
|
|
|
// User submitted, set next activity
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("AuthActivity terminated due to 'Connect'");
|
2021-03-21 17:16:52 +01:00
|
|
|
result = Some(NextActivity::FileTransfer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => { /* Nothing to do */ }
|
|
|
|
}
|
2020-11-24 22:05:11 +01:00
|
|
|
}
|
|
|
|
// Sleep for ticks
|
|
|
|
sleep(self.interval);
|
|
|
|
}
|
|
|
|
// Destroy activity
|
2020-11-29 11:33:59 +01:00
|
|
|
self.context = activity.on_destroy();
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("AuthActivity destroyed");
|
2020-11-24 22:05:11 +01:00
|
|
|
result
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ### run_filetransfer
|
|
|
|
///
|
|
|
|
/// Loop for FileTransfer activity.
|
|
|
|
/// Returns when activity terminates.
|
|
|
|
/// Returns the next activity to run
|
|
|
|
fn run_filetransfer(&mut self) -> Option<NextActivity> {
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("Starting FileTransferActivity");
|
2020-11-29 11:33:59 +01:00
|
|
|
// Get context
|
2021-05-06 22:16:38 +02:00
|
|
|
let mut ctx: Context = match self.context.take() {
|
2020-11-29 11:33:59 +01:00
|
|
|
Some(ctx) => ctx,
|
2021-05-16 15:09:17 +02:00
|
|
|
None => {
|
|
|
|
error!("Failed to start FileTransferActivity: context is None");
|
|
|
|
return None;
|
|
|
|
}
|
2020-11-29 11:33:59 +01:00
|
|
|
};
|
2021-03-21 16:38:11 +01:00
|
|
|
// If ft params is None, return None
|
2021-07-08 15:43:23 +02:00
|
|
|
let ft_params: &FileTransferParams = match ctx.ft_params() {
|
2021-08-10 22:27:20 +02:00
|
|
|
Some(ft_params) => ft_params,
|
2021-05-16 15:09:17 +02:00
|
|
|
None => {
|
|
|
|
error!("Failed to start FileTransferActivity: file transfer params is None");
|
|
|
|
return None;
|
|
|
|
}
|
2021-03-21 16:38:11 +01:00
|
|
|
};
|
|
|
|
// Prepare activity
|
|
|
|
let protocol: FileTransferProtocol = ft_params.protocol;
|
2021-05-06 22:16:38 +02:00
|
|
|
let host: Localhost = match Localhost::new(self.local_dir.clone()) {
|
|
|
|
Ok(host) => host,
|
|
|
|
Err(err) => {
|
|
|
|
// Set error in context
|
2021-05-16 15:09:17 +02:00
|
|
|
error!("Failed to initialize localhost: {}", err);
|
2021-05-06 22:16:38 +02:00
|
|
|
ctx.set_error(format!("Could not initialize localhost: {}", err));
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut activity: FileTransferActivity = FileTransferActivity::new(host, protocol);
|
2021-03-21 16:38:11 +01:00
|
|
|
// Prepare result
|
|
|
|
let result: Option<NextActivity>;
|
2020-11-24 22:05:11 +01:00
|
|
|
// Create activity
|
2020-11-29 11:33:59 +01:00
|
|
|
activity.on_create(ctx);
|
2020-11-24 22:05:11 +01:00
|
|
|
loop {
|
|
|
|
// Draw activity
|
2020-11-29 11:33:59 +01:00
|
|
|
activity.on_draw();
|
2020-11-24 22:05:11 +01:00
|
|
|
// Check if has to be terminated
|
2021-03-21 17:16:52 +01:00
|
|
|
if let Some(exit_reason) = activity.will_umount() {
|
|
|
|
match exit_reason {
|
|
|
|
ExitReason::Quit => {
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("FileTransferActivity terminated due to 'Quit'");
|
2021-03-21 17:16:52 +01:00
|
|
|
result = None;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ExitReason::Disconnect => {
|
|
|
|
// User disconnected, set next activity to authentication
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("FileTransferActivity terminated due to 'Authentication'");
|
2021-03-21 17:16:52 +01:00
|
|
|
result = Some(NextActivity::Authentication);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => { /* Nothing to do */ }
|
|
|
|
}
|
2020-11-23 16:59:30 +01:00
|
|
|
}
|
|
|
|
// Sleep for ticks
|
2020-11-24 15:13:36 +01:00
|
|
|
sleep(self.interval);
|
2020-11-23 16:59:30 +01:00
|
|
|
}
|
|
|
|
// Destroy activity
|
2020-11-29 11:33:59 +01:00
|
|
|
self.context = activity.on_destroy();
|
2020-11-23 16:59:30 +01:00
|
|
|
result
|
2020-11-22 15:32:44 +01:00
|
|
|
}
|
2020-12-25 16:41:49 +01:00
|
|
|
|
|
|
|
/// ### run_setup
|
|
|
|
///
|
|
|
|
/// `SetupActivity` run loop.
|
|
|
|
/// Returns when activity terminates.
|
|
|
|
/// Returns the next activity to run
|
|
|
|
fn run_setup(&mut self) -> Option<NextActivity> {
|
|
|
|
// Prepare activity
|
|
|
|
let mut activity: SetupActivity = SetupActivity::default();
|
|
|
|
// Get context
|
|
|
|
let ctx: Context = match self.context.take() {
|
|
|
|
Some(ctx) => ctx,
|
2021-05-16 15:09:17 +02:00
|
|
|
None => {
|
|
|
|
error!("Failed to start SetupActivity: context is None");
|
|
|
|
return None;
|
|
|
|
}
|
2020-12-25 16:41:49 +01:00
|
|
|
};
|
|
|
|
// Create activity
|
|
|
|
activity.on_create(ctx);
|
|
|
|
loop {
|
|
|
|
// Draw activity
|
|
|
|
activity.on_draw();
|
|
|
|
// Check if activity has terminated
|
2021-03-21 17:16:52 +01:00
|
|
|
if let Some(ExitReason::Quit) = activity.will_umount() {
|
2021-05-16 15:09:17 +02:00
|
|
|
info!("SetupActivity terminated due to 'Quit'");
|
2020-12-25 16:41:49 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Sleep for ticks
|
|
|
|
sleep(self.interval);
|
|
|
|
}
|
|
|
|
// Destroy activity
|
|
|
|
self.context = activity.on_destroy();
|
|
|
|
// This activity always returns to AuthActivity
|
|
|
|
Some(NextActivity::Authentication)
|
|
|
|
}
|
2021-03-08 12:01:40 +01:00
|
|
|
|
|
|
|
// -- misc
|
|
|
|
|
|
|
|
/// ### init_config_client
|
|
|
|
///
|
|
|
|
/// Initialize configuration client
|
|
|
|
fn init_config_client() -> Result<ConfigClient, String> {
|
|
|
|
// Get config dir
|
|
|
|
match environment::init_config_dir() {
|
|
|
|
Ok(config_dir) => {
|
|
|
|
match config_dir {
|
|
|
|
Some(config_dir) => {
|
|
|
|
// Get config client paths
|
|
|
|
let (config_path, ssh_dir): (PathBuf, PathBuf) =
|
|
|
|
environment::get_config_paths(config_dir.as_path());
|
|
|
|
match ConfigClient::new(config_path.as_path(), ssh_dir.as_path()) {
|
|
|
|
Ok(cli) => Ok(cli),
|
|
|
|
Err(err) => Err(format!("Could not read configuration: {}", err)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => Err(String::from(
|
2021-07-04 11:50:32 +02:00
|
|
|
"Your system doesn't provide a configuration directory",
|
2021-03-08 12:01:40 +01:00
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => Err(format!(
|
|
|
|
"Could not initialize configuration directory: {}",
|
|
|
|
err
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
2021-07-04 11:50:32 +02:00
|
|
|
|
|
|
|
fn init_theme_provider() -> ThemeProvider {
|
|
|
|
match environment::init_config_dir() {
|
|
|
|
Ok(config_dir) => {
|
|
|
|
match config_dir {
|
|
|
|
Some(config_dir) => {
|
|
|
|
// Get config client paths
|
|
|
|
let theme_path: PathBuf = environment::get_theme_path(config_dir.as_path());
|
|
|
|
match ThemeProvider::new(theme_path.as_path()) {
|
|
|
|
Ok(provider) => provider,
|
|
|
|
Err(err) => {
|
|
|
|
error!("Could not initialize theme provider with file '{}': {}; using theme provider in degraded mode", theme_path.display(), err);
|
|
|
|
ThemeProvider::degraded()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
error!("This system doesn't provide a configuration directory; using theme provider in degraded mode");
|
|
|
|
ThemeProvider::degraded()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
error!("Could not initialize configuration directory: {}; using theme provider in degraded mode", err);
|
|
|
|
ThemeProvider::degraded()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-11-22 15:32:44 +01:00
|
|
|
}
|