diff --git a/src/activity_manager.rs b/src/activity_manager.rs
new file mode 100644
index 0000000..041ca54
--- /dev/null
+++ b/src/activity_manager.rs
@@ -0,0 +1,106 @@
+//! ## ActivityManager
+//!
+//! `activity_manager` is the module which provides run methods and handling for activities
+
+/*
+*
+* Copyright (C) 2020 Christian Visintin - christian.visintin1997@gmail.com
+*
+* This file is part of "TermSCP"
+*
+* TermSCP is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* TermSCP is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with TermSCP. If not, see .
+*
+*/
+
+use std::path::PathBuf;
+
+// Deps
+use crate::filetransfer::FileTransfer;
+use crate::host::Localhost;
+use crate::ui::activities::{auth_activity::AuthActivity, auth_activity::ScpProtocol, Activity};
+use crate::ui::context::Context;
+
+/// ### NextActivity
+///
+/// NextActivity identified the next identity to run once the current has ended
+pub enum NextActivity {
+ Authentication,
+ FileTransfer,
+}
+
+/// ### FileTransferParams
+///
+/// Holds connection parameters for file transfers
+struct FileTransferParams {
+ address: String,
+ port: u16,
+ protocol: ScpProtocol,
+ username: Option,
+ password: Option,
+}
+
+/// ### ActivityManager
+///
+/// The activity manager takes care of running activities and handling them until the application has ended
+pub struct ActivityManager {
+ context: Context,
+ ftparams: Option,
+}
+
+impl ActivityManager {
+ /// ### new
+ ///
+ /// Initializes a new Activity Manager
+ pub fn new(client: Box, local_dir: &PathBuf) -> Result {
+ // Prepare Context
+ let host: Localhost = match Localhost::new(local_dir.clone()) {
+ Ok(h) => h,
+ Err(_) => return Err(()),
+ };
+ let ctx: Context = Context::new(client, host);
+ Ok(ActivityManager {
+ context: ctx,
+ ftparams: None,
+ })
+ }
+
+ /// ### set_filetransfer_params
+ ///
+ /// Set file transfer params
+ pub fn set_filetransfer_params(
+ &mut self,
+ address: String,
+ port: u16,
+ protocol: ScpProtocol,
+ username: Option,
+ password: Option,
+ ) {
+ self.ftparams = Some(FileTransferParams {
+ address: address,
+ port: port,
+ protocol: protocol,
+ username: username,
+ password: password,
+ });
+ }
+
+ /// ### run
+ ///
+ ///
+ /// Loop for activity manager. You need to provide the activity to start with
+ /// Returns the exitcode
+ pub fn run(&mut self, launch_activity: NextActivity) -> i32 {
+ 0
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 0f3bfdc..621a363 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@
*
*/
+pub mod activity_manager;
pub mod filetransfer;
pub mod fs;
pub mod host;
diff --git a/src/main.rs b/src/main.rs
index 20ee3b2..bb57dfe 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -28,31 +28,44 @@ extern crate getopts;
// External libs
use getopts::Options;
use std::env;
+use std::path::PathBuf;
+use std::time::Duration;
// Include
+mod activity_manager;
mod filetransfer;
mod fs;
mod host;
+mod ui;
+mod utils;
+
+// namespaces
+use activity_manager::{ActivityManager, NextActivity};
+use filetransfer::{FileTransfer, sftp_transfer::SftpFileTransfer};
+use ui::activities::auth_activity::ScpProtocol;
/// ### print_usage
///
/// Print usage
fn print_usage(opts: Options) {
- let brief = format!("Usage: termscp [Options]... Remote");
+ let brief = format!("Usage: termscp [Options]... [protocol:user@address:port]");
print!("{}", opts.usage(&brief));
}
fn main() {
let args: Vec = env::args().collect();
//Program CLI options
- // TODO: insert opts here
+ let mut address: Option = None; // None
+ let mut port: u16 = 22; // Default port
+ let mut username: Option = None; // Default username
+ let mut password: Option = None; // Default password
+ let mut protocol: ScpProtocol = ScpProtocol::Sftp; // Default protocol
+ let mut ticks: Duration = Duration::from_micros(50);
//Process options
+ // FIXME: there is no way to provide password from CLI atm
let mut opts = Options::new();
- // opts.optopt("c", "command", "Specify command to run. Shell returns after running the command", "");
- // opts.optopt("C", "config", "Specify YAML configuration file", "");
- // opts.optopt("l", "lang", "Specify shell language", "");
- // opts.optopt("s", "shell", "Force the shell binary path", "");
+ opts.optopt("T", "ticks", "Set UI ticks; default 50µs", "<µs>");
opts.optflag("v", "version", "");
opts.optflag("h", "help", "Print this menu");
let matches = match opts.parse(&args[1..]) {
@@ -75,6 +88,89 @@ fn main() {
);
std::process::exit(255);
}
- // TODO: ...
- std::process::exit(0);
+ // Match protocol first
+ if let Some(val) = matches.opt_str("P") {
+ match val.as_str() {
+ "sftp" => {
+ protocol = ScpProtocol::Sftp;
+ // Set port to 22 as default
+ port = 22;
+ // Set default username to current
+ username = Some(whoami::username());
+ }
+ "ftp" => {
+ protocol = ScpProtocol::Ftp;
+ // Set port to 21
+ port = 21;
+ }
+ _ => {
+ eprintln!("Unknown protocol '{}'", val);
+ print_usage(opts);
+ std::process::exit(255);
+ }
+ }
+ }
+ // Match ticks
+ if let Some(val) = matches.opt_str("T") {
+ match val.parse::() {
+ Ok(val) => ticks = Duration::from_micros(val as u64),
+ Err(_) => {
+ eprintln!("Ticks is not a number '{}'", val);
+ print_usage(opts);
+ std::process::exit(255);
+ }
+ }
+ }
+ // Check free args
+ let extra_args: Vec = matches.free.clone();
+ if let Some(remote) = extra_args.get(0) {
+ // Parse address
+ match utils::parse_remote_opt(remote) {
+ Ok((addr, portn, proto, user)) => {
+ // Set params
+ address = Some(addr);
+ port = portn;
+ protocol = proto;
+ username = user;
+ },
+ Err(err) => {
+ eprintln!("Bad address option: {}", err);
+ print_usage(opts);
+ std::process::exit(255);
+ }
+ }
+ }
+ // Prepare file transfer
+ let file_transfer: Box = match protocol {
+ ScpProtocol::Sftp => Box::new(SftpFileTransfer::new()),
+ _ => {
+ // FIXME: complete with ftp client
+ eprintln!("Unsupported protocol!");
+ std::process::exit(255);
+ }
+ };
+ // Get working directory
+ let wrkdir: PathBuf = match env::current_dir() {
+ Ok(dir) => dir,
+ Err(_) => PathBuf::from("/")
+ };
+ // Create activity manager
+ let mut manager: ActivityManager = match ActivityManager::new(file_transfer, &wrkdir) {
+ Ok(m) => m,
+ Err(_) => {
+ eprintln!("Invalid directory '{}'", wrkdir.display());
+ std::process::exit(255);
+ }
+ };
+ // Initialize client if necessary
+ let mut start_activity: NextActivity = NextActivity::Authentication;
+ if let Some(address) = address {
+ // In this case the first activity will be FileTransfer
+ start_activity = NextActivity::FileTransfer;
+ manager.set_filetransfer_params(address, port, protocol, username, password);
+ }
+ // Run
+ let rc: i32 = manager.run(start_activity);
+ // Then return
+ std::process::exit(rc);
}
diff --git a/src/ui/activities/auth_activity.rs b/src/ui/activities/auth_activity.rs
index 2c254e0..182cc72 100644
--- a/src/ui/activities/auth_activity.rs
+++ b/src/ui/activities/auth_activity.rs
@@ -33,9 +33,14 @@ use super::{Activity, Context};
// Includes
use crossterm::event::Event as InputEvent;
use crossterm::event::{KeyCode, KeyEvent};
-use tui::layout;
-use tui::style;
-use tui::widgets;
+use tui::{
+ backend::CrosstermBackend,
+ layout::{Constraint, Direction, Layout},
+ style::{Color, Modifier, Style},
+ text::{Span, Spans, Text},
+ widgets::{Block, Borders, List, ListItem, Paragraph},
+ Terminal,
+};
/// ### InputField
///
@@ -52,6 +57,7 @@ enum InputField {
/// ### ScpProtocol
///
/// ScpProtocol describes the communication protocol selected by the user to communicate with the remote
+#[derive(std::cmp::PartialEq, std::fmt::Debug)]
pub enum ScpProtocol {
Sftp,
Ftp,
@@ -147,6 +153,7 @@ impl Activity for AuthActivity {
}
// Everything OK, set enter
self.form_submit = true;
+ popup = Some(format!("Connecting to {}:{}...", self.address, self.port));
}
KeyCode::Backspace => {
// Pop last char
@@ -225,7 +232,40 @@ impl Activity for AuthActivity {
}
}
}
- // TODO: draw interface
+ // draw interface
+ context.terminal.draw(|f| {
+ let chunks = Layout::default()
+ .direction(Direction::Vertical)
+ .margin(2)
+ .constraints(
+ [
+ Constraint::Length(1),
+ Constraint::Length(3),
+ Constraint::Min(1),
+ ]
+ .as_ref(),
+ )
+ .split(f.size());
+ // Write header
+ let (header, h_style) = (
+ vec![
+ Span::raw("Press "),
+ Span::styled("", Style::default().add_modifier(Modifier::BOLD)),
+ Span::raw(" to exit, "),
+ Span::styled("", Style::default().add_modifier(Modifier::BOLD)),
+ Span::raw(" to change input field,"),
+ Span::styled("", Style::default().add_modifier(Modifier::BOLD)),
+ Span::raw(" to submit form"),
+ ],
+ Style::default().add_modifier(Modifier::RAPID_BLINK),
+ );
+ let mut header_text = Text::from(Spans::from(header));
+ header_text.patch_style(h_style);
+ let header_message = Paragraph::new(header_text);
+ f.render_widget(header_message, chunks[0]);
+ // Create input fields
+ // TODO:
+ });
}
/// ### on_destroy