working on sftp

This commit is contained in:
oupson 2022-03-04 08:49:21 +01:00
parent d804da8881
commit 9f6dc1f3c2
11 changed files with 269 additions and 41 deletions

View File

@ -24,7 +24,7 @@ include_directories(
includes includes
) )
add_executable(FooTerm src/main.cpp src/term_windows.cpp src/vte.cpp src/ssh.cpp src/eventloop.cpp) add_executable(FooTerm src/main.cpp src/term_windows.cpp src/vte.cpp src/ssh.cpp src/eventloop.cpp src/panel.cpp includes/panel.hpp)
target_link_libraries(FooTerm target_link_libraries(FooTerm
${GTKMM_LIBRARIES}) ${GTKMM_LIBRARIES})
@ -32,8 +32,7 @@ target_link_libraries(FooTerm
target_link_libraries(FooTerm ${VTE_LIBRARIES}) target_link_libraries(FooTerm ${VTE_LIBRARIES})
add_definitions(${VTE_CFLAGS} ${VTE_CFLAGS_OTHER}) add_definitions(${VTE_CFLAGS} ${VTE_CFLAGS_OTHER})
add_subdirectory(third-party) add_subdirectory(third-party)
find_package(fmt) find_package(fmt)
target_link_libraries(FooTerm fmt::fmt ssh2) target_link_libraries(FooTerm fmt::fmt ssh2 pthread util)

View File

@ -9,7 +9,9 @@
#include <sys/poll.h> #include <sys/poll.h>
#include <unistd.h> #include <unistd.h>
#include <libssh2.h> #include <libssh2.h>
#include <libssh2_sftp.h>
class FooTermWindow;
struct EventLoopEntry { struct EventLoopEntry {
enum { enum {
@ -17,8 +19,14 @@ struct EventLoopEntry {
CHANNEL CHANNEL
} entryType; } entryType;
union {
LIBSSH2_CHANNEL *channel; LIBSSH2_CHANNEL *channel;
int out; LIBSSH2_SFTP *sftp;
};
union {
int fdout;
};
}; };
class EventLoop { class EventLoop {
@ -26,12 +34,14 @@ private:
std::vector<pollfd> pfds; std::vector<pollfd> pfds;
std::vector<EventLoopEntry> outs; std::vector<EventLoopEntry> outs;
bool isClosed{}; bool isClosed{};
FooTermWindow* window;
public: public:
EventLoop(); explicit EventLoop(FooTermWindow* window);
void registerFd(int fdin, EventLoopEntry entry); void registerFd(int fdin, EventLoopEntry entry);
void run(); [[noreturn]] void run();
static void start(EventLoop *self) { static void start(EventLoop *self) {
self->run(); self->run();

66
includes/panel.hpp Normal file
View File

@ -0,0 +1,66 @@
//
// Created by oupson on 01/03/2022.
//
#ifndef FOOTERM_PANEL_HPP
#define FOOTERM_PANEL_HPP
#include <filesystem>
#include <gtkmm/widget.h>
#include <gtkmm/paned.h>
#include <gtkmm/treemodel.h>
#include <gtkmm/treeview.h>
#include <gtkmm/liststore.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/listboxrow.h>
#include "vte.hpp"
class FileModel : public Gtk::TreeModel::ColumnRecord {
public:
FileModel() {
add(colName);
add(colSize);
}
Gtk::TreeModelColumn<Glib::ustring> colName;
Gtk::TreeModelColumn<unsigned long> colSize;
};
class Panel {
private:
Gtk::Paned paned;
Vte vte;
Session session;
FileModel fileModel;
std::filesystem::path path;
Gtk::ScrolledWindow mScrolledWindow;
Gtk::TreeView mTreeView;
Glib::RefPtr<Gtk::ListStore> mRefTreeModel;
void onCellDoubleClicked(const Gtk::TreePath &treePath, [[maybe_unused]] Gtk::TreeViewColumn *column);
void openSession(const char *host, int port, const char *username, const char *password);
public:
Panel(EventLoop &eventLoop, const char *host, int port, const char *username, const char *password);
void listFiles();
Gtk::Paned &getPaned() {
return this->paned;
}
Vte &getVte() {
return this->vte;
}
Session &getSession() {
return this->session;
}
};
#endif //FOOTERM_PANEL_HPP

View File

@ -6,6 +6,7 @@
#define FOOTERM_SSH_HPP #define FOOTERM_SSH_HPP
#include <libssh2.h> #include <libssh2.h>
#include <libssh2_sftp.h>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <string> #include <string>
@ -15,6 +16,7 @@ private:
LIBSSH2_SESSION *session; LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *channel; LIBSSH2_CHANNEL *channel;
int sock; int sock;
LIBSSH2_SFTP* sftpSession;
static int openSocket(const char *addr, int port); static int openSocket(const char *addr, int port);
@ -33,13 +35,21 @@ public:
bool isConnected(); bool isConnected();
int getSock() { [[nodiscard]] int getSock() const {
return this->sock; return this->sock;
}; };
LIBSSH2_SESSION *getSession() {
return this->session;
}
LIBSSH2_CHANNEL *getChannel() { LIBSSH2_CHANNEL *getChannel() {
return this->channel; return this->channel;
} }
LIBSSH2_SFTP* getSftp() {
return this->sftpSession;
}
}; };
class SessionConnectException : public std::exception { class SessionConnectException : public std::exception {

View File

@ -5,22 +5,28 @@
#ifndef FOOTERM_TERM_WINDOWS_HPP #ifndef FOOTERM_TERM_WINDOWS_HPP
#define FOOTERM_TERM_WINDOWS_HPP #define FOOTERM_TERM_WINDOWS_HPP
#include <gtkmm/window.h> #include <gtkmm/window.h>
#include <gtkmm/notebook.h> #include <gtkmm/notebook.h>
#include <vte/vte.h> #include <vte/vte.h>
#include <fmt/core.h> #include <fmt/core.h>
#include "panel.hpp"
#include "eventloop.hpp" #include "eventloop.hpp"
class FooTermWindow : public Gtk::Window { class FooTermWindow : public Gtk::Window {
private: private:
Gtk::Notebook notebook; Gtk::Notebook notebook;
std::vector<Panel> panels;
EventLoop eventLoop; EventLoop eventLoop;
void on_button_click(); void on_button_click();
public: public:
FooTermWindow(); FooTermWindow();
std::vector<Panel> &getPanels() {
return panels;
}
}; };
#endif //FOOTERM_TERM_WINDOWS_HPP #endif //FOOTERM_TERM_WINDOWS_HPP

View File

@ -9,17 +9,18 @@
#include <gtkmm/widget.h> #include <gtkmm/widget.h>
#include "eventloop.hpp" #include "eventloop.hpp"
#include "ssh.hpp"
class Vte { class Vte {
private:
GtkWidget *terminal;
public: public:
Vte(); Vte();
void spawnShell(EventLoop &eventLoop, const char *host, int port, const char *username, const char *password); void spawnShell(EventLoop &eventLoop, Session& session);
Gtk::Widget *asGtkWidget(); Gtk::Widget *asGtkWidget();
private:
GtkWidget *terminal;
}; };
#endif //FOOTERM_VTE_HPP #endif //FOOTERM_VTE_HPP

View File

@ -2,11 +2,15 @@
// Created by oupson on 01/03/2022. // Created by oupson on 01/03/2022.
// //
#include "eventloop.hpp"
#include <libssh2.h> #include <libssh2.h>
#include <iostream> #include <iostream>
EventLoop::EventLoop() = default; #include "eventloop.hpp"
#include "term_windows.hpp"
EventLoop::EventLoop(FooTermWindow *window) {
this->window = window;
}
void EventLoop::registerFd(int fdin, EventLoopEntry entry) { void EventLoop::registerFd(int fdin, EventLoopEntry entry) {
pollfd fd = {0}; pollfd fd = {0};
@ -18,16 +22,21 @@ void EventLoop::registerFd(int fdin, EventLoopEntry entry) {
#define BUFFER_SIZE (256) #define BUFFER_SIZE (256)
void EventLoop::run() { [[noreturn]] void EventLoop::run() {
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
ssize_t bytesRead; ssize_t bytesRead;
int res; int res;
#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
while (!this->isClosed) { while (!this->isClosed) {
res = poll(this->pfds.data(), this->pfds.size(), 1000); res = poll(this->pfds.data(), this->pfds.size(), 1000);
if (res == 0)
for (auto &value: this->window->getPanels()) {
auto session = value.getSession().getSession();
libssh2_keepalive_send(session, nullptr);
}
if (res > 0) { if (res > 0) {
int i = 0; int i = 0;
@ -40,7 +49,7 @@ void EventLoop::run() {
bytesRead = libssh2_channel_read(this->outs[i].channel, buffer, BUFFER_SIZE); bytesRead = libssh2_channel_read(this->outs[i].channel, buffer, BUFFER_SIZE);
if (bytesRead > 0) { if (bytesRead > 0) {
write(this->outs[i].out, buffer, bytesRead); write(this->outs[i].fdout, buffer, bytesRead);
} else if (bytesRead < 0 && bytesRead != LIBSSH2_ERROR_EAGAIN) { } else if (bytesRead < 0 && bytesRead != LIBSSH2_ERROR_EAGAIN) {
std::cout << bytesRead << std::endl; std::cout << bytesRead << std::endl;
} }
@ -62,8 +71,6 @@ void EventLoop::run() {
} }
i++; i++;
} }
} }
} }
#pragma clang diagnostic pop
} }

112
src/panel.cpp Normal file
View File

@ -0,0 +1,112 @@
//
// Created by oupson on 01/03/2022.
//
#include <libssh2_sftp.h>
#include <thread>
#include <gtkmm/treemodelsort.h>
#include <fmt/core.h>
#include "panel.hpp"
Panel::Panel(EventLoop &eventLoop, const char *host, int port, const char *username, const char *password) : path("/") {
this->openSession(host, port, username, password);
this->vte.spawnShell(eventLoop, this->session);
this->paned.pack2(*this->vte.asGtkWidget());
this->paned.pack1(this->mScrolledWindow);
mScrolledWindow.add(mTreeView);
mScrolledWindow.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
// mScrolledWindow.set_expand();
mScrolledWindow.set_size_request(50);
mRefTreeModel = Gtk::ListStore::create(fileModel);
auto sortModel = Gtk::TreeModelSort::create(mRefTreeModel);
mTreeView.set_model(sortModel);
sortModel->set_sort_column(0, Gtk::SortType::SORT_ASCENDING);
mTreeView.append_column("Name", fileModel.colName);
mTreeView.append_column("Size", fileModel.colSize);
mTreeView.signal_row_activated().connect(sigc::mem_fun(this, &Panel::onCellDoubleClicked));
auto pColumn1 = mTreeView.get_column(0);
pColumn1->set_sort_column(fileModel.colName);
auto pColumn2 = mTreeView.get_column(1);
pColumn2->set_sort_column(fileModel.colSize);
listFiles();
this->paned.show_all();
}
void Panel::openSession(const char *host, int port, const char *username, const char *password) {
session.openConnection(host, port, username, password);
}
void Panel::onCellDoubleClicked(const Gtk::TreePath &treePath, [[maybe_unused]] Gtk::TreeViewColumn *column) {
auto row = *this->mTreeView.get_selection()->get_selected();
std::string nPath = row.get_value(fileModel.colName);
this->path.append(nPath);
listFiles();
}
void Panel::listFiles() {
LIBSSH2_SFTP_HANDLE *dir;
int rc;
do {
dir = libssh2_sftp_opendir(session.getSftp(), this->path.c_str());
} while (dir == nullptr && libssh2_session_last_errno(session.getSession()) == LIBSSH2_ERROR_EAGAIN);
if (dir == nullptr && libssh2_session_last_errno(session.getSession()) < 0) {
char *error;
int errorLen;
libssh2_session_last_error(session.getSession(), &error, &errorLen, true);
throw SessionConnectException(fmt::format("unable to opendir \"{}\": {}", this->path.c_str(), error));
}
char buffer[1024];
LIBSSH2_SFTP_ATTRIBUTES attrs;
this->mRefTreeModel->clear();
do {
rc = libssh2_sftp_readdir(dir, buffer, sizeof(buffer) - 1, &attrs);
if (rc > 0 && !(buffer[0] == '.' && buffer[1] == 0)) {
auto row = *mRefTreeModel->append();
row[this->fileModel.colName] = buffer;
row[this->fileModel.colSize] = attrs.filesize;
//std::cerr << buffer << std::endl;
}
} while (rc > 0 || rc == LIBSSH2_ERROR_EAGAIN);
if (rc < 0) {
char *error;
int errorLen;
libssh2_session_last_error(session.getSession(), &error, &errorLen, true);
throw SessionConnectException(fmt::format("unable to readdir \"{}\": {}", this->path.c_str(), error));
}
do {
rc = libssh2_sftp_closedir(dir);
} while (rc == LIBSSH2_ERROR_EAGAIN);
if (rc < 0) {
char *error;
int errorLen;
libssh2_session_last_error(session.getSession(), &error, &errorLen, true);
throw SessionConnectException(fmt::format("unable to closedir \"{}\": {}", this->path.c_str(), error));
}
}

View File

@ -2,22 +2,26 @@
// Created by oupson on 01/03/2022. // Created by oupson on 01/03/2022.
// //
#include "ssh.hpp"
#include <sys/socket.h> #include <sys/socket.h>
#include <cstdio> #include <cstdio>
#include <netdb.h> #include <netdb.h>
#include <stdexcept> #include <stdexcept>
#include <fmt/core.h> #include <fmt/core.h>
#include "ssh.hpp"
Session::Session() { Session::Session() {
this->session = nullptr; this->session = nullptr;
this->channel = nullptr; this->channel = nullptr;
this->sock = -1; this->sock = -1;
this->sftpSession = nullptr;
} }
Session::~Session() { Session::~Session() {
// TODO
if (this->isConnected()) { if (this->isConnected()) {
this->disconnect(); this->disconnect();
} }
@ -134,6 +138,10 @@ void Session::openConnection(const char *addr, int port, const char *username, c
throw SessionConnectException(fmt::format("Failed to authenticate: {}", error)); throw SessionConnectException(fmt::format("Failed to authenticate: {}", error));
} }
do {
this->sftpSession = libssh2_sftp_init(this->session);
} while (this->sftpSession == nullptr && libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_EAGAIN);
do { do {
this->channel = libssh2_channel_open_session(session); this->channel = libssh2_channel_open_session(session);
} while (this->channel == nullptr && libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_EAGAIN); } while (this->channel == nullptr && libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_EAGAIN);
@ -154,6 +162,8 @@ void Session::openConnection(const char *addr, int port, const char *username, c
do { do {
rc = libssh2_channel_shell(this->channel); rc = libssh2_channel_shell(this->channel);
} while (rc == LIBSSH2_ERROR_EAGAIN); } while (rc == LIBSSH2_ERROR_EAGAIN);
} }
void Session::disconnect() { void Session::disconnect() {

View File

@ -2,8 +2,7 @@
// Created by oupson on 28/02/2022. // Created by oupson on 28/02/2022.
// //
#include "vte.hpp"
#include "term_windows.hpp"
#include <thread> #include <thread>
#include <gtkmm/headerbar.h> #include <gtkmm/headerbar.h>
@ -13,7 +12,11 @@
#include <gtkmm/grid.h> #include <gtkmm/grid.h>
#include <iostream> #include <iostream>
FooTermWindow::FooTermWindow() { #include "term_windows.hpp"
#include "vte.hpp"
#include "panel.hpp"
FooTermWindow::FooTermWindow() : eventLoop(this) {
this->set_default_size(800, 600); this->set_default_size(800, 600);
this->add(notebook); this->add(notebook);
@ -105,12 +108,16 @@ void FooTermWindow::on_button_click() {
} }
try { try {
Vte *vte = new Vte(); auto *panel = new Panel(
vte->spawnShell(this->eventLoop, host.c_str(), port, username.c_str(), password.c_str()); this->eventLoop,
host.c_str(),
port,
username.c_str(),
password.c_str()
);
Gtk::Widget *widget = vte->asGtkWidget(); this->notebook.append_page(panel->getPaned(), host, true);
this->notebook.append_page(*widget, host, true); panel->getPaned().show_all();
widget->show();
} catch (std::exception &e) { } catch (std::exception &e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
} }

View File

@ -7,11 +7,10 @@
#include "ssh.hpp" #include "ssh.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include <thread> // std::thread
#include <pty.h> #include <pty.h>
#include <fcntl.h> #include <fcntl.h>
static void got_child_exited([[maybe_unused]] VteTerminal *vte, gint status, [[maybe_unused]] Vte *window) { static void got_child_exited([[maybe_unused]] VteTerminal *vte, [[maybe_unused]] gint status, [[maybe_unused]] Vte *window) {
// TODO WHEN LOCAL TERMINAL // TODO WHEN LOCAL TERMINAL
} }
@ -29,10 +28,13 @@ termStateCallback([[maybe_unused]] VteTerminal *terminal, GPid pid, GError *erro
Vte::Vte() { Vte::Vte() {
this->terminal = vte_terminal_new(); this->terminal = vte_terminal_new();
#if VTE_CHECK_VERSION(0, 62, 0)
vte_terminal_set_enable_sixel(VTE_TERMINAL(this->terminal), true); vte_terminal_set_enable_sixel(VTE_TERMINAL(this->terminal), true);
#endif
} }
void Vte::spawnShell(EventLoop &eventLoop, const char *host, int port, const char *username, const char *password) { void Vte::spawnShell(EventLoop &eventLoop, Session& session) {
int master, slave; int master, slave;
struct termios settings{}; struct termios settings{};
@ -45,21 +47,19 @@ void Vte::spawnShell(EventLoop &eventLoop, const char *host, int port, const cha
VtePty *pty = vte_pty_new_foreign_sync(master, nullptr, nullptr); VtePty *pty = vte_pty_new_foreign_sync(master, nullptr, nullptr);
vte_terminal_set_pty(VTE_TERMINAL(this->terminal), pty); vte_terminal_set_pty(VTE_TERMINAL(this->terminal), pty);
auto *s = new Session();
s->openConnection(host, port, username, password);
EventLoopEntry entry1{}; EventLoopEntry entry1{};
entry1.out = slave; entry1.fdout = slave;
entry1.entryType = EventLoopEntry::CHANNEL; entry1.entryType = EventLoopEntry::CHANNEL;
entry1.channel = s->getChannel(); entry1.channel = session.getChannel();
eventLoop.registerFd(s->getSock(), entry1); eventLoop.registerFd(session.getSock(), entry1);
EventLoopEntry entry2{}; EventLoopEntry entry2{};
entry2.out = slave; entry2.fdout = slave;
entry2.entryType = EventLoopEntry::DESCRIPTOR; entry2.entryType = EventLoopEntry::DESCRIPTOR;
entry2.channel = s->getChannel(); entry2.channel = session.getChannel();
eventLoop.registerFd(slave, entry2); eventLoop.registerFd(slave, entry2);