finished implementation
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"meek":["meek_lite 192.0.2.20:80 url=https://1603026938.rsc.cdn77.org front=www.phpmyadmin.net utls=HelloRandomizedALPN"],"meek-azure":["meek_lite 192.0.2.20:80 url=https://1603026938.rsc.cdn77.org front=www.phpmyadmin.net utls=HelloRandomizedALPN"],"obfs4":["obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0","obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0","obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0","obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1","obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0","obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0","obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0","obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0","obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0","obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0","obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0"],"snowflake":["snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.mixvoip.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn","snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://1098762253.rsc.cdn77.org/ fronts=www.cdn77.com,www.phpmyadmin.net ice=stun:stun.antisip.com:3478,stun:stun.epygi.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.mixvoip.com:3478,stun:stun.nextcloud.com:3478,stun:stun.bethesda.net:3478,stun:stun.nextcloud.com:443 utls-imitate=hellorandomizedalpn"]}
|
||||
@@ -17,7 +17,10 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:fading_scroll/fading_scroll.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
@@ -163,6 +166,38 @@ class DeveloperSettingsScreen extends HookConsumerWidget {
|
||||
label: const Text('Copy'),
|
||||
),
|
||||
),
|
||||
if (kDebugMode)
|
||||
CustomListTile(
|
||||
title: 'Dart VM',
|
||||
subtitle: 'Copy Dart VM service URL',
|
||||
prefix: Padding(
|
||||
padding: const EdgeInsets.only(right: 16.0),
|
||||
child: Icon(
|
||||
Icons.bug_report,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
suffix: FilledButton.icon(
|
||||
onPressed: () async {
|
||||
final serviceProtocolInfo = await Service.getInfo();
|
||||
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text:
|
||||
serviceProtocolInfo.serverUri?.toString() ??
|
||||
'Error',
|
||||
),
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
showInfoMessage(context, 'Service URL copied');
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.copy),
|
||||
label: const Text('Copy'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart' as path_provider;
|
||||
import 'package:pluggable_transports_proxy/pluggable_transports_proxy.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:universal_io/io.dart';
|
||||
|
||||
part 'builtin_bridges.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class BuiltinBridgesService extends _$BuiltinBridgesService {
|
||||
late final _bridgeFileFuture = path_provider
|
||||
.getApplicationSupportDirectory()
|
||||
.then((dir) => File(p.join(dir.path, 'builtin-bridges.json')));
|
||||
|
||||
Future<DateTime?> lastUpdate() async {
|
||||
final bridgeFile = await _bridgeFileFuture;
|
||||
if (!await bridgeFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return bridgeFile.lastModified();
|
||||
}
|
||||
|
||||
Future<void> updateStoredBuiltinBridges(BuiltInBridges bridges) async {
|
||||
final bridgeFile = await _bridgeFileFuture;
|
||||
await bridgeFile.writeAsString(jsonEncode(bridges.toJson()), flush: true);
|
||||
}
|
||||
|
||||
Future<BuiltInBridges?> getStoredBuiltinBridges() async {
|
||||
final bridgeFile = await _bridgeFileFuture;
|
||||
if (!await bridgeFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final content = await bridgeFile.readAsString();
|
||||
return BuiltInBridges.fromJson(jsonDecode(content) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
Future<BuiltInBridges> getBundledBuiltinBridges() async {
|
||||
final content = await rootBundle.loadString(
|
||||
'assets/preferences/builtin-bridges.json',
|
||||
);
|
||||
return BuiltInBridges.fromJson(jsonDecode(content) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
@override
|
||||
void build() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'builtin_bridges.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$builtinBridgesServiceHash() =>
|
||||
r'beacb3c9d8c5a7179c7c9cad8024d128b09241f5';
|
||||
|
||||
/// See also [BuiltinBridgesService].
|
||||
@ProviderFor(BuiltinBridgesService)
|
||||
final builtinBridgesServiceProvider =
|
||||
NotifierProvider<BuiltinBridgesService, void>.internal(
|
||||
BuiltinBridgesService.new,
|
||||
name: r'builtinBridgesServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$builtinBridgesServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$BuiltinBridgesService = Notifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:pluggable_transports_proxy/pluggable_transports_proxy.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/features/tor/data/services/builtin_bridges.dart';
|
||||
|
||||
part 'builtin_bridges.g.dart';
|
||||
|
||||
@Riverpod()
|
||||
class BuiltinBridgesRepository extends _$BuiltinBridgesRepository {
|
||||
Future<void> updateIfNecessary() async {
|
||||
final lastUpdate = await ref
|
||||
.read(builtinBridgesServiceProvider.notifier)
|
||||
.lastUpdate();
|
||||
|
||||
if (lastUpdate == null ||
|
||||
DateTime.now().difference(lastUpdate) > const Duration(days: 2)) {
|
||||
BuiltInBridges? remoteBridges;
|
||||
try {
|
||||
remoteBridges = await service.getBuiltinBridges();
|
||||
} catch (e, s) {
|
||||
logger.e('Failed fetching builtin bridges', error: e, stackTrace: s);
|
||||
}
|
||||
|
||||
if (remoteBridges != null) {
|
||||
await ref
|
||||
.read(builtinBridgesServiceProvider.notifier)
|
||||
.updateStoredBuiltinBridges(remoteBridges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<BuiltInBridges> getBridges({bool tryUpdate = true}) async {
|
||||
if (tryUpdate) {
|
||||
await updateIfNecessary();
|
||||
}
|
||||
|
||||
final storedBridges = await ref
|
||||
.read(builtinBridgesServiceProvider.notifier)
|
||||
.getStoredBuiltinBridges();
|
||||
|
||||
return storedBridges ??
|
||||
await ref
|
||||
.read(builtinBridgesServiceProvider.notifier)
|
||||
.getBundledBuiltinBridges();
|
||||
}
|
||||
|
||||
@override
|
||||
void build(MoatService service) {}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'builtin_bridges.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$builtinBridgesRepositoryHash() =>
|
||||
r'05fc40a7742fc266aeb76b4e3f1d9bdfdb8d448f';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$BuiltinBridgesRepository
|
||||
extends BuildlessAutoDisposeNotifier<void> {
|
||||
late final MoatService service;
|
||||
|
||||
void build(MoatService service);
|
||||
}
|
||||
|
||||
/// See also [BuiltinBridgesRepository].
|
||||
@ProviderFor(BuiltinBridgesRepository)
|
||||
const builtinBridgesRepositoryProvider = BuiltinBridgesRepositoryFamily();
|
||||
|
||||
/// See also [BuiltinBridgesRepository].
|
||||
class BuiltinBridgesRepositoryFamily extends Family<void> {
|
||||
/// See also [BuiltinBridgesRepository].
|
||||
const BuiltinBridgesRepositoryFamily();
|
||||
|
||||
/// See also [BuiltinBridgesRepository].
|
||||
BuiltinBridgesRepositoryProvider call(MoatService service) {
|
||||
return BuiltinBridgesRepositoryProvider(service);
|
||||
}
|
||||
|
||||
@override
|
||||
BuiltinBridgesRepositoryProvider getProviderOverride(
|
||||
covariant BuiltinBridgesRepositoryProvider provider,
|
||||
) {
|
||||
return call(provider.service);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'builtinBridgesRepositoryProvider';
|
||||
}
|
||||
|
||||
/// See also [BuiltinBridgesRepository].
|
||||
class BuiltinBridgesRepositoryProvider
|
||||
extends AutoDisposeNotifierProviderImpl<BuiltinBridgesRepository, void> {
|
||||
/// See also [BuiltinBridgesRepository].
|
||||
BuiltinBridgesRepositoryProvider(MoatService service)
|
||||
: this._internal(
|
||||
() => BuiltinBridgesRepository()..service = service,
|
||||
from: builtinBridgesRepositoryProvider,
|
||||
name: r'builtinBridgesRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$builtinBridgesRepositoryHash,
|
||||
dependencies: BuiltinBridgesRepositoryFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
BuiltinBridgesRepositoryFamily._allTransitiveDependencies,
|
||||
service: service,
|
||||
);
|
||||
|
||||
BuiltinBridgesRepositoryProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.service,
|
||||
}) : super.internal();
|
||||
|
||||
final MoatService service;
|
||||
|
||||
@override
|
||||
void runNotifierBuild(covariant BuiltinBridgesRepository notifier) {
|
||||
return notifier.build(service);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(BuiltinBridgesRepository Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: BuiltinBridgesRepositoryProvider._internal(
|
||||
() => create()..service = service,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
service: service,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeNotifierProviderElement<BuiltinBridgesRepository, void>
|
||||
createElement() {
|
||||
return _BuiltinBridgesRepositoryProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is BuiltinBridgesRepositoryProvider &&
|
||||
other.service == service;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, service.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin BuiltinBridgesRepositoryRef on AutoDisposeNotifierProviderRef<void> {
|
||||
/// The parameter `service` of this provider.
|
||||
MoatService get service;
|
||||
}
|
||||
|
||||
class _BuiltinBridgesRepositoryProviderElement
|
||||
extends AutoDisposeNotifierProviderElement<BuiltinBridgesRepository, void>
|
||||
with BuiltinBridgesRepositoryRef {
|
||||
_BuiltinBridgesRepositoryProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
MoatService get service =>
|
||||
(origin as BuiltinBridgesRepositoryProvider).service;
|
||||
}
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -20,13 +20,17 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||
import 'package:nullability/nullability.dart';
|
||||
import 'package:pluggable_transports_proxy/pluggable_transports_proxy.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/domain/providers/lifecycle.dart';
|
||||
import 'package:weblibre/features/tor/domain/repositories/builtin_bridges.dart';
|
||||
import 'package:weblibre/features/tor/utils/tor_entrypoint.dart';
|
||||
import 'package:weblibre/features/user/data/models/tor_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/tor_settings.dart';
|
||||
|
||||
part 'tor_proxy.g.dart';
|
||||
@@ -38,9 +42,19 @@ class _TorService {
|
||||
|
||||
ValueStream<int?> get portStream => _portSubject.stream;
|
||||
|
||||
Future<void> start({ProxyType? proxyType, String? bridgeLines}) async {
|
||||
Future<void> startOrReconfigure({
|
||||
ProxyType? proxyType,
|
||||
String? bridgeLines,
|
||||
}) async {
|
||||
await service.startService();
|
||||
service.invoke('start', {
|
||||
service.invoke('startOrReconfigure', {
|
||||
if (proxyType != null) 'proxyType': proxyType.name,
|
||||
if (bridgeLines != null) 'bridgeLines': bridgeLines,
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> reconfigure({ProxyType? proxyType, String? bridgeLines}) async {
|
||||
service.invoke('reconfigure', {
|
||||
if (proxyType != null) 'proxyType': proxyType.name,
|
||||
if (bridgeLines != null) 'bridgeLines': bridgeLines,
|
||||
});
|
||||
@@ -101,7 +115,7 @@ class TorProxyService extends _$TorProxyService {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> connect({bool forceReconnect = false}) async {
|
||||
Future<void> startOrReconfigure({bool forceReconnect = false}) async {
|
||||
final currentPort = await requestSync();
|
||||
|
||||
if (currentPort == null || forceReconnect) {
|
||||
@@ -111,14 +125,97 @@ class TorProxyService extends _$TorProxyService {
|
||||
.read(torSettingsRepositoryProvider.notifier)
|
||||
.fetchSettings();
|
||||
|
||||
if (torSettings.autoConfig) {
|
||||
final moat = MoatService();
|
||||
final config = await moat.autoConf();
|
||||
Setting? setting;
|
||||
if (torSettings.config == TorConnectionConfig.auto) {
|
||||
List<Setting>? config;
|
||||
|
||||
todo
|
||||
final moat = MoatService();
|
||||
try {
|
||||
await moat.initialize();
|
||||
config = await moat.autoConf(
|
||||
cannotConnectWithoutPt: torSettings.requireBridge,
|
||||
);
|
||||
|
||||
if (config == null && torSettings.requireBridge) {
|
||||
config = MoatService.convertBuiltinToSettings(
|
||||
await ref
|
||||
.read(builtinBridgesRepositoryProvider(moat).notifier)
|
||||
.getBridges(),
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
logger.e('Failed auto configure bridges', error: e, stackTrace: s);
|
||||
} finally {
|
||||
await moat.dispose();
|
||||
}
|
||||
|
||||
setting = config.mapNotNull(
|
||||
(config) =>
|
||||
config.firstWhereOrNull(
|
||||
(setting) => setting.bridge.type == TransportType.obfs4,
|
||||
) ??
|
||||
config.firstWhereOrNull(
|
||||
(setting) => setting.bridge.type == TransportType.snowflake,
|
||||
),
|
||||
);
|
||||
} else if (torSettings.config != TorConnectionConfig.direct) {
|
||||
List<Setting>? config;
|
||||
|
||||
final moat = MoatService();
|
||||
try {
|
||||
await moat.initialize();
|
||||
if (torSettings.fetchRemoteBridges) {
|
||||
config = await moat.getDefaultBridges();
|
||||
}
|
||||
|
||||
if (config == null && torSettings.requireBridge) {
|
||||
config = MoatService.convertBuiltinToSettings(
|
||||
await ref
|
||||
.read(builtinBridgesRepositoryProvider(moat).notifier)
|
||||
.getBridges(tryUpdate: torSettings.fetchRemoteBridges),
|
||||
);
|
||||
}
|
||||
|
||||
setting = config.mapNotNull(
|
||||
(config) => config.firstWhereOrNull(
|
||||
(setting) =>
|
||||
setting.bridge.type ==
|
||||
switch (torSettings.config) {
|
||||
TorConnectionConfig.auto => throw UnimplementedError(),
|
||||
TorConnectionConfig.direct => throw UnimplementedError(),
|
||||
TorConnectionConfig.obfs4 => TransportType.obfs4,
|
||||
TorConnectionConfig.snowflake => TransportType.snowflake,
|
||||
},
|
||||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
logger.e('Failed auto configure bridges', error: e, stackTrace: s);
|
||||
} finally {
|
||||
await moat.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (setting != null) {
|
||||
switch (setting.bridge.type) {
|
||||
case TransportType.obfs4:
|
||||
await _tor.startOrReconfigure(
|
||||
proxyType: ProxyType.obfs4,
|
||||
bridgeLines: setting.bridge.bridges?.join('\n'),
|
||||
);
|
||||
case TransportType.snowflake:
|
||||
await _tor.startOrReconfigure(
|
||||
proxyType: ProxyType.snowflake,
|
||||
bridgeLines: setting.bridge.bridges?.join('\n'),
|
||||
);
|
||||
case TransportType.meek:
|
||||
case TransportType.meekAzure:
|
||||
case TransportType.webtunnel:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
} else {
|
||||
await _tor.startOrReconfigure();
|
||||
}
|
||||
|
||||
await _tor.start();
|
||||
_enableHeartbeatTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'tor_proxy.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$torProxyServiceHash() => r'82b4964ddf14d02893ebbea3b25d91216359ba10';
|
||||
|
||||
/// See also [TorProxyService].
|
||||
@ProviderFor(TorProxyService)
|
||||
final torProxyServiceProvider =
|
||||
AsyncNotifierProvider<TorProxyService, int?>.internal(
|
||||
TorProxyService.new,
|
||||
name: r'torProxyServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$torProxyServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$TorProxyService = AsyncNotifier<int?>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -47,7 +47,7 @@ class StartProxyController extends _$StartProxyController {
|
||||
if (result == true) {
|
||||
final connection = ref
|
||||
.read(torProxyServiceProvider.notifier)
|
||||
.connect();
|
||||
.startOrReconfigure();
|
||||
|
||||
ref
|
||||
.read(overlayControllerProvider.notifier)
|
||||
@@ -59,6 +59,9 @@ class StartProxyController extends _$StartProxyController {
|
||||
await connection;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Reconfigure
|
||||
await ref.read(torProxyServiceProvider.notifier).startOrReconfigure();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'start_tor_proxy.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$startProxyControllerHash() =>
|
||||
r'3e8b409f069f32b601b1c60acbe0a72694fd1cc2';
|
||||
r'8840d5ffe9346c3f8b0aa0b3e034605b606c5e34';
|
||||
|
||||
/// See also [StartProxyController].
|
||||
@ProviderFor(StartProxyController)
|
||||
|
||||
@@ -39,6 +39,14 @@ class TorProxyScreen extends HookConsumerWidget {
|
||||
await ref.read(torProxyServiceProvider.notifier).requestSync();
|
||||
});
|
||||
|
||||
ref.listen(torSettingsRepositoryProvider, (previous, next) async {
|
||||
final torService = ref.read(torProxyServiceProvider.notifier);
|
||||
|
||||
if (await torService.requestSync() != null) {
|
||||
await torService.startOrReconfigure();
|
||||
}
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Tor™ Proxy')),
|
||||
body: SafeArea(
|
||||
@@ -46,9 +54,8 @@ class TorProxyScreen extends HookConsumerWidget {
|
||||
color: const Color(0xFF7D4698),
|
||||
child: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
const Icon(TorIcons.onionAlt, size: 84),
|
||||
const SizedBox(height: 16),
|
||||
@@ -92,7 +99,7 @@ class TorProxyScreen extends HookConsumerWidget {
|
||||
if (value) {
|
||||
await ref
|
||||
.read(torProxyServiceProvider.notifier)
|
||||
.connect();
|
||||
.startOrReconfigure();
|
||||
} else {
|
||||
await ref
|
||||
.read(torProxyServiceProvider.notifier)
|
||||
@@ -137,7 +144,7 @@ class TorProxyScreen extends HookConsumerWidget {
|
||||
subtitle: const Text(
|
||||
'When enabled, all Private Tabs will be tunneled through Tor',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.arrowDecision),
|
||||
secondary: const Icon(MdiIcons.incognito),
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveTorSettingsControllerProvider.notifier)
|
||||
@@ -179,27 +186,173 @@ class TorProxyScreen extends HookConsumerWidget {
|
||||
}
|
||||
return null; // Use the default color.
|
||||
}),
|
||||
value: torSettings.autoConfig,
|
||||
title: const Text('Configure Transport Automatically'),
|
||||
value: torSettings.config == TorConnectionConfig.auto,
|
||||
title: const Text('Auto Configure Transport'),
|
||||
subtitle: const Text(
|
||||
'From some locations, it is necessary to use a pluggable transport to connect to the Tor network that is otherwise blocked. Turn off automatic configuration to manually configure how Tor connects.',
|
||||
'From some locations, it is necessary to use a pluggable transport to connect to Tor',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.arrowDecision),
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveTorSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(currentSettings) =>
|
||||
currentSettings.copyWith.autoConfig(value),
|
||||
);
|
||||
},
|
||||
secondary: const Icon(MdiIcons.arrowDecisionAuto),
|
||||
onChanged: torProxyPort.isLoading
|
||||
? null
|
||||
: (value) async {
|
||||
await ref
|
||||
.read(
|
||||
saveTorSettingsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) =>
|
||||
currentSettings.copyWith.config(
|
||||
value
|
||||
? TorConnectionConfig.auto
|
||||
: TorConnectionConfig.direct,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (torSettings.config == TorConnectionConfig.auto) ...[
|
||||
SwitchListTile.adaptive(
|
||||
value: torSettings.requireBridge,
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 56,
|
||||
right: 24,
|
||||
),
|
||||
onChanged: torProxyPort.isLoading
|
||||
? null
|
||||
: (value) async {
|
||||
await ref
|
||||
.read(
|
||||
saveTorSettingsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) => currentSettings
|
||||
.copyWith
|
||||
.requireBridge(value),
|
||||
);
|
||||
},
|
||||
title: const Text(
|
||||
"I'm sure I cannot connect without a bridge",
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
RadioGroup(
|
||||
groupValue: torSettings.config,
|
||||
onChanged: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(
|
||||
saveTorSettingsControllerProvider.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) =>
|
||||
currentSettings.copyWith.config(value),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: RadioListTile.adaptive(
|
||||
value: TorConnectionConfig.direct,
|
||||
enabled: !torProxyPort.isLoading,
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 56,
|
||||
right: 24,
|
||||
),
|
||||
title: Text('Direct Connection'),
|
||||
subtitle: Text(
|
||||
'The best way to connect to Tor if Tor is not blocked',
|
||||
),
|
||||
),
|
||||
),
|
||||
RadioGroup(
|
||||
groupValue: torSettings.config,
|
||||
onChanged: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(
|
||||
saveTorSettingsControllerProvider.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) =>
|
||||
currentSettings.copyWith.config(value),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: RadioListTile.adaptive(
|
||||
value: TorConnectionConfig.obfs4,
|
||||
enabled: !torProxyPort.isLoading,
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 56,
|
||||
right: 24,
|
||||
),
|
||||
title: Text('obfs4'),
|
||||
subtitle: Text(
|
||||
'Suitable for light censorship and high bandwidth needs',
|
||||
),
|
||||
),
|
||||
),
|
||||
RadioGroup(
|
||||
groupValue: torSettings.config,
|
||||
onChanged: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(
|
||||
saveTorSettingsControllerProvider.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) =>
|
||||
currentSettings.copyWith.config(value),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: RadioListTile.adaptive(
|
||||
value: TorConnectionConfig.snowflake,
|
||||
enabled: !torProxyPort.isLoading,
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 56,
|
||||
right: 24,
|
||||
),
|
||||
title: Text('Snowflake'),
|
||||
subtitle: Text('Suitable for heavy censorship'),
|
||||
),
|
||||
),
|
||||
CheckboxListTile.adaptive(
|
||||
value: torSettings.fetchRemoteBridges,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
enabled:
|
||||
torSettings.config != TorConnectionConfig.direct,
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 56,
|
||||
right: 24,
|
||||
),
|
||||
onChanged: torProxyPort.isLoading
|
||||
? null
|
||||
: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(
|
||||
saveTorSettingsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) => currentSettings
|
||||
.copyWith
|
||||
.fetchRemoteBridges(value),
|
||||
);
|
||||
}
|
||||
},
|
||||
title: const Text(
|
||||
"Fetch fresh Bridges before connecting",
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (torProxyPort.isLoading)
|
||||
const Column(
|
||||
|
||||
@@ -35,16 +35,19 @@ Future<void> onStart(ServiceInstance service) async {
|
||||
service.invoke('portUpdate', {'port': port});
|
||||
});
|
||||
|
||||
Future<void> stopProxies() async {
|
||||
for (final proxyType in startedProxyType) {
|
||||
await IPtProxyController().stop(proxyType);
|
||||
}
|
||||
startedProxyType.clear();
|
||||
}
|
||||
|
||||
Future<void> stopService() async {
|
||||
timeout?.cancel();
|
||||
timeout = null;
|
||||
|
||||
Tor.instance.stop();
|
||||
|
||||
for (final proxyType in startedProxyType) {
|
||||
await IPtProxyController().stop(proxyType);
|
||||
}
|
||||
startedProxyType.clear();
|
||||
await stopProxies();
|
||||
|
||||
service.invoke('portUpdate', {'port': -1});
|
||||
|
||||
@@ -84,12 +87,57 @@ Future<void> onStart(ServiceInstance service) async {
|
||||
}
|
||||
}
|
||||
|
||||
service.on("start").listen((event) async {
|
||||
Future<void> reconfigureService(
|
||||
ProxyType? proxyType,
|
||||
String? bridgeLines,
|
||||
) async {
|
||||
if (Tor.instance.hasClient) {
|
||||
await stopProxies();
|
||||
|
||||
if (proxyType != null) {
|
||||
final port = await IPtProxyController().start(proxyType, "");
|
||||
startedProxyType.add(proxyType);
|
||||
|
||||
switch (proxyType) {
|
||||
case ProxyType.obfs4:
|
||||
await Tor.instance.reconfigure(
|
||||
obfs4Port: port,
|
||||
bridgeLines: bridgeLines,
|
||||
);
|
||||
case ProxyType.meekLite:
|
||||
throw UnimplementedError();
|
||||
case ProxyType.webtunnel:
|
||||
throw UnimplementedError();
|
||||
case ProxyType.snowflake:
|
||||
await Tor.instance.reconfigure(
|
||||
snowflakePort: port,
|
||||
bridgeLines: bridgeLines,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await Tor.instance.reconfigure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service.on("startOrReconfigure").listen((event) async {
|
||||
final proxyType = ProxyType.values.firstWhereOrNull(
|
||||
(x) => x.name == event?['proxyType'],
|
||||
);
|
||||
|
||||
await startService(proxyType, event?['bridgeLines'] as String?);
|
||||
await reconfigureService(proxyType, event?['bridgeLines'] as String?);
|
||||
|
||||
if (!Tor.instance.hasClient) {
|
||||
await startService(proxyType, event?['bridgeLines'] as String?);
|
||||
}
|
||||
});
|
||||
|
||||
service.on("reconfigure").listen((event) async {
|
||||
final proxyType = ProxyType.values.firstWhereOrNull(
|
||||
(x) => x.name == event?['proxyType'],
|
||||
);
|
||||
|
||||
await reconfigureService(proxyType, event?['bridgeLines'] as String?);
|
||||
});
|
||||
|
||||
service.on("heartbeat").listen((event) {
|
||||
|
||||
@@ -42,7 +42,12 @@ class SettingDao extends DatabaseAccessor<UserDatabase> with _$SettingDaoMixin {
|
||||
partitionKey: Value(partitionKey),
|
||||
value: Value(driftvalue),
|
||||
),
|
||||
onConflict: DoUpdate((old) => SettingCompanion(value: Value(driftvalue))),
|
||||
onConflict: DoUpdate(
|
||||
(old) => SettingCompanion(
|
||||
partitionKey: Value(partitionKey),
|
||||
value: Value(driftvalue),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,8 @@ Map<String, dynamic> _$GeneralSettingsToJson(
|
||||
'showExtensionShortcut': instance.showExtensionShortcut,
|
||||
'enableLocalAiFeatures': instance.enableLocalAiFeatures,
|
||||
'defaultCreateTabType': _$TabTypeEnumMap[instance.defaultCreateTabType]!,
|
||||
'defaultIntentTabType': _$TabTypeEnumMap[instance.defaultIntentTabType]!,
|
||||
'tabIntentOpenSetting':
|
||||
_$TabIntentOpenSettingEnumMap[instance.tabIntentOpenSetting]!,
|
||||
'autoHideTabBar': instance.autoHideTabBar,
|
||||
'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!,
|
||||
};
|
||||
|
||||
@@ -4,17 +4,32 @@ import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'tor_settings.g.dart';
|
||||
|
||||
enum TorConnectionConfig { auto, direct, obfs4, snowflake }
|
||||
|
||||
@CopyWith()
|
||||
@JsonSerializable(includeIfNull: true, constructor: 'withDefaults')
|
||||
class TorSettings with FastEquatable {
|
||||
final bool proxyPrivateTabsTor;
|
||||
final bool autoConfig;
|
||||
final TorConnectionConfig config;
|
||||
final bool requireBridge;
|
||||
final bool fetchRemoteBridges;
|
||||
|
||||
TorSettings({required this.proxyPrivateTabsTor, required this.autoConfig});
|
||||
TorSettings({
|
||||
required this.proxyPrivateTabsTor,
|
||||
required this.config,
|
||||
required this.requireBridge,
|
||||
required this.fetchRemoteBridges,
|
||||
});
|
||||
|
||||
TorSettings.withDefaults({bool? proxyPrivateTabsTor, bool? autoConfig})
|
||||
: proxyPrivateTabsTor = proxyPrivateTabsTor ?? false,
|
||||
autoConfig = autoConfig ?? true;
|
||||
TorSettings.withDefaults({
|
||||
bool? proxyPrivateTabsTor,
|
||||
TorConnectionConfig? config,
|
||||
bool? requireBridge,
|
||||
bool? fetchRemoteBridges,
|
||||
}) : proxyPrivateTabsTor = proxyPrivateTabsTor ?? false,
|
||||
config = config ?? TorConnectionConfig.auto,
|
||||
requireBridge = requireBridge ?? false,
|
||||
fetchRemoteBridges = fetchRemoteBridges ?? true;
|
||||
|
||||
factory TorSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$TorSettingsFromJson(json);
|
||||
@@ -22,5 +37,10 @@ class TorSettings with FastEquatable {
|
||||
Map<String, dynamic> toJson() => _$TorSettingsToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [proxyPrivateTabsTor, autoConfig];
|
||||
List<Object?> get hashParameters => [
|
||||
proxyPrivateTabsTor,
|
||||
config,
|
||||
requireBridge,
|
||||
fetchRemoteBridges,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -9,7 +9,11 @@ part of 'tor_settings.dart';
|
||||
abstract class _$TorSettingsCWProxy {
|
||||
TorSettings proxyPrivateTabsTor(bool proxyPrivateTabsTor);
|
||||
|
||||
TorSettings autoConfig(bool autoConfig);
|
||||
TorSettings config(TorConnectionConfig config);
|
||||
|
||||
TorSettings requireBridge(bool requireBridge);
|
||||
|
||||
TorSettings fetchRemoteBridges(bool fetchRemoteBridges);
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TorSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
@@ -17,7 +21,12 @@ abstract class _$TorSettingsCWProxy {
|
||||
/// ```dart
|
||||
/// TorSettings(...).copyWith(id: 12, name: "My name")
|
||||
/// ````
|
||||
TorSettings call({bool proxyPrivateTabsTor, bool autoConfig});
|
||||
TorSettings call({
|
||||
bool proxyPrivateTabsTor,
|
||||
TorConnectionConfig config,
|
||||
bool requireBridge,
|
||||
bool fetchRemoteBridges,
|
||||
});
|
||||
}
|
||||
|
||||
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfTorSettings.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfTorSettings.copyWith.fieldName(...)`
|
||||
@@ -31,7 +40,15 @@ class _$TorSettingsCWProxyImpl implements _$TorSettingsCWProxy {
|
||||
this(proxyPrivateTabsTor: proxyPrivateTabsTor);
|
||||
|
||||
@override
|
||||
TorSettings autoConfig(bool autoConfig) => this(autoConfig: autoConfig);
|
||||
TorSettings config(TorConnectionConfig config) => this(config: config);
|
||||
|
||||
@override
|
||||
TorSettings requireBridge(bool requireBridge) =>
|
||||
this(requireBridge: requireBridge);
|
||||
|
||||
@override
|
||||
TorSettings fetchRemoteBridges(bool fetchRemoteBridges) =>
|
||||
this(fetchRemoteBridges: fetchRemoteBridges);
|
||||
|
||||
@override
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `TorSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
@@ -42,17 +59,27 @@ class _$TorSettingsCWProxyImpl implements _$TorSettingsCWProxy {
|
||||
/// ````
|
||||
TorSettings call({
|
||||
Object? proxyPrivateTabsTor = const $CopyWithPlaceholder(),
|
||||
Object? autoConfig = const $CopyWithPlaceholder(),
|
||||
Object? config = const $CopyWithPlaceholder(),
|
||||
Object? requireBridge = const $CopyWithPlaceholder(),
|
||||
Object? fetchRemoteBridges = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return TorSettings(
|
||||
proxyPrivateTabsTor: proxyPrivateTabsTor == const $CopyWithPlaceholder()
|
||||
? _value.proxyPrivateTabsTor
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: proxyPrivateTabsTor as bool,
|
||||
autoConfig: autoConfig == const $CopyWithPlaceholder()
|
||||
? _value.autoConfig
|
||||
config: config == const $CopyWithPlaceholder()
|
||||
? _value.config
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: autoConfig as bool,
|
||||
: config as TorConnectionConfig,
|
||||
requireBridge: requireBridge == const $CopyWithPlaceholder()
|
||||
? _value.requireBridge
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: requireBridge as bool,
|
||||
fetchRemoteBridges: fetchRemoteBridges == const $CopyWithPlaceholder()
|
||||
? _value.fetchRemoteBridges
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: fetchRemoteBridges as bool,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -70,11 +97,22 @@ extension $TorSettingsCopyWith on TorSettings {
|
||||
TorSettings _$TorSettingsFromJson(Map<String, dynamic> json) =>
|
||||
TorSettings.withDefaults(
|
||||
proxyPrivateTabsTor: json['proxyPrivateTabsTor'] as bool?,
|
||||
autoConfig: json['autoConfig'] as bool?,
|
||||
config: $enumDecodeNullable(_$TorConnectionConfigEnumMap, json['config']),
|
||||
requireBridge: json['requireBridge'] as bool?,
|
||||
fetchRemoteBridges: json['fetchRemoteBridges'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TorSettingsToJson(TorSettings instance) =>
|
||||
<String, dynamic>{
|
||||
'proxyPrivateTabsTor': instance.proxyPrivateTabsTor,
|
||||
'autoConfig': instance.autoConfig,
|
||||
'config': _$TorConnectionConfigEnumMap[instance.config]!,
|
||||
'requireBridge': instance.requireBridge,
|
||||
'fetchRemoteBridges': instance.fetchRemoteBridges,
|
||||
};
|
||||
|
||||
const _$TorConnectionConfigEnumMap = {
|
||||
TorConnectionConfig.auto: 'auto',
|
||||
TorConnectionConfig.direct: 'direct',
|
||||
TorConnectionConfig.obfs4: 'obfs4',
|
||||
TorConnectionConfig.snowflake: 'snowflake',
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ final generalSettingsWithDefaultsProvider =
|
||||
typedef GeneralSettingsWithDefaultsRef =
|
||||
AutoDisposeProviderRef<GeneralSettings>;
|
||||
String _$generalSettingsRepositoryHash() =>
|
||||
r'07eeaf811fac4b9c787cce7c29379a7329910ae5';
|
||||
r'0d207ae4ae9fd94b1257eb8aa4cfd73d4a47caf2';
|
||||
|
||||
/// See also [GeneralSettingsRepository].
|
||||
@ProviderFor(GeneralSettingsRepository)
|
||||
|
||||
@@ -23,7 +23,12 @@ class TorSettingsRepository extends _$TorSettingsRepository {
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'autoConfig': settings['autoConfig']?.readAs(
|
||||
'config': settings['config']?.readAs(DriftSqlType.string, db.typeMapping),
|
||||
'requireBridge': settings['requireBridge']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'fetchRemoteBridges': settings['fetchRemoteBridges']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
|
||||
@@ -26,7 +26,7 @@ final torSettingsWithDefaultsProvider =
|
||||
// ignore: unused_element
|
||||
typedef TorSettingsWithDefaultsRef = AutoDisposeProviderRef<TorSettings>;
|
||||
String _$torSettingsRepositoryHash() =>
|
||||
r'686e8576453f33a0d2d0eb2869e29ca88e00bc37';
|
||||
r'ef80d1cc6b9b72cce371c03c656b10a0924d49d0';
|
||||
|
||||
/// See also [TorSettingsRepository].
|
||||
@ProviderFor(TorSettingsRepository)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
- drift: true
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>readability [24 Aug 2025 at 12:45]</title>
|
||||
<title>readability [12 Sep 2025 at 07:08]</title>
|
||||
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"hash": "1d5bede379cdf6035685",
|
||||
"version": "5.97.1",
|
||||
"time": 548,
|
||||
"builtAt": 1756032309689,
|
||||
"time": 557,
|
||||
"builtAt": 1757653694040,
|
||||
"publicPath": "auto",
|
||||
"outputPath": "/home/fafre/development/repos/lensai/packages/flutter_mozilla_components/javascript/readability/dist",
|
||||
"assetsByChunkName": {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export 'src/data/models/moat.dart';
|
||||
export 'src/data/service/moat_service.dart';
|
||||
export 'src/pigeons/proxy.g.dart' show IPtProxyController, ProxyType;
|
||||
|
||||
@@ -83,13 +83,19 @@ class Setting {
|
||||
|
||||
@JsonSerializable()
|
||||
class Bridge {
|
||||
final String type;
|
||||
@JsonKey(toJson: _transportToJson, fromJson: _transportFromJson)
|
||||
final TransportType type;
|
||||
final String source;
|
||||
@JsonKey(name: 'bridge_strings')
|
||||
final List<String>? bridges;
|
||||
|
||||
const Bridge({required this.type, required this.source, this.bridges});
|
||||
|
||||
static String _transportToJson(TransportType transport) => transport.value;
|
||||
|
||||
static TransportType _transportFromJson(dynamic json) =>
|
||||
TransportType.fromString(json as String)!;
|
||||
|
||||
factory Bridge.fromJson(Map<String, dynamic> json) => _$BridgeFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$BridgeToJson(this);
|
||||
|
||||
@@ -50,7 +50,7 @@ Map<String, dynamic> _$SettingToJson(Setting instance) => <String, dynamic>{
|
||||
};
|
||||
|
||||
Bridge _$BridgeFromJson(Map<String, dynamic> json) => Bridge(
|
||||
type: json['type'] as String,
|
||||
type: Bridge._transportFromJson(json['type']),
|
||||
source: json['source'] as String,
|
||||
bridges: (json['bridge_strings'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
@@ -58,7 +58,7 @@ Bridge _$BridgeFromJson(Map<String, dynamic> json) => Bridge(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$BridgeToJson(Bridge instance) => <String, dynamic>{
|
||||
'type': instance.type,
|
||||
'type': Bridge._transportToJson(instance.type),
|
||||
'source': instance.source,
|
||||
'bridge_strings': instance.bridges,
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ import 'package:socks5_proxy/socks_client.dart';
|
||||
const String _meekParameters =
|
||||
'url=https://1723079976.rsc.cdn77.org;front=www.phpmyadmin.net';
|
||||
|
||||
const defaultBridges = [TransportType.obfs4, TransportType.snowflake];
|
||||
|
||||
/// Manages MOAT API connections with persistent proxy setup.
|
||||
class MoatService {
|
||||
MoatApi? _api;
|
||||
@@ -66,10 +68,10 @@ class MoatService {
|
||||
}
|
||||
|
||||
/// Converts BuiltInBridges to Settings for requested transports.
|
||||
List<Setting> _convertBuiltinToSettings(
|
||||
BuiltInBridges builtinBridges,
|
||||
List<TransportType> transports,
|
||||
) {
|
||||
static List<Setting> convertBuiltinToSettings(
|
||||
BuiltInBridges builtinBridges, {
|
||||
List<TransportType> transports = defaultBridges,
|
||||
}) {
|
||||
final settings = <Setting>[];
|
||||
|
||||
for (final transport in transports) {
|
||||
@@ -85,7 +87,7 @@ class MoatService {
|
||||
settings.add(
|
||||
Setting(
|
||||
bridge: Bridge(
|
||||
type: transport.value,
|
||||
type: transport,
|
||||
source: 'builtin',
|
||||
bridges: bridgeStrings,
|
||||
),
|
||||
@@ -98,29 +100,20 @@ class MoatService {
|
||||
}
|
||||
|
||||
/// Gets built-in bridges from the MOAT service endpoint.
|
||||
Future<List<Setting>?> getBuiltinBridges({
|
||||
List<TransportType> transports = const [
|
||||
TransportType.obfs4,
|
||||
TransportType.snowflake,
|
||||
TransportType.meek,
|
||||
],
|
||||
Future<BuiltInBridges> getBuiltinBridges({
|
||||
List<TransportType> transports = defaultBridges,
|
||||
}) async {
|
||||
_ensureInitialized();
|
||||
|
||||
final builtinBridges = await _api!.builtin();
|
||||
final settings = _convertBuiltinToSettings(builtinBridges, transports);
|
||||
return settings.isEmpty ? null : settings;
|
||||
return builtinBridges;
|
||||
}
|
||||
|
||||
/// Tries to automatically configure Pluggable Transports.
|
||||
Future<List<Setting>?> autoConf({
|
||||
String? country,
|
||||
bool cannotConnectWithoutPt = false,
|
||||
List<TransportType> transports = const [
|
||||
TransportType.obfs4,
|
||||
TransportType.snowflake,
|
||||
TransportType.webtunnel,
|
||||
],
|
||||
List<TransportType> transports = defaultBridges,
|
||||
}) async {
|
||||
_ensureInitialized();
|
||||
|
||||
@@ -146,6 +139,18 @@ class MoatService {
|
||||
return response.settings;
|
||||
}
|
||||
|
||||
Future<List<Setting>?> getDefaultBridges({
|
||||
List<TransportType> transports = defaultBridges,
|
||||
}) async {
|
||||
_ensureInitialized();
|
||||
|
||||
final response = await _api!.defaults(
|
||||
SettingsRequest(transports: transports),
|
||||
);
|
||||
|
||||
return response.settings;
|
||||
}
|
||||
|
||||
/// Gets the list of supported countries from the MOAT service.
|
||||
Future<List<String>> getCountries() async {
|
||||
_ensureInitialized();
|
||||
|
||||
@@ -13,10 +13,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
http: ^1.5.0
|
||||
json_annotation: ^4.9.0
|
||||
socks5_proxy:
|
||||
git:
|
||||
url: https://github.com/sylvieon/socks_dart.git
|
||||
ref: 04550bc08f42cc1a35f1e870f651b93a4f9aceab
|
||||
socks5_proxy: ^2.1.1
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.15
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
BASE=$(dirname "$0")
|
||||
|
||||
cd "$BASE/../packages/flutter_mozilla_components/javascript/container_proxy" || exit
|
||||
|
||||
npm ci
|
||||
npx webpack
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
BASE=$(dirname "$0")
|
||||
|
||||
cd "$BASE/../packages/flutter_mozilla_components/javascript/readability" || exit
|
||||
|
||||
npm ci
|
||||
npx webpack
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
BASE=$(dirname "$0")
|
||||
|
||||
cd "$BASE/../app/assets/preferences" || exit
|
||||
|
||||
rm -f builtin-bridges.json
|
||||
|
||||
wget -O builtin-bridges.json https://bridges.torproject.org/moat/circumvention/builtin
|
||||
Reference in New Issue
Block a user