This commit is contained in:
Sarah Jamie Lewis 2021-12-17 16:54:30 -08:00
parent 9cb6eb857d
commit 6f3d5b65cd
9 changed files with 141 additions and 59 deletions

View File

@ -15,7 +15,9 @@ abstract class Cwtch {
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void LoadProfiles(String pass); void LoadProfiles(String pass);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void DeleteProfile(String onion, String pass); void DeleteProfile(String profile, String pass);
// ignore: non_constant_identifier_names
void ChangePassword(String profile, String pass, String newpass, String newpassAgain);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void ResetTor(); void ResetTor();

View File

@ -224,7 +224,10 @@ class CwtchNotifier {
} }
break; break;
case "SendMessageToPeerError": case "SendMessageToPeerError":
// Ignore // Ignore dealt with by IndexedFailure
break;
case "SendMessageToGroupError":
// Ignore dealt with by IndexedFailure
break; break;
case "IndexedFailure": case "IndexedFailure":
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]);

View File

@ -115,13 +115,13 @@ class CwtchFfi implements Cwtch {
} }
CwtchFfi(CwtchNotifier _cwtchNotifier) { CwtchFfi(CwtchNotifier _cwtchNotifier) {
String library_path = getLibraryPath(); String libraryPath = getLibraryPath();
if (library_path == UNSUPPORTED_OS) { if (libraryPath == UNSUPPORTED_OS) {
print("OS ${Platform.operatingSystem} not supported by cwtch/ffi"); print("OS ${Platform.operatingSystem} not supported by cwtch/ffi");
// emergency, ideally the app stays on splash and just posts the error till user closes // emergency, ideally the app stays on splash and just posts the error till user closes
exit(0); exit(0);
} }
library = DynamicLibrary.open(library_path); library = DynamicLibrary.open(libraryPath);
cwtchNotifier = _cwtchNotifier; cwtchNotifier = _cwtchNotifier;
} }
@ -716,4 +716,21 @@ class CwtchFfi implements Cwtch {
malloc.free(utf8profile); malloc.free(utf8profile);
return jsonMessage; return jsonMessage;
} }
@override
// ignore: non_constant_identifier_names
void ChangePassword(String profile, String pass, String newpass, String newpassAgain) {
var changePasswordC = library.lookup<NativeFunction<void_from_string_string_string_string_function>>("c_ChangePassword");
// ignore: non_constant_identifier_names
final ChangePasswordFn = changePasswordC.asFunction<VoidFromStringStringStringStringFn>();
final utf8profile = profile.toNativeUtf8();
final utf8pass = pass.toNativeUtf8();
final utf8newpass = newpass.toNativeUtf8();
final utf8newpasssagain = newpassAgain.toNativeUtf8();
ChangePasswordFn(utf8profile, utf8profile.length, utf8pass, utf8pass.length, utf8newpass, utf8newpass.length, utf8newpasssagain, utf8newpasssagain.length);
malloc.free(utf8profile);
malloc.free(utf8pass);
malloc.free(utf8newpass);
malloc.free(utf8newpasssagain);
}
} }

View File

@ -284,4 +284,9 @@ class CwtchGomobile implements Cwtch {
String defaultDownloadPath() { String defaultDownloadPath() {
return this.androidHomeDirectoryStr; return this.androidHomeDirectoryStr;
} }
@override
void ChangePassword(String profile, String pass, String newpass, String newpassAgain) {
cwtchPlatform.invokeMethod("ChangePassword", {"ProfileOnion": profile, "OldPass": pass, "NewPass": newpass, "NewPassAgain": newpassAgain});
}
} }

View File

@ -6,12 +6,17 @@ class ErrorHandler extends ChangeNotifier {
// Add Contact Specific Errors... // Add Contact Specific Errors...
static const String addContactErrorPrefix = "addcontact"; static const String addContactErrorPrefix = "addcontact";
static const String changePasswordErrorPrefix = "changepassword";
static const String invalidImportStringErrorType = "invalid_import_string"; static const String invalidImportStringErrorType = "invalid_import_string";
static const String contactAlreadyExistsErrorType = "contact_already_exists"; static const String contactAlreadyExistsErrorType = "contact_already_exists";
bool invalidImportStringError = false; bool invalidImportStringError = false;
bool contactAlreadyExistsError = false; bool contactAlreadyExistsError = false;
bool explicitAddContactSuccess = false; bool explicitAddContactSuccess = false;
// ChangePassword
bool changePasswordError = false;
bool explicitChangePasswordSuccess = false;
// Import Bundle Specific Errors // Import Bundle Specific Errors
static const String importBundleErrorPrefix = "importBundle"; static const String importBundleErrorPrefix = "importBundle";
bool importBundleError = false; bool importBundleError = false;
@ -39,6 +44,9 @@ class ErrorHandler extends ChangeNotifier {
deletedServerError = false; deletedServerError = false;
deletedServerSuccess = false; deletedServerSuccess = false;
changePasswordError = false;
explicitChangePasswordSuccess = false;
notifyListeners(); notifyListeners();
} }
@ -58,6 +66,9 @@ class ErrorHandler extends ChangeNotifier {
case deleteProfileErrorPrefix: case deleteProfileErrorPrefix:
handleDeleteProfileError(errorType); handleDeleteProfileError(errorType);
break; break;
case changePasswordErrorPrefix:
handleChangePasswordError(errorType);
break;
case deletedServerErrorPrefix: case deletedServerErrorPrefix:
handleDeletedServerError(errorType); handleDeletedServerError(errorType);
} }
@ -115,6 +126,21 @@ class ErrorHandler extends ChangeNotifier {
} }
} }
handleChangePasswordError(String errorType) {
// Reset add contact errors
changePasswordError = false;
explicitChangePasswordSuccess = false;
switch (errorType) {
case successErrorType:
explicitChangePasswordSuccess = true;
break;
default:
changePasswordError = true;
break;
}
}
handleDeletedServerError(String errorType) { handleDeletedServerError(String errorType) {
// reset // reset
deletedServerError = false; deletedServerError = false;

View File

@ -150,5 +150,6 @@ class MessageMetadata extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
MessageMetadata(this.profileOnion, this.conversationIdentifier, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd, this._error, this.isAuto); MessageMetadata(
this.profileOnion, this.conversationIdentifier, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd, this._error, this.isAuto);
} }

View File

@ -54,14 +54,16 @@ class FileMessage extends Message {
if (!validHash(rootHash, nonce)) { if (!validHash(rootHash, nonce)) {
return MessageRow(MalformedBubble()); return MessageRow(MalformedBubble());
} }
return FileBubble( return Container(
nameSuggestion, alignment: Alignment.center,
rootHash, child: FileBubble(
nonce, nameSuggestion,
fileSize, rootHash,
isAuto: metadata.isAuto, nonce,
interactive: false, fileSize,
); isAuto: metadata.isAuto,
interactive: false,
));
}); });
} }

View File

@ -1,6 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:cwtch/config.dart';
import 'package:cwtch/cwtch/cwtch.dart'; import 'package:cwtch/cwtch/cwtch.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -278,7 +280,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
// TODO Toast // TODO Toast
} }
void _createPressed() { void _createPressed() async {
// This will run all the validations in the form including // This will run all the validations in the form including
// checking that display name is not empty, and an actual check that the passwords // checking that display name is not empty, and an actual check that the passwords
// match (and are provided if the user has requested an encrypted profile). // match (and are provided if the user has requested an encrypted profile).
@ -301,17 +303,32 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
} else { } else {
// At this points passwords have been validated to be the same and not empty // At this points passwords have been validated to be the same and not empty
// Update both password and name, even if name hasn't been changed... // Update both password and name, even if name hasn't been changed...
var profile = Provider.of<ProfileInfoState>(context, listen: false).onion;
Provider.of<ProfileInfoState>(context, listen: false).nickname = ctrlrNick.value.text; Provider.of<ProfileInfoState>(context, listen: false).nickname = ctrlrNick.value.text;
Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(Provider.of<ProfileInfoState>(context, listen: false).onion, "profile.name", ctrlrNick.value.text); Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(profile, "profile.name", ctrlrNick.value.text);
final updatePasswordEvent = { Provider.of<FlwtchState>(context, listen: false).cwtch.ChangePassword(profile, ctrlrOldPass.text, ctrlrPass.text, ctrlrPass2.text);
"EventType": "ChangePassword",
"Data": {"Password": ctrlrOldPass.text, "NewPassword": ctrlrPass.text}
};
final updatePasswordEventJson = jsonEncode(updatePasswordEvent);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(Provider.of<ProfileInfoState>(context, listen: false).onion, updatePasswordEventJson); EnvironmentConfig.debugLog("waiting for change password response");
Future.delayed(const Duration(milliseconds: 500), () {
Navigator.of(context).pop(); if (globalErrorHandler.changePasswordError) {
// TODO: This isn't ideal, but because onChange can be fired during this future check
// and because the context can change after being popped we have this kind of double assertion...
// There is probably a better pattern to handle this...
if (AppLocalizations.of(context) != null) {
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.passwordChangeError));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
Navigator.pop(context);
return;
}
}
}).whenComplete(() {
if (globalErrorHandler.explicitChangePasswordSuccess) {
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.newPassword));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
Navigator.pop(context);
return; // otherwise round and round we go...
}
});
} }
} }
} }

View File

@ -83,8 +83,8 @@ class FileBubbleState extends State<FileBubble> {
} }
} }
return LayoutBuilder(builder: (bcontext, constraints) { return LayoutBuilder(builder: (bcontext, constraints) {
var wdgSender = Center( var wdgSender = Visibility(
widthFactor: 1, visible: widget.interactive,
child: SelectableText(senderDisplayStr + '\u202F', child: SelectableText(senderDisplayStr + '\u202F',
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()))); style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor())));
@ -99,40 +99,47 @@ class FileBubbleState extends State<FileBubble> {
if (!showFileSharing) { if (!showFileSharing) {
wdgDecorations = Text('\u202F'); wdgDecorations = Text('\u202F');
} else if (fromMe) { } else if (fromMe) {
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, prettyDate: prettyDate); wdgDecorations = Visibility(
visible: widget.interactive,
child: MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, prettyDate: prettyDate));
} else if (downloadComplete) { } else if (downloadComplete) {
// in this case, whatever marked download.complete would have also set the path // in this case, whatever marked download.complete would have also set the path
var lpath = path!.toLowerCase(); var lpath = path!.toLowerCase();
if (lpath.endsWith("jpg") || lpath.endsWith("jpeg") || lpath.endsWith("png") || lpath.endsWith("gif") || lpath.endsWith("webp") || lpath.endsWith("bmp")) { if (lpath.endsWith("jpg") || lpath.endsWith("jpeg") || lpath.endsWith("png") || lpath.endsWith("gif") || lpath.endsWith("webp") || lpath.endsWith("bmp")) {
isPreview = true; isPreview = true;
wdgDecorations = GestureDetector( wdgDecorations = Center(
child: Image.file( child: GestureDetector(
myFile!, child: Padding(
cacheWidth: 2048, // limit the amount of space the image can decode too, we keep this high-ish to allow quality previews... padding: EdgeInsets.all(1.0),
filterQuality: FilterQuality.medium, child: Image.file(
fit: BoxFit.fill, myFile!,
alignment: Alignment.center, cacheWidth: 2048, // limit the amount of space the image can decode too, we keep this high-ish to allow quality previews...
width: constraints.maxWidth, filterQuality: FilterQuality.medium,
isAntiAlias: false, fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) { alignment: Alignment.center,
return MalformedBubble(); height: MediaQuery.of(bcontext).size.height * 0.30,
}, isAntiAlias: false,
), errorBuilder: (context, error, stackTrace) {
return MalformedBubble();
},
)),
onTap: () { onTap: () {
pop(bcontext, myFile!, wdgMessage); pop(bcontext, myFile!, wdgMessage);
}, },
); ));
} else { } else {
wdgDecorations = Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F'); wdgDecorations = Visibility(visible: widget.interactive, child: Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F'));
} }
} else if (downloadActive) { } else if (downloadActive) {
if (!downloadGotManifest) { if (!downloadGotManifest) {
wdgDecorations = Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F'); wdgDecorations = Visibility(visible: widget.interactive, child: Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F'));
} else { } else {
wdgDecorations = LinearProgressIndicator( wdgDecorations = Visibility(
value: Provider.of<ProfileInfoState>(context).downloadProgress(widget.fileKey()), visible: widget.interactive,
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(), child: LinearProgressIndicator(
); value: Provider.of<ProfileInfoState>(context).downloadProgress(widget.fileKey()),
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
));
} }
} else if (flagStarted) { } else if (flagStarted) {
// in this case, the download was done in a previous application launch, // in this case, the download was done in a previous application launch,
@ -141,17 +148,21 @@ class FileBubbleState extends State<FileBubble> {
wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F'); wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F');
} else { } else {
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey()) ?? ""; var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey()) ?? "";
wdgDecorations = Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ wdgDecorations = Visibility(
Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'), visible: widget.interactive,
ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton)) child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
]); Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'),
ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton))
]));
} }
} else if (!widget.isAuto) { } else if (!widget.isAuto) {
wdgDecorations = Center( wdgDecorations = Visibility(
widthFactor: 1, visible: widget.interactive,
child: Wrap(children: [ child: Center(
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F'), onPressed: _btnAccept)), widthFactor: 1,
])); child: Wrap(children: [
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F'), onPressed: _btnAccept)),
])));
} else { } else {
wdgDecorations = Container(); wdgDecorations = Container();
} }
@ -174,9 +185,7 @@ class FileBubbleState extends State<FileBubble> {
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: fromMe children: fromMe ? [wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)] : [wdgSender, isPreview ? Container() : wdgMessage, wdgDecorations]),
? [wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)]
: [wdgSender, isPreview ? Container() : wdgMessage, Visibility(visible: widget.interactive, child: wdgDecorations)]),
)); ));
}); });
} }