diff --git a/README.md b/README.md index 11de907..7f5e004 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,16 @@ A GTK3-based SSH client with a tabbed terminal interface, server sidebar, notifications, and SSH key storage. -![Icon](icons/x3-ssh.svg) +![Icon](icons/x3-ssh.png) ## Features - **Tabbed terminal** - Multiple SSH sessions in tabs -- **Server sidebar** - Saved servers displayed in a sidebar, double-click to connect +- **Server sidebar** - Saved servers in a sidebar, double-click to connect, right-click for Edit/Delete - **Quick connect** - Connect directly via host/user/port fields in the toolbar +- **Password storage** - Optionally save passwords with servers (stored in config file) - **SSH key management** - Generate, import, and manage SSH keys -- **Notifications** - Animated notification bar for status messages (saved, connected, disconnected, errors) +- **Notifications** - Animated notification bar for status messages - **VTE terminal** - Full terminal emulation with 256 color support - **Resizable pty** - Terminal auto-resizes the remote pty on window resize - **System integration** - `.desktop` file and icon for application menu entry @@ -63,11 +64,25 @@ SSH keys are stored in: ## Usage 1. Start the application: `x3-ssh` -2. **Server Sidebar**: Saved servers appear in the left sidebar. Double-click to connect. +2. **Server Sidebar**: Saved servers appear in the left sidebar + - **Double-click** to connect + - **Right-click** for context menu (Connect / Edit / Delete) + - Click `+` to add a new server 3. **Quick Connect**: Enter host, user, and port in the toolbar, click "Connect" -4. **Add Server**: Click the `+` button in the sidebar header, or use the "Add Server" dialog -5. **SSH Keys**: Click "Keys" in the toolbar to generate or import SSH keys -6. **Tabs**: Click "+ Tab" for a new empty tab, "Close Tab" to close the current one +4. **SSH Keys**: Click "Keys" in the toolbar to generate or import SSH keys +5. **Tabs**: Click "+ Tab" for a new empty tab, "Close Tab" to close the current one + +## Server Dialog Fields + +| Field | Description | +|-------|-------------| +| Name | Display name for the server | +| Host | Hostname or IP address | +| User | SSH username | +| Port | SSH port (default: 22) | +| Password | Optional, can be saved | +| Save password | Checkbox to store password in config | +| SSH Key | Optional key for public key authentication | ## Notifications @@ -90,7 +105,7 @@ Notifications auto-hide after 4 seconds. │ [Server▼] [host___] [user___] [port] [Connect] ...│ ├───────────┬────────────────────────────────────────┤ │ Servers │ [Tab: MyServer] [Tab: Prod] [+] │ -│ ─────── │ │ +│ +─────── │ │ │ MyServer │ user@host:~$ │ │ root@h:22 │ │ │ │ │ @@ -101,6 +116,15 @@ Notifications auto-hide after 4 seconds. └────────────────────────────────────────────────────┘ ``` +Right-click on a server in the sidebar: +``` +┌──────────┐ +│ Connect │ +│ Edit │ +│ Delete │ +└──────────┘ +``` + ## License MIT diff --git a/icons/x3-ssh.png b/icons/x3-ssh.png index f02eee6..dbf1638 100644 Binary files a/icons/x3-ssh.png and b/icons/x3-ssh.png differ diff --git a/src/server_store.cpp b/src/server_store.cpp index 0532caa..581795e 100644 --- a/src/server_store.cpp +++ b/src/server_store.cpp @@ -55,7 +55,9 @@ void ServerStore::load() { else if (key == "port") { try { current.port = std::stoi(val); } catch (...) { current.port = 22; } } else if (key == "user") current.user = val; else if (key == "keyfile") current.keyfile = val; + else if (key == "password") current.password = val; else if (key == "use_key") current.use_key = (val == "true"); + else if (key == "save_password") current.save_password = (val == "true"); } if (in_server && !current.name.empty()) { servers_.push_back(current); @@ -65,7 +67,7 @@ void ServerStore::load() { void ServerStore::save() const { std::string dir = config_path_.substr(0, config_path_.rfind('/')); std::string cmd = "mkdir -p \"" + dir + "\""; - system(cmd.c_str()); + (void)system(cmd.c_str()); std::ofstream f(config_path_); if (!f.is_open()) return; @@ -79,7 +81,9 @@ void ServerStore::save() const { f << "port=" << s.port << "\n"; f << "user=" << s.user << "\n"; f << "keyfile=" << s.keyfile << "\n"; - f << "use_key=" << (s.use_key ? "true" : "false") << "\n\n"; + f << "password=" << s.password << "\n"; + f << "use_key=" << (s.use_key ? "true" : "false") << "\n"; + f << "save_password=" << (s.save_password ? "true" : "false") << "\n\n"; } } diff --git a/src/server_store.h b/src/server_store.h index 0c055f1..9ec0ce0 100644 --- a/src/server_store.h +++ b/src/server_store.h @@ -9,7 +9,9 @@ struct ServerConfig { int port = 22; std::string user; std::string keyfile; + std::string password; bool use_key = false; + bool save_password = false; }; class ServerStore { diff --git a/src/ssh_client.cpp b/src/ssh_client.cpp index 5673587..20aa4ad 100644 --- a/src/ssh_client.cpp +++ b/src/ssh_client.cpp @@ -35,6 +35,7 @@ static void refresh_server_list_widget(GtkWidget* listbox, const std::vector