diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 96d46b3..0ca3675 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -30,4 +30,6 @@
android:name="flutterEmbedding"
android:value="2" />
+
+
diff --git a/lib/core/features/action.dart b/lib/core/features/action.dart
index f26001a..135195b 100644
--- a/lib/core/features/action.dart
+++ b/lib/core/features/action.dart
@@ -66,6 +66,8 @@ class MUDAction {
};
String _doSpecialReplacements(GameStore store, String content) {
+ debugPrint('MUDAction._doSpecialReplacements: $content');
+ debugPrint("password: ${store.currentProfile.password}");
return content
.replaceAll('%PASSWORD', store.currentProfile.password)
.replaceAll('%USERNAME', store.currentProfile.username)
diff --git a/lib/core/features/profile.dart b/lib/core/features/profile.dart
index e341215..6d2124e 100644
--- a/lib/core/features/profile.dart
+++ b/lib/core/features/profile.dart
@@ -1,6 +1,9 @@
+import 'dart:convert';
+
import 'package:encrypt/encrypt.dart' as enc;
import 'package:flutter/foundation.dart';
+import '../consts.dart';
import '../secrets.dart';
import '../storage.dart';
import '../string_utils.dart';
@@ -52,15 +55,17 @@ class MUDProfile {
password: password ?? this.password,
);
- factory MUDProfile.fromJson(Map json) => MUDProfile(
- id: json['id'],
- name: json['name'],
- host: json['host'],
- port: json['port'],
- mccpEnabled: json['mccpEnabled'],
- username: json['username'],
- password: decrypt(json['password']),
- );
+ factory MUDProfile.fromJson(Map json) {
+ return MUDProfile(
+ id: json['id'],
+ name: json['name'],
+ host: json['host'],
+ port: json['port'],
+ mccpEnabled: json['mccpEnabled'],
+ username: json['username'],
+ password: decrypt(json['password']),
+ );
+ }
Map toJson() => {
'id': id,
@@ -74,48 +79,68 @@ class MUDProfile {
static Future save(MUDProfile profile) async {
debugPrint('MUDProfile.save: ${profile.id}');
- return ProfileStorage.writeProfileFile(profile.id, profile.id, profile.toJson());
+ return ProfileStorage.writeProfileFile(
+ profile.id, profile.id, jsonEncode(profile.toJson()));
}
Future> loadTriggers() async {
debugPrint('MUDProfile.loadTriggers: $id');
final triggers = await ProfileStorage.listProfileFiles(id, 'triggers');
- return triggers.values.map((e) => Trigger.fromJson(e)).toList();
+ final triggerFiles = [];
+ for (final trigger in triggers) {
+ debugPrint('MUDProfile.loadTriggers: $id/triggers/$trigger');
+ final triggerFile =
+ await ProfileStorage.readProfileFile(id, 'triggers/$trigger');
+ if (triggerFile != null) {
+ triggerFiles.add(triggerFile);
+ }
+ }
+ return triggerFiles.map((e) => Trigger.fromJson(jsonDecode(e))).toList();
}
Future> loadAliases() async {
debugPrint('MUDProfile.loadAliases: $id');
final aliases = await ProfileStorage.listProfileFiles(id, 'aliases');
- return aliases.values.map((e) => Alias.fromJson(e)).toList();
+ return aliases.map((e) => Alias.fromJson(jsonDecode(e))).toList();
}
Future saveAlias(Alias alias) async {
debugPrint('MUDProfile.saveAlias: $id/aliases/${alias.id}');
return ProfileStorage.writeProfileFile(
- id, 'aliases/${alias.id}', alias.toJson());
+ id, 'aliases/${alias.id}', jsonEncode(alias.toJson()));
}
Future saveTrigger(Trigger trigger) async {
debugPrint('MUDProfile.saveTrigger: $id/triggers/${trigger.id}');
return ProfileStorage.writeProfileFile(
- id, 'triggers/${trigger.id}', trigger.toJson());
+ id, 'triggers/${trigger.id}', jsonEncode(trigger.toJson()));
}
+ static final encKey = enc.Key.fromUtf8(pwdKey);
+ static final encrypter = enc.Encrypter(enc.AES(encKey, padding: null));
+ static final iv = enc.IV.fromLength(16);
+
static String encrypt(String password) {
if (password.isEmpty) {
return '';
}
- final key = enc.Key.fromUtf8(pwdKey);
- final encrypter = enc.Encrypter(enc.AES(key));
- final encrypted = encrypter.encrypt(password, iv: enc.IV.fromLength(16));
+ final encrypted = encrypter.encrypt(password, iv: iv);
+ // debugPrint('MUDProfile.encrypt: $password -> ${encrypted.base64}');
return encrypted.base64;
}
- static String decrypt(String json) {
- final key = enc.Key.fromUtf8(pwdKey);
- final encrypter = enc.Encrypter(enc.AES(key));
- final encrypted = enc.Encrypted.fromBase64(json);
- return encrypter.decrypt(encrypted, iv: enc.IV.fromLength(16));
+ static String decrypt(String password) {
+ if (password.isEmpty) {
+ return '';
+ }
+ try {
+ // debugPrint('MUDProfile.decrypt: $password');
+ final encrypted = enc.Encrypted.fromBase64(password);
+ return encrypter.decrypt(encrypted, iv: iv);
+ } catch (e, stack) {
+ debugPrint('MUDProfile.decrypt: $e$lf$stack');
+ return password;
+ }
}
}
diff --git a/lib/core/platform_utils.dart b/lib/core/platform_utils.dart
index 4334db9..da844f8 100644
--- a/lib/core/platform_utils.dart
+++ b/lib/core/platform_utils.dart
@@ -1,8 +1,34 @@
import 'dart:io';
+import 'package:path/path.dart' as path;
+import 'package:path_provider/path_provider.dart';
+
class PlatformUtils {
static get isDesktop =>
Platform.isMacOS || Platform.isWindows || Platform.isLinux;
static get isMobile => !isDesktop;
+
+ static Future getStorageBasePath() async {
+ switch (Platform.operatingSystem) {
+ case 'macos':
+ case 'linux':
+ final username = Platform.environment['USER'] ?? Platform.environment['USERNAME'];
+ return '/Users/$username/.config/mudblock';
+ case 'windows':
+ final username = Platform.environment['USERNAME'];
+ return 'C:\\Users\\$username\\AppData\\Roaming\\mudblock';
+ case 'android':
+ final base = await getExternalStorageDirectory();
+ if (base == null) {
+ throw UnsupportedError('External storage not available');
+ }
+ return path.join(base.path, 'mudblock');
+ case 'ios':
+ final base = await getApplicationDocumentsDirectory();
+ return path.join(base.path, 'mudblock');
+ default:
+ throw UnsupportedError('Unsupported platform');
+ }
+ }
}
diff --git a/lib/core/secrets.example.dart b/lib/core/secrets.example.dart
new file mode 100644
index 0000000..6c01470
--- /dev/null
+++ b/lib/core/secrets.example.dart
@@ -0,0 +1,5 @@
+// can be generated using `hexdump -vn16 -e'4/4 "%08X" 1 "\n"' /dev/urandom`
+// DO NOT LOSE THIS KEY!
+// copy this file to secrets.dart (which is in .gitignore)
+// in order to properly encrypt/decrypt passwords
+const pwdKey = "...";
diff --git a/lib/core/storage.dart b/lib/core/storage.dart
index 5b2373b..c84520a 100644
--- a/lib/core/storage.dart
+++ b/lib/core/storage.dart
@@ -1,55 +1,88 @@
+import 'dart:io';
+
import 'package:flutter/foundation.dart';
import 'package:localstore/localstore.dart';
import 'package:path/path.dart' as path;
-class FileStorage {
- static final Localstore _store = Localstore.instance;
+import 'platform_utils.dart';
- static Future