Public Access
The SSH App opens one session to a shell, over libssh (LibSSH-ESP32 5.10.0) on mbedTLS. A server is trusted the first time on its fingerprint, and a changed key is a warning with Cancel selected. The password is typed each time and kept nowhere; or the device makes itself an Ed25519 key, whose public half is shown, written to /ssh/id_ed25519.pub and printed by `ssh status`. lib/term is the terminal: what a shell, less, top, nano and vim send, with sixteen colours, scroll regions, the alternate screen and 100 lines of scrollback. Five text sizes with Ctrl and + or -, from 60x20 to 26x8, told to the far end. The session goes on when the App is left; SSH shows in the Status Bar. `ssh user@host` in the Shell opens the App. Also: - Keys that aren't characters carry Shift, Ctrl and Alt. The terminal needs it, and it makes Ctrl+Fn+up/down in a note and Shift+Tab in Gemini work from the real keyboard. - IRC doesn't try to connect under 60 KB free: started with a session open, its TLS handshake took the heap down to 236 bytes. - libssh's own curve25519 is left out of the build (scripts/libssh_filter.py): libsodium's has the same names. Costs 292 KB of flash and about 50 KB of heap while a session is open; not started under 75 KB free. Docs: guide page, how-to, FAQ, home page, Status Bar, SD card folders, the memory how-to, README, glossary, N1 notes with Q254 to Q266 and the checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
104 lines
3.8 KiB
C++
104 lines
3.8 KiB
C++
#pragma once
|
|
|
|
#include <NetworkClientSecure.h>
|
|
#include "platform/counted_client.h"
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/semphr.h>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "event_bus.h"
|
|
#include "irc_config.h"
|
|
#include "irc_session.h"
|
|
#include "reconnect_policy.h"
|
|
#include "service.h"
|
|
#include "services/clock_service.h"
|
|
#include "services/storage_service.h"
|
|
#include "services/wifi_service.h"
|
|
|
|
namespace roro {
|
|
|
|
// Keeps the IRC connection alive in the background once started (see IRC Service in CONTEXT.md):
|
|
// connects over TLS while Wi-Fi is Connected, pauses while Monitoring, reconnects with backoff,
|
|
// writes Logs and raises Notifications for Mentions. Networking runs on its own task; the
|
|
// session is shared with the IRC App under a lock.
|
|
class IrcService : public Service {
|
|
public:
|
|
static constexpr size_t kNeedFree = 60 * 1024; // free memory a connection attempt wants
|
|
enum class Status { Stopped, WaitingForWifi, Connecting, Registering, Online, Paused, Retrying };
|
|
|
|
IrcService(KeyValueStore& store, const std::string& defaultNick, WifiService& wifi, StorageService& storage,
|
|
ClockService& clock, EventBus& bus);
|
|
const char* name() const override { return "irc"; }
|
|
void start() override;
|
|
|
|
// Starts the session (the IRC App does this when opened, unless you stopped it).
|
|
void connect();
|
|
// Stops it, from any state: says QUIT if connected, and stops retrying. /quit, `irc stop`.
|
|
void disconnect();
|
|
bool running() const { return wanted_; }
|
|
bool stoppedByUser() const { return stoppedByUser_; }
|
|
|
|
// A TLS connection to Gitea needs more memory than is left beside this one (#6, Q172): the
|
|
// Update Service asks IRC to step aside while it talks to the server, and to come back after.
|
|
// Unlike disconnect(), nothing is remembered as "stopped by the user".
|
|
void holdForUpdate(bool hold) { held_ = hold; }
|
|
Status status() const { return status_; }
|
|
|
|
// Read or act on the session while holding its lock: withSession([](IrcSession& s) { ... }).
|
|
template <typename F>
|
|
auto withSession(F f) {
|
|
Lock l(lock_);
|
|
return f(*session_);
|
|
}
|
|
int totalUnread();
|
|
|
|
// A copy of the server settings to edit; applyConfig() validates, saves and, if running,
|
|
// reconnects with it (a fresh session).
|
|
IrcConfig draftConfig();
|
|
std::string applyConfig(const IrcConfig& draft);
|
|
|
|
private:
|
|
struct Lock {
|
|
explicit Lock(SemaphoreHandle_t m) : m_(m) { xSemaphoreTake(m_, portMAX_DELAY); }
|
|
~Lock() { xSemaphoreGive(m_); }
|
|
SemaphoreHandle_t m_;
|
|
};
|
|
|
|
static void taskEntry(void* self);
|
|
void loop();
|
|
bool open();
|
|
void close(const std::string& reason);
|
|
void readLines();
|
|
void flushEffects();
|
|
void scheduleRetry(const std::string& why);
|
|
|
|
IrcConfig config_;
|
|
WifiService& wifi_;
|
|
StorageService& storage_;
|
|
ClockService& clock_;
|
|
EventBus& bus_;
|
|
std::unique_ptr<IrcSession> session_;
|
|
SemaphoreHandle_t lock_ = nullptr;
|
|
TaskHandle_t task_ = nullptr;
|
|
Counted<NetworkClientSecure> tlsClient_{net::User::Irc}; // counted for the System App (S1, Q121)
|
|
Counted<NetworkClient> plainClient_{net::User::Irc};
|
|
NetworkClient* conn_ = &tlsClient_; // whichever the config asks for
|
|
ReconnectPolicy backoff_;
|
|
std::string partial_;
|
|
volatile bool wanted_ = false;
|
|
volatile bool stoppedByUser_ = false; // a manual stop: opening the App doesn't reconnect
|
|
volatile bool stopRequested_ = false;
|
|
bool quitSent_ = false; // /quit already queued its QUIT
|
|
volatile bool restart_ = false;
|
|
volatile bool held_ = false; // stepping aside for an update
|
|
volatile Status status_ = Status::Stopped;
|
|
bool open_ = false;
|
|
uint32_t retryAtMs_ = 0;
|
|
uint32_t lastRxMs_ = 0;
|
|
bool pingSent_ = false;
|
|
};
|
|
|
|
} // namespace roro
|