mirror of https://github.com/oupson/FooTerm.git
Store server in db, password in secret and display server list
This commit is contained in:
parent
7359a03f90
commit
d7b4940608
|
@ -12,7 +12,8 @@
|
||||||
"--share=ipc",
|
"--share=ipc",
|
||||||
"--socket=fallback-x11",
|
"--socket=fallback-x11",
|
||||||
"--device=dri",
|
"--device=dri",
|
||||||
"--socket=wayland"
|
"--socket=wayland",
|
||||||
|
"--talk-name=org.freedesktop.secrets"
|
||||||
],
|
],
|
||||||
"build-options" : {
|
"build-options" : {
|
||||||
"append-path" : "/usr/lib/sdk/vala/bin",
|
"append-path" : "/usr/lib/sdk/vala/bin",
|
||||||
|
|
|
@ -20,12 +20,14 @@
|
||||||
|
|
||||||
namespace Footerm.Model {
|
namespace Footerm.Model {
|
||||||
public class Server {
|
public class Server {
|
||||||
|
public int? id;
|
||||||
public string name;
|
public string name;
|
||||||
public string hostname;
|
public string hostname;
|
||||||
public ushort port;
|
public ushort port;
|
||||||
public string username;
|
public string username;
|
||||||
|
|
||||||
public Server(string name, string hostname, ushort port, string username) {
|
public Server(int? id, string name, string hostname, ushort port, string username) {
|
||||||
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
|
|
|
@ -52,6 +52,17 @@ namespace Footerm {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var config = Footerm.Services.Config.get_instance ();
|
var config = Footerm.Services.Config.get_instance ();
|
||||||
|
|
||||||
|
var stored_server_list = config.get_server_list ();
|
||||||
|
foreach (var server in stored_server_list) {
|
||||||
|
var action_row = new Adw.ActionRow ();
|
||||||
|
action_row.set_title (server.name);
|
||||||
|
action_row.set_activatable (true);
|
||||||
|
action_row.activated.connect (() => {
|
||||||
|
this.on_server_selected (server);
|
||||||
|
});
|
||||||
|
server_list.add (action_row);
|
||||||
|
}
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
GLib.warning ("Failed to read server list : %s", e.message);
|
GLib.warning ("Failed to read server list : %s", e.message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,11 @@
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesGroup">
|
<object class="AdwPreferencesGroup">
|
||||||
|
<child>
|
||||||
|
<object class="AdwEntryRow" id="name_entry">
|
||||||
|
<property name="title" translatable="yes">Name</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwEntryRow" id="hostname_entry">
|
<object class="AdwEntryRow" id="hostname_entry">
|
||||||
<property name="title" translatable="yes">Hostname</property>
|
<property name="title" translatable="yes">Hostname</property>
|
||||||
|
|
|
@ -22,6 +22,8 @@ namespace Footerm {
|
||||||
[GtkTemplate(ui = "/fr/oupson/FooTerm/newserver.ui")]
|
[GtkTemplate(ui = "/fr/oupson/FooTerm/newserver.ui")]
|
||||||
public class NewServer : Gtk.Box {
|
public class NewServer : Gtk.Box {
|
||||||
public signal void on_new_server(Footerm.Model.Server server);
|
public signal void on_new_server(Footerm.Model.Server server);
|
||||||
|
[GtkChild]
|
||||||
|
private unowned Adw.EntryRow name_entry;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private unowned Adw.EntryRow hostname_entry;
|
private unowned Adw.EntryRow hostname_entry;
|
||||||
|
@ -42,7 +44,8 @@ namespace Footerm {
|
||||||
add_server_button.clicked.connect(this.on_add_button_clicked);
|
add_server_button.clicked.connect(this.on_add_button_clicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_add_button_clicked() {
|
private async void on_add_button_clicked() {
|
||||||
|
var name = this.name_entry.get_text();
|
||||||
var hostname = this.hostname_entry.get_text();
|
var hostname = this.hostname_entry.get_text();
|
||||||
var port = int.parse(this.port_entry.get_text());
|
var port = int.parse(this.port_entry.get_text());
|
||||||
var username = this.username_entry.get_text();
|
var username = this.username_entry.get_text();
|
||||||
|
@ -52,7 +55,11 @@ namespace Footerm {
|
||||||
// Port is invalid
|
// Port is invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.on_new_server(new Footerm.Model.Server(hostname, (ushort)port, username, password));
|
var server = new Footerm.Model.Server(null, name, hostname, (ushort)port, username);
|
||||||
|
var config_service = Footerm.Services.Config.get_instance();
|
||||||
|
yield config_service.save_server(server, password);
|
||||||
|
|
||||||
|
this.on_new_server(server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ namespace Footerm.Services {
|
||||||
public List<Footerm.Model.Server> get_server_list() throws ConfigError {
|
public List<Footerm.Model.Server> get_server_list() throws ConfigError {
|
||||||
List<Footerm.Model.Server> list = new List<Footerm.Model.Server> ();
|
List<Footerm.Model.Server> list = new List<Footerm.Model.Server> ();
|
||||||
|
|
||||||
var stm_str = "SELECT (serverId, serverName, serverHostName, serverPort) FROM SERVER";
|
var stm_str = "SELECT serverId, serverName, serverHostName, serverPort, serverUsername FROM SERVER;";
|
||||||
Sqlite.Statement stm;
|
Sqlite.Statement stm;
|
||||||
var ec = this.db.prepare_v2(stm_str, stm_str.length, out stm);
|
var ec = this.db.prepare_v2(stm_str, stm_str.length, out stm);
|
||||||
if (ec != Sqlite.OK) {
|
if (ec != Sqlite.OK) {
|
||||||
|
@ -119,10 +119,48 @@ namespace Footerm.Services {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (stm.step() == Sqlite.ROW) {
|
while (stm.step() == Sqlite.ROW) {
|
||||||
list.append(new Footerm.Model.Server(stm.column_text(0), stm.column_text(1), (ushort) stm.column_int(2), stm.column_text(3)));
|
list.append(new Footerm.Model.Server(stm.column_int(0), stm.column_text(1), stm.column_text(2), (ushort) stm.column_int(3), stm.column_text(4)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void save_server(Footerm.Model.Server server, string password) throws ConfigError, SecretError, Error {
|
||||||
|
var secrets = Secrets.get_instance();
|
||||||
|
yield secrets.store_password(server, password);
|
||||||
|
|
||||||
|
var stm_str = "INSERT INTO SERVER (serverName, serverHostName, serverPort, serverUsername, serverAuthentificationType) VALUES(?, ?, ?, ?, 'password')";
|
||||||
|
Sqlite.Statement stm;
|
||||||
|
var ec = this.db.prepare_v2(stm_str, stm_str.length, out stm);
|
||||||
|
if (ec != Sqlite.OK) {
|
||||||
|
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
|
||||||
|
}
|
||||||
|
|
||||||
|
ec = stm.bind_text(1, server.name);
|
||||||
|
if (ec != Sqlite.OK) {
|
||||||
|
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
|
||||||
|
}
|
||||||
|
ec = stm.bind_text(2, server.hostname);
|
||||||
|
if (ec != Sqlite.OK) {
|
||||||
|
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
|
||||||
|
}
|
||||||
|
ec = stm.bind_int(3, server.port);
|
||||||
|
if (ec != Sqlite.OK) {
|
||||||
|
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
|
||||||
|
}
|
||||||
|
ec = stm.bind_text(4, server.username);
|
||||||
|
if (ec != Sqlite.OK) {
|
||||||
|
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
|
||||||
|
}
|
||||||
|
|
||||||
|
ec = stm.step();
|
||||||
|
stm.reset();
|
||||||
|
|
||||||
|
if (ec != Sqlite.DONE) {
|
||||||
|
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
|
||||||
|
}
|
||||||
|
|
||||||
|
server.id = (int)this.db.last_insert_rowid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* Secrets.vala
|
||||||
|
*
|
||||||
|
* Copyright 2023 oupson
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*
|
||||||
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Footerm.Services {
|
||||||
|
public errordomain SecretError {
|
||||||
|
FAILED_TO_STORE
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Secrets {
|
||||||
|
private static Secrets instance = null;
|
||||||
|
|
||||||
|
public static Secrets get_instance() {
|
||||||
|
if (Secrets.instance == null) {
|
||||||
|
Secrets.instance = new Secrets();
|
||||||
|
}
|
||||||
|
return Secrets.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Secret.Schema password_schema;
|
||||||
|
|
||||||
|
private Secrets() {
|
||||||
|
this.password_schema = new Secret.Schema("fr.oupson.FooTerm.Password", Secret.SchemaFlags.NONE,
|
||||||
|
"hostname", Secret.SchemaAttributeType.STRING,
|
||||||
|
"port", Secret.SchemaAttributeType.INTEGER,
|
||||||
|
"username", Secret.SchemaAttributeType.STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void store_password(Footerm.Model.Server server, string password) throws SecretError, Error {
|
||||||
|
var attributes = new GLib.HashTable<string, string> (str_hash, str_equal);
|
||||||
|
attributes["hostname"] = server.hostname;
|
||||||
|
attributes["port"] = server.port.to_string();
|
||||||
|
attributes["username"] = server.username;
|
||||||
|
|
||||||
|
var res = yield Secret.password_storev(this.password_schema, attributes, Secret.COLLECTION_DEFAULT, @"$(server.username)@$(server.hostname):$(server.port)", password, null);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
throw new SecretError.FAILED_TO_STORE("Failed to store the password");
|
||||||
|
}
|
||||||
|
debug("Password is stored");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async string get_password(Footerm.Model.Server server) throws Error {
|
||||||
|
var attributes = new GLib.HashTable<string, string> (str_hash, str_equal);
|
||||||
|
attributes["hostname"] = server.hostname;
|
||||||
|
attributes["port"] = server.port.to_string();
|
||||||
|
attributes["username"] = server.username;
|
||||||
|
|
||||||
|
return yield Secret.password_lookupv(this.password_schema, attributes, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
footerm_sources += files(
|
footerm_sources += files(
|
||||||
'Config.vala'
|
'Config.vala',
|
||||||
|
'Secrets.vala'
|
||||||
)
|
)
|
|
@ -34,7 +34,13 @@ namespace Footerm {
|
||||||
public TerminalPane(Footerm.Model.Server server) {
|
public TerminalPane(Footerm.Model.Server server) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.terminal.set_enable_sixel(true);
|
this.terminal.set_enable_sixel(true);
|
||||||
this.connect_to_server();
|
this.connect_to_server.begin((obj, res) => {
|
||||||
|
try {
|
||||||
|
this.connect_to_server.end(res);
|
||||||
|
} catch (Error e) {
|
||||||
|
warning("Failed to connect to the server : %s", e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
this.terminal.char_size_changed.connect(() => {
|
this.terminal.char_size_changed.connect(() => {
|
||||||
int rows = 0;
|
int rows = 0;
|
||||||
int columns = 0;
|
int columns = 0;
|
||||||
|
@ -51,7 +57,7 @@ namespace Footerm {
|
||||||
// stdout.printf("all done!\n");)
|
// stdout.printf("all done!\n");)
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connect_to_server() throws GLib.IOError, GLib.Error {
|
private async void connect_to_server() throws GLib.IOError, GLib.Error {
|
||||||
var addrs = new NetworkAddress(this.server.hostname, this.server.port);
|
var addrs = new NetworkAddress(this.server.hostname, this.server.port);
|
||||||
var addr = addrs.enumerate().next();
|
var addr = addrs.enumerate().next();
|
||||||
this.socket = new Socket(addr.get_family(), SocketType.STREAM, SocketProtocol.TCP);
|
this.socket = new Socket(addr.get_family(), SocketType.STREAM, SocketProtocol.TCP);
|
||||||
|
@ -73,7 +79,10 @@ namespace Footerm {
|
||||||
stdout.printf("\n");
|
stdout.printf("\n");
|
||||||
|
|
||||||
// TODO QUERY PASSWORD FROM libsecret
|
// TODO QUERY PASSWORD FROM libsecret
|
||||||
if (session.auth_password(this.server.username, null) != SSH2.Error.NONE) {
|
var secrets = Footerm.Services.Secrets.get_instance();
|
||||||
|
var password = yield secrets.get_password(this.server);
|
||||||
|
|
||||||
|
if (session.auth_password(this.server.username, password) != SSH2.Error.NONE) {
|
||||||
stdout.printf("\tAuthentication by password failed!\n");
|
stdout.printf("\tAuthentication by password failed!\n");
|
||||||
session.disconnect("Normal Shutdown, Thank you for playing");
|
session.disconnect("Normal Shutdown, Thank you for playing");
|
||||||
session = null;
|
session = null;
|
||||||
|
|
Loading…
Reference in New Issue