diff --git a/Monal/Classes/MLSettingsTableViewController.m b/Monal/Classes/MLSettingsTableViewController.m index 48f010f3b..326b12032 100644 --- a/Monal/Classes/MLSettingsTableViewController.m +++ b/Monal/Classes/MLSettingsTableViewController.m @@ -366,15 +366,16 @@ -(void)tableView:(UITableView*) tableView didSelectRowAtIndexPath:(NSIndexPath*) else { switch(indexPath.row - [self getAccountNum]) { - case QuickSettingsRow: - { + case QuickSettingsRow: { UIViewController* loginView = [[SwiftuiInterface new] makeViewWithName:@"LogIn"]; [self showDetailViewController:loginView sender:self]; break; } - case AdvancedSettingsRow: - [self performSegueWithIdentifier:@"editXMPP" sender:self]; + case AdvancedSettingsRow: { + UIViewController* advancedLoginView = [[SwiftuiInterface new] makeViewWithName:@"AdvancedLogIn"]; + [self showDetailViewController:advancedLoginView sender:self]; break; + } default: unreachable(); } diff --git a/Monal/Classes/MLXMPPManager.h b/Monal/Classes/MLXMPPManager.h index 511f2583b..97ad99e41 100644 --- a/Monal/Classes/MLXMPPManager.h +++ b/Monal/Classes/MLXMPPManager.h @@ -87,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN -(NSDate *) connectedTimeFor:(NSNumber*) accountID; -(NSNumber* _Nullable) login:(NSString*) jid password:(NSString*) password; +-(NSNumber*) login:(NSString*) jid password:(NSString*) password hardcodedServer:(NSString* _Nullable) hardcodedServer hardcodedPort:(NSString* _Nullable) hardcodedPort forceDirectTLS:(BOOL) directTLS allowPlainAuth:(BOOL) plainActivated; -(void) removeAccountForAccountID:(NSNumber*) accountID; -(void) addNewAccountToKeychainAndConnectWithPassword:(NSString*) password andAccountID:(NSNumber*) accountID; diff --git a/Monal/Classes/MLXMPPManager.m b/Monal/Classes/MLXMPPManager.m index 0a3b38cd7..72e0e52c8 100644 --- a/Monal/Classes/MLXMPPManager.m +++ b/Monal/Classes/MLXMPPManager.m @@ -713,6 +713,26 @@ -(void) sendChatState:(BOOL) isTyping toContact:(MLContact*) contact #pragma mark - login/register -(NSNumber*) login:(NSString*) jid password:(NSString*) password +{ + NSArray* elements = [jid componentsSeparatedByString:@"@"]; + MLAssert([elements count] > 1, @"Got invalid jid", (@{@"jid": nilWrapper(jid), @"elements": elements})); + NSString* domain = ((NSString*)[elements objectAtIndex:1]).lowercaseString; + + //we don't want to set kPlainActivated (not even according to our preload list) and default to plain_activated=false, + //because the error message will warn the user and direct them to the advanced account creation menu to activate PLAIN + //if they still want to connect to this server + //only exception: yax.im --> we don't want to suggest a server during account creation that has a scary warning + //when logging in using another device afterwards + //TODO: to be removed once yax.im and quicksy.im supports SASL2 and SSDP!! + //TODO: use preload list and allow PLAIN for all others once enough domains are on this list + //allow plain for all servers not on preload list, since prosody with SASL2 wasn't even released yet + BOOL defaultPlainActivated = YES; + BOOL plainActivated = ([domain isEqualToString:@"yax.im"] || [domain isEqualToString:@"quicksy.im"]) ? YES : defaultPlainActivated; + +return [self login:jid password:password hardcodedServer:nil hardcodedPort:nil forceDirectTLS:NO allowPlainAuth:plainActivated]; +} + +-(NSNumber*) login:(NSString*) jid password:(NSString*) password hardcodedServer:(NSString*) hardcodedServer hardcodedPort:(NSString*) hardcodedPort forceDirectTLS:(BOOL) directTLS allowPlainAuth:(BOOL) plainActivated { //check if it is a JID NSArray* elements = [jid componentsSeparatedByString:@"@"]; @@ -737,17 +757,12 @@ -(NSNumber*) login:(NSString*) jid password:(NSString*) password [dic setObject:user forKey:kUsername]; [dic setObject:[HelperTools encodeRandomResource] forKey:kResource]; [dic setObject:@YES forKey:kEnabled]; - [dic setObject:@NO forKey:kDirectTLS]; - //we don't want to set kPlainActivated (not even according to our preload list) and default to plain_activated=false, - //because the error message will warn the user and direct them to the advanced account creation menu to activate PLAIN - //if they still want to connect to this server - //only exception: yax.im --> we don't want to suggest a server during account creation that has a scary warning - //when logging in using another device afterwards - //TODO: to be removed once yax.im and quicksy.im supports SASL2 and SSDP!! - //TODO: use preload list and allow PLAIN for all others once enough domains are on this list - //allow plain for all servers not on preload list, since prosody with SASL2 wasn't even released yet - NSNumber* defaultPlainActivated = @YES; - [dic setObject:([domain isEqualToString:@"yax.im"] || [domain isEqualToString:@"quicksy.im"] ? @YES : defaultPlainActivated) forKey:kPlainActivated]; + if(hardcodedServer != nil) + [dic setObject:hardcodedServer forKey:kServer]; + if(hardcodedPort != nil) + [dic setObject:hardcodedPort forKey:kPort]; + [dic setObject:@(directTLS) forKey:kDirectTLS]; + [dic setObject:@(plainActivated) forKey:kPlainActivated]; NSNumber* accountID = [[DataLayer sharedInstance] addAccountWithDictionary:dic]; if(accountID == nil) diff --git a/Monal/Classes/SwiftuiHelpers.swift b/Monal/Classes/SwiftuiHelpers.swift index f02f38cc1..61f0ae684 100644 --- a/Monal/Classes/SwiftuiHelpers.swift +++ b/Monal/Classes/SwiftuiHelpers.swift @@ -809,6 +809,8 @@ class SwiftuiInterface : NSObject { host = UIHostingController(rootView:AnyView(AddTopLevelNavigation(withDelegate:delegate, to:WelcomeLogIn(delegate:delegate)))) case "LogIn": host = UIHostingController(rootView:AnyView(UIKitWorkaround(WelcomeLogIn(delegate:delegate)))) + case "AdvancedLogIn": + host = UIHostingController(rootView:AnyView(UIKitWorkaround(WelcomeLogIn(advancedMode: true, delegate: delegate)))) case "ChatPlaceholder": host = UIHostingController(rootView:AnyView(ChatPlaceholder())) case "GeneralSettings" : diff --git a/Monal/Classes/WelcomeLogIn.swift b/Monal/Classes/WelcomeLogIn.swift index f8f641629..c97bd41c3 100644 --- a/Monal/Classes/WelcomeLogIn.swift +++ b/Monal/Classes/WelcomeLogIn.swift @@ -9,6 +9,7 @@ struct WelcomeLogIn: View { static private let credFaultyPattern = "^.+@.+\\..{2,}$" + var advancedMode: Bool = false var delegate: SheetDismisserProtocol @State private var isEditingJid: Bool = false @@ -16,6 +17,11 @@ struct WelcomeLogIn: View { @State private var isEditingPassword: Bool = false @State private var password: String = "" + @State private var hardcodedServer: String = "" + @State private var hardcodedPort: String = "" + @State private var allowPlainAuth: Bool = false + @State private var forceDirectTLS: Bool = false + @State private var showAlert = false @State private var showQRCodeScanner = false @@ -26,7 +32,7 @@ struct WelcomeLogIn: View { @State private var loginComplete = false @State private var isLoadingOmemoBundles = false - @State private var alertPrompt = AlertPrompt(dismissLabel: Text("Close")) + @State private var alertPrompt = AlertPrompt() @StateObject private var overlay = LoadingOverlayState() #if IS_ALPHA @@ -40,18 +46,21 @@ struct WelcomeLogIn: View { private var credentialsEnteredAlert: Bool { alertPrompt.title = Text("Empty Values!") alertPrompt.message = Text("Please make sure you have entered both a username and password.") + alertPrompt.dismissLabel = Text("Close") return credentialsEntered } private var credentialsFaultyAlert: Bool { alertPrompt.title = Text("Invalid Credentials!") alertPrompt.message = Text("Your XMPP jid should be in in the format user@domain.tld. For special configurations, use manual setup.") + alertPrompt.dismissLabel = Text("Close") return credentialsFaulty } private var credentialsExistAlert: Bool { alertPrompt.title = Text("Duplicate jid!") alertPrompt.message = Text("This account already exists in Monal.") + alertPrompt.dismissLabel = Text("Close") return credentialsExist } @@ -60,6 +69,7 @@ struct WelcomeLogIn: View { hideLoadingOverlay(overlay) alertPrompt.title = Text("Timeout Error") alertPrompt.message = Text("We were not able to connect your account. Please check your username and password and make sure you are connected to the internet.") + alertPrompt.dismissLabel = Text("Close") showAlert = true } @@ -67,6 +77,7 @@ struct WelcomeLogIn: View { hideLoadingOverlay(overlay) alertPrompt.title = Text("Success!") alertPrompt.message = Text("You are set up and connected.") + alertPrompt.dismissLabel = Text("Close") showAlert = true } @@ -74,6 +85,14 @@ struct WelcomeLogIn: View { hideLoadingOverlay(overlay) alertPrompt.title = Text("Error") alertPrompt.message = Text(String(format: NSLocalizedString("We were not able to connect your account. Please check your username and password and make sure you are connected to the internet.\n\nTechnical error message: %@", comment: ""), errorMessage)) + alertPrompt.dismissLabel = Text("Close") + showAlert = true + } + + private func showPlainAuthWarningAlert() { + alertPrompt.title = Text("Warning") + alertPrompt.message = Text("If you turn this on, you will no longer be safe from man-in-the-middle attacks. Such attacks enable the adversary to manipulate your incoming and outgoing messages, add their own OMEMO keys, change your account details and even know or change your password!\n\nYou should rather switch to another server than turning this on.") + alertPrompt.dismissLabel = Text("Understood") showAlert = true } @@ -126,11 +145,16 @@ struct WelcomeLogIn: View { .frame(width: CGFloat(120), height: CGFloat(120), alignment: .center) .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) .padding() - - Text("Log in to your existing account or register a new account. If required you will find more advanced options in Monal settings.") - .padding() - .padding(.leading, -16.0) - + + if advancedMode { + Text("Log in to your existing account or register a new account.") + .padding() + .padding(.leading, -16.0) + } else { + Text("Log in to your existing account or register a new account. If required you will find more advanced options in Monal settings.") + .padding() + .padding(.leading, -16.0) + } } } .frame(maxWidth: .infinity) @@ -154,7 +178,34 @@ struct WelcomeLogIn: View { SecureField(NSLocalizedString("Password", comment: "placeholder when adding account"), text: $password) .addClearButton(isEditing: password.count > 0, text: $password) .listRowSeparator(.hidden) - + + if advancedMode { + TextField("Optional Hardcoded Hostname", text: $hardcodedServer) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .keyboardType(.URL) + .addClearButton(isEditing: hardcodedServer.count > 0, text: $hardcodedServer) + .listRowSeparator(.hidden) + + TextField("Optional Port", text: $hardcodedPort) + .keyboardType(.numberPad) + .addClearButton(isEditing: hardcodedPort.count > 0, text: $hardcodedPort) + .listRowSeparator(.hidden) + + Toggle(isOn: $forceDirectTLS){ + Text("Always use direct TLS, not STARTTLS") + } + + Toggle(isOn: $allowPlainAuth){ + Text("Allow MITM-prone PLAIN authentication") + } + .onChange(of: allowPlainAuth) { _ in + if allowPlainAuth { + showPlainAuthWarningAlert() + } + } + } + HStack() { Button(action: { showAlert = !credentialsEnteredAlert || credentialsFaultyAlert || credentialsExistAlert @@ -163,7 +214,11 @@ struct WelcomeLogIn: View { startLoginTimeout() showLoadingOverlay(overlay, headline:NSLocalizedString("Logging in", comment: "")) self.errorObserverEnabled = true - self.newAccountID = MLXMPPManager.sharedInstance().login(self.jid, password: self.password) + if advancedMode { + self.newAccountID = MLXMPPManager.sharedInstance().login(self.jid, password: self.password, hardcodedServer:self.hardcodedServer, hardcodedPort:self.hardcodedPort, forceDirectTLS: self.forceDirectTLS, allowPlainAuth: self.allowPlainAuth) + } else { + self.newAccountID = MLXMPPManager.sharedInstance().login(self.jid, password: self.password) + } if(self.newAccountID == nil) { currentTimeout = nil // <- disable timeout on error errorObserverEnabled = false @@ -214,7 +269,12 @@ struct WelcomeLogIn: View { ) } } - + .listRowSeparator(.hidden, edges: .top) + // Align the (bottom) list row separator to the very left + .alignmentGuide(.listRowSeparatorLeading) { _ in + return 0 + } + NavigationLink(destination: LazyClosureView(RegisterAccount(delegate: self.delegate))) { Text("Register a new account") .foregroundColor(Color.accentColor) @@ -243,7 +303,8 @@ struct WelcomeLogIn: View { } } .addLoadingOverlay(overlay) - .navigationBarTitle(Text("Welcome"), displayMode:.large) + .navigationTitle(Text("Welcome")) + .navigationBarTitleDisplayMode(advancedMode ? .inline : .large) .onDisappear {UITableView.appearance().tableHeaderView = nil} //why that?? .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kXMPPError")).receive(on: RunLoop.main)) { notification in if(self.errorObserverEnabled == false) {