fix: scope server pins by port (fumi-core rev 1468f3c)

This commit is contained in:
randogoth 2026-09-30 08:52:57 +03:00
parent 996e879509
commit 7d2a147fec
16 changed files with 109 additions and 79 deletions

View file

@ -112,14 +112,14 @@ class FumiNative {
Future<int> rotations() => _io((ffi) => ffi.rotations(_s));
Future<void> pinServer(String host, String keyB32) =>
_io((ffi) => ffi.pinServer(_s, host, keyB32));
Future<void> pinServer(String host, String keyB32, int port) =>
_io((ffi) => ffi.pinServer(_s, host, keyB32, port));
Future<String?> serverPin(String host) =>
_io((ffi) => ffi.serverPin(_s, host));
Future<String?> serverPin(String host, int port) =>
_io((ffi) => ffi.serverPin(_s, host, port));
Future<void> unpinServer(String host) =>
_io((ffi) => ffi.unpinServer(_s, host));
Future<void> unpinServer(String host, int port) =>
_io((ffi) => ffi.unpinServer(_s, host, port));
Future<List<dynamic>> pins() => _io((ffi) => ffi.pins(_s));

View file

@ -109,15 +109,15 @@ class SmolFfi {
Void Function(Pointer<Void>),
void Function(Pointer<Void>)>("smol_flag_free");
late final int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>)
late final int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>, int)
_pinServer = _lib.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>),
int Function(
Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>)>("smol_pin_server");
late final Pointer<Utf8> Function(Pointer<Void>, Pointer<Utf8>)
Int32 Function(Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>, Uint16),
int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>,
int)>("smol_pin_server");
late final Pointer<Utf8> Function(Pointer<Void>, Pointer<Utf8>, int)
_serverPin = _lib.lookupFunction<
Pointer<Utf8> Function(Pointer<Void>, Pointer<Utf8>),
Pointer<Utf8> Function(Pointer<Void>, Pointer<Utf8>)>(
Pointer<Utf8> Function(Pointer<Void>, Pointer<Utf8>, Uint16),
Pointer<Utf8> Function(Pointer<Void>, Pointer<Utf8>, int)>(
"smol_server_pin");
late final Pointer<Utf8> Function(Pointer<Void>) _storeAccount = _lib
.lookupFunction<Pointer<Utf8> Function(Pointer<Void>),
@ -226,9 +226,11 @@ class SmolFfi {
Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>, Int32),
int Function(Pointer<Void>, Pointer<Utf8>, Pointer<Utf8>,
int)>("smol_contact_save");
late final int Function(Pointer<Void>, Pointer<Utf8>) _unpinServer = _lib
.lookupFunction<Int32 Function(Pointer<Void>, Pointer<Utf8>),
int Function(Pointer<Void>, Pointer<Utf8>)>("smol_unpin_server");
late final int Function(Pointer<Void>, Pointer<Utf8>, int) _unpinServer =
_lib.lookupFunction<
Int32 Function(Pointer<Void>, Pointer<Utf8>, Uint16),
int Function(
Pointer<Void>, Pointer<Utf8>, int)>("smol_unpin_server");
late final Pointer<Utf8> Function(Pointer<Void>) _pins = _lib.lookupFunction<
Pointer<Utf8> Function(Pointer<Void>),
Pointer<Utf8> Function(Pointer<Void>)>("smol_pins");
@ -334,12 +336,14 @@ class SmolFfi {
void freeFlag(int flag) => _flagFree(_h(flag));
void pinServer(int store, String host, String keyB32) {
if (_pinServer(_h(store), _text(host), _text(keyB32)) != smolOk) _fail();
void pinServer(int store, String host, String keyB32, int port) {
if (_pinServer(_h(store), _text(host), _text(keyB32), port) != smolOk) {
_fail();
}
}
void unpinServer(int store, String host) {
if (_unpinServer(_h(store), _text(host)) != smolOk) _fail();
void unpinServer(int store, String host, int port) {
if (_unpinServer(_h(store), _text(host), port) != smolOk) _fail();
}
/// Every pin: [{host, key}] with keys as base32.
@ -355,8 +359,8 @@ class SmolFfi {
_h(store), _text(address), _text(keyB32), verified ? 1 : 0);
/// Null when nothing is pinned.
String? serverPin(int store, String host) {
final text = take(_serverPin(_h(store), _text(host)));
String? serverPin(int store, String host, int port) {
final text = take(_serverPin(_h(store), _text(host), port));
return text.isEmpty ? null : text;
}

View file

@ -161,7 +161,7 @@ class _OnboardingScreenState extends ConsumerState<OnboardingScreen> {
// "already pinned, e.g. by a previous attempt or a preset server".
final serverKey = serverKeyController.text.trim();
if (serverKey.isNotEmpty) {
client.pinServer(address.host, serverKey);
client.pinServer(address.host, serverKey, address.port);
}
if (recall && restoreIntent) {
await client.restoreAndRecall(seedController.text, address.short);
@ -210,7 +210,7 @@ class _OnboardingScreenState extends ConsumerState<OnboardingScreen> {
image: AssetImage("assets/images/bug_logo.png"),
fit: BoxFit.contain),
),
Text("kirakira",
Text("キラキラ",
style: Theme.of(context).textTheme.titleLarge),
const SizedBox(height: 40),
Text(title, style: Theme.of(context).textTheme.titleMedium),

View file

@ -12,6 +12,7 @@ import "package:smol_mail/data/providers/providers.dart";
import "package:smol_mail/presentation/routes/app_router.gr.dart";
import "package:smol_mail/presentation/theme/app_colors.dart";
import "package:smol_mail/shared/utils/snackbar.dart";
import "package:smol_mail/smol/address.dart";
import "package:smol_mail/smol/errors.dart";
import "package:smol_mail/smol/ui.dart";
@ -320,7 +321,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
trailing: IconButton(
icon: const Icon(Icons.delete_outline, size: 18),
onPressed: () {
store.unpinServer(host);
store.unpinServer(host, defaultPort);
ref.read(revisionProvider.notifier).bump();
setState(() {});
},
@ -350,9 +351,8 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
TextButton(
onPressed: () {
try {
ref
.read(clientProvider)
.pinServer(hostController.text, keyController.text);
ref.read(clientProvider).pinServer(
hostController.text, keyController.text, defaultPort);
keyController.clear();
setState(() {});
} on SmolError catch (err) {

View file

@ -144,7 +144,7 @@ class SmolClient {
// registration and fetching demand it anyway (sec 4), so restoring is
// stopped at the pin step rather than letting the account bind to a
// server whose key nobody verified.
if (store.serverPin(addr.host) == null) {
if (store.serverPin(addr.host, addr.port) == null) {
throw SmolError("no pinned key for ${addr.host}. Obtain it from the "
"operator through a trusted channel, then pin it in settings.");
}
@ -157,7 +157,8 @@ class SmolClient {
}
}
void pinServer(String host, String keyB32) => store.pinServer(host, keyB32);
void pinServer(String host, String keyB32, int port) =>
store.pinServer(host, keyB32, port);
Future<void> registerAccount(String addressText, {String token = ""}) async {
final addr = parseAddress(addressText);

View file

@ -9,6 +9,7 @@
// removable in settings like any other, and never overwrites a host the user
// (or a previous install) already pinned.
import "package:smol_mail/smol/address.dart";
import "package:smol_mail/smol/store.dart";
const _presetHost = String.fromEnvironment("SMOL_PRESET_SERVER");
@ -16,7 +17,7 @@ const _presetKey = String.fromEnvironment("SMOL_PRESET_SERVER_KEY");
void applyPresetServer(SmolStore store) {
if (_presetHost.isEmpty || _presetKey.isEmpty) return;
if (store.serverPin(_presetHost) != null) return;
if (store.serverPin(_presetHost, defaultPort) != null) return;
// A malformed preset leaves the host unpinned rather than blocking boot.
store.pinServer(_presetHost, _presetKey);
store.pinServer(_presetHost, _presetKey, defaultPort);
}

View file

@ -291,10 +291,11 @@ class SmolStore {
// --- pins -------------------------------------------------------------------
/// Pins a server's static key (§4): sync, because it is one local write.
void pinServer(String host, String keyB32) =>
_ffi.pinServer(_native.store!, host, keyB32);
void pinServer(String host, String keyB32, int port) =>
_ffi.pinServer(_native.store!, host, keyB32, port);
String? serverPin(String host) => _ffi.serverPin(_native.store!, host);
String? serverPin(String host, int port) =>
_ffi.serverPin(_native.store!, host, port);
List<(String, String)> allPins() {
final rows = _ffi.pins(_native.store!);
@ -304,7 +305,8 @@ class SmolStore {
];
}
Future<void> unpinServer(String host) async => _native.unpinServer(host);
Future<void> unpinServer(String host, int port) async =>
_native.unpinServer(host, port);
// --- backups ----------------------------------------------------------------