SSL Handshake Prompt for IMAP (#381)

* Fix an incorrect namespace for copy auth url request.

* Implemented SSL handshake process for testing imap configuration.

* Implemented SSL handshake process for testing imap configuration.

* Replace certificate PathIcon with WinoFontIcon in XAML.
This commit is contained in:
Burak Kaan Köse
2024-09-14 21:51:43 +02:00
committed by GitHub
parent cad9250cb7
commit 56bfbeca58
24 changed files with 473 additions and 113 deletions

View File

@@ -1,21 +1,18 @@
using System; using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Core.Domain.Models.AutoDiscovery;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions
{ {
public class ImapConnectionFailedPackage public class ImapConnectionFailedPackage
{ {
public ImapConnectionFailedPackage(Exception error, string protocolLog, AutoDiscoverySettings settings) public ImapConnectionFailedPackage(string errorMessage, string protocolLog, AutoDiscoverySettings settings)
{ {
Error = error; ErrorMessage = errorMessage;
ProtocolLog = protocolLog; ProtocolLog = protocolLog;
Settings = settings; Settings = settings;
} }
public AutoDiscoverySettings Settings { get; } public AutoDiscoverySettings Settings { get; }
public Exception Error { get; } public string ErrorMessage { get; set; }
public string ProtocolLog { get; } public string ProtocolLog { get; }
public string GetErrorMessage() => Error.InnerException == null ? Error.Message : Error.InnerException.Message;
} }
} }

View File

@@ -0,0 +1,17 @@
namespace Wino.Core.Domain.Exceptions
{
public class ImapTestSSLCertificateException : System.Exception
{
public ImapTestSSLCertificateException(string issuer, string expirationDateString, string validFromDateString)
{
Issuer = issuer;
ExpirationDateString = expirationDateString;
ValidFromDateString = validFromDateString;
}
public string Issuer { get; set; }
public string ExpirationDateString { get; set; }
public string ValidFromDateString { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace Wino.Core.Domain.Extensions
{
public static class ExceptionExtensions
{
public static IEnumerable<Exception> GetInnerExceptions(this Exception ex)
{
if (ex == null)
{
throw new ArgumentNullException("ex");
}
var innerException = ex;
do
{
yield return innerException;
innerException = innerException.InnerException;
}
while (innerException != null);
}
}
}

View File

@@ -5,6 +5,6 @@ namespace Wino.Core.Domain.Interfaces
{ {
public interface IImapTestService public interface IImapTestService
{ {
Task TestImapConnectionAsync(CustomServerInformation serverInformation); Task TestImapConnectionAsync(CustomServerInformation serverInformation, bool allowSSLHandShake);
} }
} }

View File

@@ -0,0 +1,48 @@
using System;
using System.Linq;
using System.Text.Json.Serialization;
using Wino.Core.Domain.Extensions;
namespace Wino.Core.Domain.Models.Connectivity
{
/// <summary>
/// Contains validation of the IMAP server connectivity during account setup.
/// </summary>
public class ImapConnectivityTestResults
{
[JsonConstructor]
protected ImapConnectivityTestResults() { }
public bool IsSuccess { get; set; }
public bool IsCertificateUIRequired { get; set; }
public string FailedReason { get; set; }
public string FailureProtocolLog { get; set; }
public static ImapConnectivityTestResults Success() => new ImapConnectivityTestResults() { IsSuccess = true };
public static ImapConnectivityTestResults Failure(Exception ex, string failureProtocolLog) => new ImapConnectivityTestResults()
{
FailedReason = string.Join(Environment.NewLine, ex.GetInnerExceptions().Select(e => e.Message)),
FailureProtocolLog = failureProtocolLog
};
public static ImapConnectivityTestResults CertificateUIRequired(string issuer,
string expirationString,
string validFromString)
{
return new ImapConnectivityTestResults()
{
IsSuccess = false,
IsCertificateUIRequired = true,
CertificateIssuer = issuer,
CertificateExpirationDateString = expirationString,
CertificateValidFromDateString = validFromString
};
}
public string CertificateIssuer { get; set; }
public string CertificateValidFromDateString { get; set; }
public string CertificateExpirationDateString { get; set; }
}
}

View File

@@ -34,6 +34,8 @@
"BasicIMAPSetupDialog_Title": "IMAP Account", "BasicIMAPSetupDialog_Title": "IMAP Account",
"Buttons_AddAccount": "Add Account", "Buttons_AddAccount": "Add Account",
"Buttons_AddNewAlias": "Add New Alias", "Buttons_AddNewAlias": "Add New Alias",
"Buttons_Allow": "Allow",
"Buttons_Deny": "Deny",
"Buttons_SyncAliases": "Synchronize Aliases", "Buttons_SyncAliases": "Synchronize Aliases",
"Buttons_ApplyTheme": "Apply Theme", "Buttons_ApplyTheme": "Apply Theme",
"Buttons_Browse": "Browse", "Buttons_Browse": "Browse",
@@ -221,6 +223,14 @@
"IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe",
"IMAPSetupDialog_ConnectionFailedTitle": "Connection Failed", "IMAPSetupDialog_ConnectionFailedTitle": "Connection Failed",
"IMAPSetupDialog_ConnectionFailedMessage": "IMAP connection failed.", "IMAPSetupDialog_ConnectionFailedMessage": "IMAP connection failed.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row0": "This server is requesting a SSL handshake to continue. Please confirm the certificate details below.",
"IMAPSetupDialog_CertificateAllowanceRequired_Row1": "Allow the handshake to continue setting up your account.",
"IMAPSetupDialog_CertificateIssuer": "Issuer",
"IMAPSetupDialog_CertificateSubject": "Subject",
"IMAPSetupDialog_CertificateValidFrom": "Valid from",
"IMAPSetupDialog_CertificateValidTo": "Valid to",
"IMAPSetupDialog_CertificateDenied": "User didn't authorize the handshake with the certificate.",
"IMAPSetupDialog_CertificateView": "View Certificate",
"ImageRenderingDisabled": "Image rendering is disabled for this message.", "ImageRenderingDisabled": "Image rendering is disabled for this message.",
"InfoBarAction_Enable": "Enable", "InfoBarAction_Enable": "Enable",
"InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.",

View File

@@ -193,6 +193,16 @@ namespace Wino.Core.Domain
/// </summary> /// </summary>
public static string Buttons_AddNewAlias => Resources.GetTranslatedString(@"Buttons_AddNewAlias"); public static string Buttons_AddNewAlias => Resources.GetTranslatedString(@"Buttons_AddNewAlias");
/// <summary>
/// Allow
/// </summary>
public static string Buttons_Allow => Resources.GetTranslatedString(@"Buttons_Allow");
/// <summary>
/// Deny
/// </summary>
public static string Buttons_Deny => Resources.GetTranslatedString(@"Buttons_Deny");
/// <summary> /// <summary>
/// Synchronize Aliases /// Synchronize Aliases
/// </summary> /// </summary>
@@ -1128,6 +1138,46 @@ namespace Wino.Core.Domain
/// </summary> /// </summary>
public static string IMAPSetupDialog_ConnectionFailedMessage => Resources.GetTranslatedString(@"IMAPSetupDialog_ConnectionFailedMessage"); public static string IMAPSetupDialog_ConnectionFailedMessage => Resources.GetTranslatedString(@"IMAPSetupDialog_ConnectionFailedMessage");
/// <summary>
/// This server is requesting a SSL handshake to continue. Please confirm the certificate details below.
/// </summary>
public static string IMAPSetupDialog_CertificateAllowanceRequired_Row0 => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateAllowanceRequired_Row0");
/// <summary>
/// Allow the handshake to continue setting up your account.
/// </summary>
public static string IMAPSetupDialog_CertificateAllowanceRequired_Row1 => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateAllowanceRequired_Row1");
/// <summary>
/// Issuer
/// </summary>
public static string IMAPSetupDialog_CertificateIssuer => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateIssuer");
/// <summary>
/// Subject
/// </summary>
public static string IMAPSetupDialog_CertificateSubject => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateSubject");
/// <summary>
/// Valid from
/// </summary>
public static string IMAPSetupDialog_CertificateValidFrom => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateValidFrom");
/// <summary>
/// Valid to
/// </summary>
public static string IMAPSetupDialog_CertificateValidTo => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateValidTo");
/// <summary>
/// User didn't authorize the handshake with the certificate.
/// </summary>
public static string IMAPSetupDialog_CertificateDenied => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateDenied");
/// <summary>
/// View Certificate
/// </summary>
public static string IMAPSetupDialog_CertificateView => Resources.GetTranslatedString(@"IMAPSetupDialog_CertificateView");
/// <summary> /// <summary>
/// Image rendering is disabled for this message. /// Image rendering is disabled for this message.
/// </summary> /// </summary>

View File

@@ -18,7 +18,6 @@ using Wino.Core.Integration.Json;
using Wino.Messaging; using Wino.Messaging;
using Wino.Messaging.Client.Connection; using Wino.Messaging.Client.Connection;
using Wino.Messaging.Enums; using Wino.Messaging.Enums;
using Wino.Messaging.Server;
using Wino.Messaging.UI; using Wino.Messaging.UI;
namespace Wino.Core.UWP.Services namespace Wino.Core.UWP.Services

View File

@@ -12,7 +12,7 @@ using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Authentication; using Wino.Core.Domain.Models.Authentication;
using Wino.Core.Domain.Models.Authorization; using Wino.Core.Domain.Models.Authorization;
using Wino.Core.Services; using Wino.Core.Services;
using Wino.Messaging.Server; using Wino.Messaging.UI;
namespace Wino.Core.Authenticators namespace Wino.Core.Authenticators
{ {

View File

@@ -2,6 +2,8 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -9,6 +11,7 @@ using MailKit;
using MailKit.Net.Imap; using MailKit.Net.Imap;
using MailKit.Net.Proxy; using MailKit.Net.Proxy;
using MailKit.Security; using MailKit.Security;
using MimeKit.Cryptography;
using MoreLinq; using MoreLinq;
using Serilog; using Serilog;
using Wino.Core.Domain.Entities; using Wino.Core.Domain.Entities;
@@ -42,6 +45,8 @@ namespace Wino.Core.Integration
Name = "Wino Mail User", Name = "Wino Mail User",
}; };
public bool ThrowOnSSLHandshakeCallback { get; set; }
private readonly int MinimumPoolSize = 5; private readonly int MinimumPoolSize = 5;
private readonly ConcurrentStack<ImapClient> _clients = []; private readonly ConcurrentStack<ImapClient> _clients = [];
@@ -57,6 +62,8 @@ namespace Wino.Core.Integration
// Set the maximum pool size to 5 or the custom value if it's greater. // Set the maximum pool size to 5 or the custom value if it's greater.
_semaphore = new(Math.Max(MinimumPoolSize, customServerInformation.MaxConcurrentClients)); _semaphore = new(Math.Max(MinimumPoolSize, customServerInformation.MaxConcurrentClients));
CryptographyContext.Register(typeof(WindowsSecureMimeContext));
} }
private async Task EnsureConnectivityAsync(ImapClient client, bool isCreatedNew) private async Task EnsureConnectivityAsync(ImapClient client, bool isCreatedNew)
@@ -107,6 +114,9 @@ namespace Wino.Core.Integration
} }
catch (Exception ex) catch (Exception ex)
{ {
if (ex.InnerException is ImapTestSSLCertificateException imapTestSSLCertificateException)
throw imapTestSSLCertificateException;
throw new ImapClientPoolException(ex, GetProtocolLogContent()); throw new ImapClientPoolException(ex, GetProtocolLogContent());
} }
finally finally
@@ -215,11 +225,27 @@ namespace Wino.Core.Integration
{ {
if (client.IsConnected) return; if (client.IsConnected) return;
client.ServerCertificateValidationCallback = MyServerCertificateValidationCallback;
await client.ConnectAsync(_customServerInformation.IncomingServer, await client.ConnectAsync(_customServerInformation.IncomingServer,
int.Parse(_customServerInformation.IncomingServerPort), int.Parse(_customServerInformation.IncomingServerPort),
GetSocketOptions(_customServerInformation.IncomingServerSocketOption)); GetSocketOptions(_customServerInformation.IncomingServerSocketOption));
} }
bool MyServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// If there are no errors, then everything went smoothly.
if (sslPolicyErrors == SslPolicyErrors.None) return true;
// Imap connectivity test will throw to alert the user here.
if (ThrowOnSSLHandshakeCallback)
{
throw new ImapTestSSLCertificateException(certificate.Issuer, certificate.GetExpirationDateString(), certificate.GetEffectiveDateString());
}
return true;
}
public async Task EnsureAuthenticatedAsync(ImapClient client) public async Task EnsureAuthenticatedAsync(ImapClient client)
{ {
if (client.IsAuthenticated) return; if (client.IsAuthenticated) return;

View File

@@ -34,11 +34,14 @@ namespace Wino.Core.Services
_protocolLogStream = File.Create(logFile); _protocolLogStream = File.Create(logFile);
} }
public async Task TestImapConnectionAsync(CustomServerInformation serverInformation) public async Task TestImapConnectionAsync(CustomServerInformation serverInformation, bool allowSSLHandShake)
{ {
EnsureProtocolLogFileExists(); EnsureProtocolLogFileExists();
var clientPool = new ImapClientPool(serverInformation, _protocolLogStream); var clientPool = new ImapClientPool(serverInformation, _protocolLogStream)
{
ThrowOnSSLHandshakeCallback = !allowSSLHandShake
};
using (clientPool) using (clientPool)
{ {

View File

@@ -3,7 +3,7 @@ using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Messaging.Server; using Wino.Messaging.UI;
namespace Wino.Dialogs namespace Wino.Dialogs
{ {

View File

@@ -2,13 +2,13 @@
x:Class="Wino.Views.ImapSetup.AdvancedImapSetupPage" x:Class="Wino.Views.ImapSetup.AdvancedImapSetupPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:domain="using:Wino.Core.Domain"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:domain="using:Wino.Core.Domain"
d:RequestedTheme="Dark"
xmlns:helpers="using:Wino.Helpers" xmlns:helpers="using:Wino.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
d:Background="Black" d:Background="Black"
d:RequestedTheme="Dark"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid RowSpacing="4"> <Grid RowSpacing="4">
@@ -18,31 +18,31 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ScrollViewer x:Name="MainScrollviewer" Padding="{StaticResource ImapSetupDialogSubPagePadding}"> <ScrollViewer x:Name="MainScrollviewer" Padding="{StaticResource ImapSetupDialogSubPagePadding}">
<StackPanel Spacing="12" Padding="0,0,16,0"> <StackPanel Padding="0,0,16,0" Spacing="12">
<TextBlock <TextBlock
d:Text="Advanced IMAP / SMTP Configuration"
Text="{x:Bind domain:Translator.IMAPSetupDialog_Title}"
Margin="1,0,0,0" Margin="1,0,0,0"
Style="{StaticResource TitleTextBlockStyle}" /> d:Text="Advanced IMAP / SMTP Configuration"
Style="{StaticResource TitleTextBlockStyle}"
Text="{x:Bind domain:Translator.IMAPSetupDialog_Title}" />
<TextBox <TextBox
x:Name="AddressBox"
d:Header="Mail" d:Header="Mail"
Header="{x:Bind domain:Translator.IMAPSetupDialog_MailAddress}" Header="{x:Bind domain:Translator.IMAPSetupDialog_MailAddress}"
PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_MailAddressPlaceholder}" PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_MailAddressPlaceholder}" />
x:Name="AddressBox" />
<TextBox <TextBox
x:Name="DisplayNameBox"
d:Header="Display Name" d:Header="Display Name"
Header="{x:Bind domain:Translator.IMAPSetupDialog_DisplayName}" Header="{x:Bind domain:Translator.IMAPSetupDialog_DisplayName}"
PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_DisplayNamePlaceholder}" PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_DisplayNamePlaceholder}" />
x:Name="DisplayNameBox" />
<CheckBox Content="{x:Bind domain:Translator.IMAPSetupDialog_UseSameConfig}" IsChecked="{x:Bind UseSameCredentialsForSending, Mode=TwoWay}" /> <CheckBox Content="{x:Bind domain:Translator.IMAPSetupDialog_UseSameConfig}" IsChecked="{x:Bind UseSameCredentialsForSending, Mode=TwoWay}" />
<muxc:TabView <muxc:TabView
d:SelectedIndex="0" d:SelectedIndex="0"
IsAddTabButtonVisible="False"
CanReorderTabs="False" CanReorderTabs="False"
IsAddTabButtonVisible="False"
TabWidthMode="Equal"> TabWidthMode="Equal">
<muxc:TabViewItem Header="IMAP Settings" IsClosable="False"> <muxc:TabViewItem Header="IMAP Settings" IsClosable="False">
<!-- IMAP --> <!-- IMAP -->
@@ -55,34 +55,34 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox <TextBox
x:Name="IncomingServerBox"
d:Header="Incoming Server" d:Header="Incoming Server"
TextChanged="IncomingServerChanged"
PlaceholderText="eg. imap.gmail.com"
Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServer}" Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServer}"
x:Name="IncomingServerBox" /> PlaceholderText="eg. imap.gmail.com"
TextChanged="IncomingServerChanged" />
<TextBox <TextBox
d:Header="Port"
Text="993"
Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServerPort}"
x:Name="IncomingServerPortBox" x:Name="IncomingServerPortBox"
Grid.Column="1" /> Grid.Column="1"
d:Header="Port"
Header="{x:Bind domain:Translator.IMAPSetupDialog_IncomingMailServerPort}"
/>
</Grid> </Grid>
<!-- Username + Password --> <!-- Username + Password -->
<StackPanel Spacing="6"> <StackPanel Spacing="6">
<TextBox <TextBox
x:Name="UsernameBox"
d:Header="Username" d:Header="Username"
TextChanged="IncomingUsernameChanged"
Header="{x:Bind domain:Translator.IMAPSetupDialog_Username}" Header="{x:Bind domain:Translator.IMAPSetupDialog_Username}"
PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_UsernamePlaceholder}" PlaceholderText="{x:Bind domain:Translator.IMAPSetupDialog_UsernamePlaceholder}"
x:Name="UsernameBox" /> TextChanged="IncomingUsernameChanged" />
<PasswordBox <PasswordBox
PasswordChanged="IncomingPasswordChanged" x:Name="PasswordBox"
d:Header="Password" d:Header="Password"
Header="{x:Bind domain:Translator.IMAPSetupDialog_Password}" Header="{x:Bind domain:Translator.IMAPSetupDialog_Password}"
x:Name="PasswordBox" /> PasswordChanged="IncomingPasswordChanged" />
</StackPanel> </StackPanel>
<!-- Security and Authentication --> <!-- Security and Authentication -->
@@ -94,35 +94,35 @@
<!-- Security --> <!-- Security -->
<StackPanel Spacing="6"> <StackPanel Spacing="6">
<TextBlock <TextBlock
HorizontalAlignment="Center"
d:Text="Connection security" d:Text="Connection security"
Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}" Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}" />
HorizontalAlignment="Center" />
<ComboBox <ComboBox
x:Name="IncomingConnectionSecurity" x:Name="IncomingConnectionSecurity"
SelectedIndex="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableConnectionSecurities}" ItemsSource="{x:Bind AvailableConnectionSecurities}"
DisplayMemberPath="DisplayName" /> SelectedIndex="0" />
</StackPanel> </StackPanel>
<!-- Authentication --> <!-- Authentication -->
<StackPanel Grid.Column="1" Spacing="6"> <StackPanel Grid.Column="1" Spacing="6">
<TextBlock <TextBlock
HorizontalAlignment="Center"
d:Text="Authentication method" d:Text="Authentication method"
Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}" Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}" />
HorizontalAlignment="Center" />
<ComboBox <ComboBox
x:Name="IncomingAuthenticationMethod" x:Name="IncomingAuthenticationMethod"
SelectedIndex="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableAuthenticationMethods}" ItemsSource="{x:Bind AvailableAuthenticationMethods}"
DisplayMemberPath="DisplayName" /> SelectedIndex="0" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</StackPanel> </StackPanel>
</muxc:TabViewItem> </muxc:TabViewItem>
<muxc:TabViewItem IsClosable="False" Header="SMTP Settings"> <muxc:TabViewItem Header="SMTP Settings" IsClosable="False">
<!-- SMTP --> <!-- SMTP -->
<StackPanel Padding="12" Spacing="10"> <StackPanel Padding="12" Spacing="10">
<!-- Server + Port --> <!-- Server + Port -->
@@ -133,18 +133,18 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox <TextBox
x:Name="OutgoingServerBox"
d:Header="Outgoing Server" d:Header="Outgoing Server"
TextChanged="OutgoingServerChanged"
PlaceholderText="eg. smtp.gmail.com"
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServer}" Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServer}"
x:Name="OutgoingServerBox" /> PlaceholderText="eg. smtp.gmail.com"
TextChanged="OutgoingServerChanged" />
<TextBox <TextBox
x:Name="OutgoingServerPort"
Grid.Column="1"
d:Header="Port" d:Header="Port"
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPort}" Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPort}"
x:Name="OutgoingServerPort" />
Text="587"
Grid.Column="1" />
</Grid> </Grid>
<!-- Username + Password --> <!-- Username + Password -->
@@ -152,13 +152,13 @@
<TextBox <TextBox
x:Name="OutgoingUsernameBox" x:Name="OutgoingUsernameBox"
d:Header="UserName" d:Header="UserName"
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(UseSameCredentialsForSending), Mode=OneWay}" Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerUsername}"
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerUsername}" /> IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(UseSameCredentialsForSending), Mode=OneWay}" />
<PasswordBox <PasswordBox
x:Name="OutgoingPasswordBox" x:Name="OutgoingPasswordBox"
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(UseSameCredentialsForSending), Mode=OneWay}"
d:Header="Password" d:Header="Password"
Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPassword}" /> Header="{x:Bind domain:Translator.IMAPSetupDialog_OutgoingMailServerPassword}"
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(UseSameCredentialsForSending), Mode=OneWay}" />
</StackPanel> </StackPanel>
<!-- Security and Authentication --> <!-- Security and Authentication -->
@@ -169,42 +169,42 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Security --> <!-- Security -->
<StackPanel Spacing="6"> <StackPanel Spacing="6">
<TextBlock Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}" HorizontalAlignment="Center" /> <TextBlock HorizontalAlignment="Center" Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_ConnectionSecurity}" />
<ComboBox <ComboBox
x:Name="OutgoingConnectionSecurity" x:Name="OutgoingConnectionSecurity"
SelectedIndex="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableConnectionSecurities}" ItemsSource="{x:Bind AvailableConnectionSecurities}"
DisplayMemberPath="DisplayName" /> SelectedIndex="0" />
</StackPanel> </StackPanel>
<!-- Authentication --> <!-- Authentication -->
<StackPanel Grid.Column="1" Spacing="6"> <StackPanel Grid.Column="1" Spacing="6">
<TextBlock Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}" HorizontalAlignment="Center" /> <TextBlock HorizontalAlignment="Center" Text="{x:Bind domain:Translator.ImapAdvancedSetupDialog_AuthenticationMethod}" />
<ComboBox <ComboBox
x:Name="OutgoingAuthenticationMethod" x:Name="OutgoingAuthenticationMethod"
SelectedIndex="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberPath="DisplayName"
ItemsSource="{x:Bind AvailableAuthenticationMethods}" ItemsSource="{x:Bind AvailableAuthenticationMethods}"
DisplayMemberPath="DisplayName" /> SelectedIndex="0" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</StackPanel> </StackPanel>
</muxc:TabViewItem> </muxc:TabViewItem>
<muxc:TabViewItem IsClosable="False" Header="Proxy"> <muxc:TabViewItem Header="Proxy" IsClosable="False">
<!-- Proxy --> <!-- Proxy -->
<StackPanel Spacing="10" Padding="12"> <StackPanel Padding="12" Spacing="10">
<TextBlock Text="Define your optional proxy server for the connection if your mail server requires it. This is optional." /> <TextBlock Text="Define your optional proxy server for the connection if your mail server requires it. This is optional." />
<Grid ColumnSpacing="12"> <Grid ColumnSpacing="12">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox Header="Proxy server" x:Name="ProxyServerBox" /> <TextBox x:Name="ProxyServerBox" Header="Proxy server" />
<muxc:NumberBox <muxc:NumberBox
Header="Port" x:Name="ProxyServerPortBox"
Grid.Column="1" Grid.Column="1"
x:Name="ProxyServerPortBox" /> Header="Port" />
</Grid> </Grid>
</StackPanel> </StackPanel>
</muxc:TabViewItem> </muxc:TabViewItem>
@@ -214,10 +214,10 @@
<!-- Buttons --> <!-- Buttons -->
<Grid <Grid
Padding="{StaticResource ImapSetupDialogSubPagePadding}"
Background="{ThemeResource ContentDialogBackground}"
Grid.Row="1" Grid.Row="1"
Padding="{StaticResource ImapSetupDialogSubPagePadding}"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Background="{ThemeResource ContentDialogBackground}"
ColumnSpacing="6"> ColumnSpacing="6">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@@ -225,18 +225,18 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button <Button
d:Content="Cancel"
Content="{x:Bind domain:Translator.Buttons_Cancel}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Click="CancelClicked" /> d:Content="Cancel"
Click="CancelClicked"
Content="{x:Bind domain:Translator.Buttons_Cancel}" />
<Button <Button
d:Content="Sign In"
Content="{x:Bind domain:Translator.Buttons_SignIn}"
Click="SignInClicked"
Style="{ThemeResource AccentButtonStyle}"
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" /> HorizontalAlignment="Stretch"
d:Content="Sign In"
Click="SignInClicked"
Content="{x:Bind domain:Translator.Buttons_SignIn}"
Style="{ThemeResource AccentButtonStyle}" />
</Grid> </Grid>
</Grid> </Grid>
</Page> </Page>

View File

@@ -75,7 +75,10 @@ namespace Wino.Views.ImapSetup
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
// ProtocolLogGrid.Visibility = Visibility.Collapsed; // Don't override settings on back scenarios.
// User is trying to try again the same configuration.
if (e.NavigationMode == NavigationMode.Back) return;
// Connection is succesfull but error occurred. // Connection is succesfull but error occurred.
// Imap and Smptp settings exists here at this point. // Imap and Smptp settings exists here at this point.

View File

@@ -35,7 +35,7 @@ namespace Wino.Views.ImapSetup
if (e.Parameter is ImapConnectionFailedPackage failedPackage) if (e.Parameter is ImapConnectionFailedPackage failedPackage)
{ {
ConnectionFailedMessage.Text = failedPackage.GetErrorMessage(); ConnectionFailedMessage.Text = failedPackage.ErrorMessage;
ProtocolLogGrid.Visibility = !string.IsNullOrEmpty(failedPackage.ProtocolLog) ? Visibility.Visible : Visibility.Collapsed; ProtocolLogGrid.Visibility = !string.IsNullOrEmpty(failedPackage.ProtocolLog) ? Visibility.Visible : Visibility.Collapsed;
_protocolLog = failedPackage.ProtocolLog; _protocolLog = failedPackage.ProtocolLog;

View File

@@ -2,31 +2,102 @@
x:Class="Wino.Views.ImapSetup.TestingImapConnectionPage" x:Class="Wino.Views.ImapSetup.TestingImapConnectionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Wino.Views.ImapSetup" xmlns:controls="using:Wino.Controls"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:domain="using:Wino.Core.Domain" xmlns:domain="using:Wino.Core.Domain"
xmlns:local="using:Wino.Views.ImapSetup"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid> <Grid>
<!-- Testing Connection Panel --> <!-- Testing Connection Panel -->
<StackPanel <StackPanel
x:Name="TestingConnectionPanel" x:Name="TestingConnectionPanel"
VerticalAlignment="Center" HorizontalAlignment="Center"
HorizontalAlignment="Center"> VerticalAlignment="Center">
<Viewbox <Viewbox
VerticalAlignment="Center"
HorizontalAlignment="Center"
Width="26" Width="26"
Height="26"> Height="26"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<PathIcon Data="F1 M 18.75 18.125 C 18.75 18.294271 18.68815 18.440756 18.564453 18.564453 C 18.440754 18.68815 18.29427 18.75 18.125 18.75 L 13.75 18.75 C 13.75 18.925781 13.717447 19.088541 13.652344 19.238281 C 13.587239 19.388021 13.497721 19.519857 13.383789 19.633789 C 13.269855 19.747721 13.13802 19.83724 12.988281 19.902344 C 12.838541 19.967447 12.675781 20 12.5 20 L 6.25 20 C 6.074219 20 5.909831 19.967447 5.756836 19.902344 C 5.603841 19.83724 5.472005 19.74935 5.361328 19.638672 C 5.250651 19.527994 5.16276 19.396158 5.097656 19.243164 C 5.032552 19.09017 5 18.925781 5 18.75 L 0.625 18.75 C 0.455729 18.75 0.309245 18.68815 0.185547 18.564453 C 0.061849 18.440756 0 18.294271 0 18.125 C 0 17.955729 0.061849 17.809244 0.185547 17.685547 C 0.309245 17.56185 0.455729 17.5 0.625 17.5 L 5 17.5 C 5 17.154949 5.120442 16.86198 5.361328 16.621094 C 5.608724 16.373699 5.904948 16.25 6.25 16.25 L 7.5 16.25 C 7.5 16.074219 7.532552 15.911459 7.597656 15.761719 C 7.66276 15.611979 7.752278 15.480144 7.866211 15.366211 C 7.980143 15.252279 8.111979 15.162761 8.261719 15.097656 C 8.411458 15.032553 8.574219 15 8.75 15 L 8.75 13.75 L 6.875 13.75 C 6.621094 13.75 6.380208 13.701172 6.152344 13.603516 C 5.924479 13.505859 5.724284 13.370769 5.551758 13.198242 C 5.379231 13.025717 5.244141 12.825521 5.146484 12.597656 C 5.048828 12.369792 5 12.128906 5 11.875 L 5 1.875 C 5 1.621094 5.048828 1.380209 5.146484 1.152344 C 5.244141 0.92448 5.379231 0.724285 5.551758 0.551758 C 5.724284 0.379232 5.924479 0.244141 6.152344 0.146484 C 6.380208 0.048828 6.621094 0 6.875 0 L 11.875 0 C 12.128906 0 12.369791 0.048828 12.597656 0.146484 C 12.825521 0.244141 13.025716 0.379232 13.198242 0.551758 C 13.370768 0.724285 13.505859 0.92448 13.603516 1.152344 C 13.701172 1.380209 13.75 1.621094 13.75 1.875 L 13.75 11.875 C 13.75 12.128906 13.701172 12.369792 13.603516 12.597656 C 13.505859 12.825521 13.370768 13.025717 13.198242 13.198242 C 13.025716 13.370769 12.825521 13.505859 12.597656 13.603516 C 12.369791 13.701172 12.128906 13.75 11.875 13.75 L 10 13.75 L 10 15 C 10.169271 15 10.330403 15.032553 10.483398 15.097656 C 10.636393 15.162761 10.769856 15.252279 10.883789 15.366211 C 10.997721 15.480144 11.087239 15.613607 11.152344 15.766602 C 11.217447 15.919597 11.25 16.080729 11.25 16.25 L 12.5 16.25 C 12.669271 16.25 12.828775 16.282553 12.978516 16.347656 C 13.128255 16.41276 13.261719 16.503906 13.378906 16.621094 C 13.626302 16.86849 13.75 17.161459 13.75 17.5 L 18.125 17.5 C 18.29427 17.5 18.440754 17.56185 18.564453 17.685547 C 18.68815 17.809244 18.75 17.955729 18.75 18.125 Z M 11.875 12.5 C 12.04427 12.5 12.190754 12.438151 12.314453 12.314453 C 12.43815 12.190756 12.5 12.044271 12.5 11.875 L 12.5 1.875 C 12.5 1.70573 12.43815 1.559246 12.314453 1.435547 C 12.190754 1.31185 12.04427 1.25 11.875 1.25 L 6.875 1.25 C 6.705729 1.25 6.559244 1.31185 6.435547 1.435547 C 6.311849 1.559246 6.25 1.70573 6.25 1.875 L 6.25 11.875 C 6.25 12.044271 6.311849 12.190756 6.435547 12.314453 C 6.559244 12.438151 6.705729 12.5 6.875 12.5 Z M 10.625 2.5 C 10.794271 2.5 10.940755 2.56185 11.064453 2.685547 C 11.18815 2.809246 11.25 2.95573 11.25 3.125 C 11.25 3.294271 11.18815 3.440756 11.064453 3.564453 C 10.940755 3.688152 10.794271 3.75 10.625 3.75 L 8.125 3.75 C 7.955729 3.75 7.809245 3.688152 7.685547 3.564453 C 7.561849 3.440756 7.5 3.294271 7.5 3.125 C 7.5 2.95573 7.561849 2.809246 7.685547 2.685547 C 7.809245 2.56185 7.955729 2.5 8.125 2.5 Z M 10.625 5 C 10.794271 5.000001 10.940755 5.06185 11.064453 5.185547 C 11.18815 5.309245 11.25 5.455729 11.25 5.625 C 11.25 5.794271 11.18815 5.940756 11.064453 6.064453 C 10.940755 6.188151 10.794271 6.25 10.625 6.25 L 8.125 6.25 C 7.955729 6.25 7.809245 6.188151 7.685547 6.064453 C 7.561849 5.940756 7.5 5.794271 7.5 5.625 C 7.5 5.455729 7.561849 5.309245 7.685547 5.185547 C 7.809245 5.06185 7.955729 5.000001 8.125 5 Z M 12.5 18.75 L 12.5 17.5 L 10.625 17.5 C 10.481771 17.5 10.367838 17.47233 10.283203 17.416992 C 10.198567 17.361654 10.135091 17.290039 10.092773 17.202148 C 10.050455 17.114258 10.022786 17.016602 10.009766 16.90918 C 9.996744 16.801758 9.990234 16.692709 9.990234 16.582031 C 9.990234 16.523438 9.991861 16.466471 9.995117 16.411133 C 9.998372 16.355795 10 16.302084 10 16.25 L 8.75 16.25 L 8.75 16.582031 C 8.75 16.692709 8.743489 16.801758 8.730469 16.90918 C 8.717447 17.016602 8.689778 17.114258 8.647461 17.202148 C 8.605143 17.290039 8.543294 17.361654 8.461914 17.416992 C 8.380533 17.47233 8.268229 17.5 8.125 17.5 L 6.25 17.5 L 6.25 18.75 Z " /> <PathIcon Data="F1 M 18.75 18.125 C 18.75 18.294271 18.68815 18.440756 18.564453 18.564453 C 18.440754 18.68815 18.29427 18.75 18.125 18.75 L 13.75 18.75 C 13.75 18.925781 13.717447 19.088541 13.652344 19.238281 C 13.587239 19.388021 13.497721 19.519857 13.383789 19.633789 C 13.269855 19.747721 13.13802 19.83724 12.988281 19.902344 C 12.838541 19.967447 12.675781 20 12.5 20 L 6.25 20 C 6.074219 20 5.909831 19.967447 5.756836 19.902344 C 5.603841 19.83724 5.472005 19.74935 5.361328 19.638672 C 5.250651 19.527994 5.16276 19.396158 5.097656 19.243164 C 5.032552 19.09017 5 18.925781 5 18.75 L 0.625 18.75 C 0.455729 18.75 0.309245 18.68815 0.185547 18.564453 C 0.061849 18.440756 0 18.294271 0 18.125 C 0 17.955729 0.061849 17.809244 0.185547 17.685547 C 0.309245 17.56185 0.455729 17.5 0.625 17.5 L 5 17.5 C 5 17.154949 5.120442 16.86198 5.361328 16.621094 C 5.608724 16.373699 5.904948 16.25 6.25 16.25 L 7.5 16.25 C 7.5 16.074219 7.532552 15.911459 7.597656 15.761719 C 7.66276 15.611979 7.752278 15.480144 7.866211 15.366211 C 7.980143 15.252279 8.111979 15.162761 8.261719 15.097656 C 8.411458 15.032553 8.574219 15 8.75 15 L 8.75 13.75 L 6.875 13.75 C 6.621094 13.75 6.380208 13.701172 6.152344 13.603516 C 5.924479 13.505859 5.724284 13.370769 5.551758 13.198242 C 5.379231 13.025717 5.244141 12.825521 5.146484 12.597656 C 5.048828 12.369792 5 12.128906 5 11.875 L 5 1.875 C 5 1.621094 5.048828 1.380209 5.146484 1.152344 C 5.244141 0.92448 5.379231 0.724285 5.551758 0.551758 C 5.724284 0.379232 5.924479 0.244141 6.152344 0.146484 C 6.380208 0.048828 6.621094 0 6.875 0 L 11.875 0 C 12.128906 0 12.369791 0.048828 12.597656 0.146484 C 12.825521 0.244141 13.025716 0.379232 13.198242 0.551758 C 13.370768 0.724285 13.505859 0.92448 13.603516 1.152344 C 13.701172 1.380209 13.75 1.621094 13.75 1.875 L 13.75 11.875 C 13.75 12.128906 13.701172 12.369792 13.603516 12.597656 C 13.505859 12.825521 13.370768 13.025717 13.198242 13.198242 C 13.025716 13.370769 12.825521 13.505859 12.597656 13.603516 C 12.369791 13.701172 12.128906 13.75 11.875 13.75 L 10 13.75 L 10 15 C 10.169271 15 10.330403 15.032553 10.483398 15.097656 C 10.636393 15.162761 10.769856 15.252279 10.883789 15.366211 C 10.997721 15.480144 11.087239 15.613607 11.152344 15.766602 C 11.217447 15.919597 11.25 16.080729 11.25 16.25 L 12.5 16.25 C 12.669271 16.25 12.828775 16.282553 12.978516 16.347656 C 13.128255 16.41276 13.261719 16.503906 13.378906 16.621094 C 13.626302 16.86849 13.75 17.161459 13.75 17.5 L 18.125 17.5 C 18.29427 17.5 18.440754 17.56185 18.564453 17.685547 C 18.68815 17.809244 18.75 17.955729 18.75 18.125 Z M 11.875 12.5 C 12.04427 12.5 12.190754 12.438151 12.314453 12.314453 C 12.43815 12.190756 12.5 12.044271 12.5 11.875 L 12.5 1.875 C 12.5 1.70573 12.43815 1.559246 12.314453 1.435547 C 12.190754 1.31185 12.04427 1.25 11.875 1.25 L 6.875 1.25 C 6.705729 1.25 6.559244 1.31185 6.435547 1.435547 C 6.311849 1.559246 6.25 1.70573 6.25 1.875 L 6.25 11.875 C 6.25 12.044271 6.311849 12.190756 6.435547 12.314453 C 6.559244 12.438151 6.705729 12.5 6.875 12.5 Z M 10.625 2.5 C 10.794271 2.5 10.940755 2.56185 11.064453 2.685547 C 11.18815 2.809246 11.25 2.95573 11.25 3.125 C 11.25 3.294271 11.18815 3.440756 11.064453 3.564453 C 10.940755 3.688152 10.794271 3.75 10.625 3.75 L 8.125 3.75 C 7.955729 3.75 7.809245 3.688152 7.685547 3.564453 C 7.561849 3.440756 7.5 3.294271 7.5 3.125 C 7.5 2.95573 7.561849 2.809246 7.685547 2.685547 C 7.809245 2.56185 7.955729 2.5 8.125 2.5 Z M 10.625 5 C 10.794271 5.000001 10.940755 5.06185 11.064453 5.185547 C 11.18815 5.309245 11.25 5.455729 11.25 5.625 C 11.25 5.794271 11.18815 5.940756 11.064453 6.064453 C 10.940755 6.188151 10.794271 6.25 10.625 6.25 L 8.125 6.25 C 7.955729 6.25 7.809245 6.188151 7.685547 6.064453 C 7.561849 5.940756 7.5 5.794271 7.5 5.625 C 7.5 5.455729 7.561849 5.309245 7.685547 5.185547 C 7.809245 5.06185 7.955729 5.000001 8.125 5 Z M 12.5 18.75 L 12.5 17.5 L 10.625 17.5 C 10.481771 17.5 10.367838 17.47233 10.283203 17.416992 C 10.198567 17.361654 10.135091 17.290039 10.092773 17.202148 C 10.050455 17.114258 10.022786 17.016602 10.009766 16.90918 C 9.996744 16.801758 9.990234 16.692709 9.990234 16.582031 C 9.990234 16.523438 9.991861 16.466471 9.995117 16.411133 C 9.998372 16.355795 10 16.302084 10 16.25 L 8.75 16.25 L 8.75 16.582031 C 8.75 16.692709 8.743489 16.801758 8.730469 16.90918 C 8.717447 17.016602 8.689778 17.114258 8.647461 17.202148 C 8.605143 17.290039 8.543294 17.361654 8.461914 17.416992 C 8.380533 17.47233 8.268229 17.5 8.125 17.5 L 6.25 17.5 L 6.25 18.75 Z " />
</Viewbox> </Viewbox>
<TextBlock Text="{x:Bind domain:Translator.TestingImapConnectionMessage}" /> <TextBlock Text="{x:Bind domain:Translator.TestingImapConnectionMessage}" />
<muxc:ProgressBar IsIndeterminate="True" Margin="0,4,0,0" /> <muxc:ProgressBar Margin="0,4,0,0" IsIndeterminate="True" />
</StackPanel> </StackPanel>
<!-- Allow untrusted certificate dialog -->
<Grid
x:Name="CertificateDialog"
MaxWidth="600"
Padding="20"
RowSpacing="12"
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<controls:WinoFontIcon FontSize="46" Icon="Certificate" />
<TextBlock
Grid.Row="1"
Margin="0,16,0,6"
TextWrapping="WrapWholeWords">
<Run Text="{x:Bind domain:Translator.IMAPSetupDialog_CertificateAllowanceRequired_Row0}" />
<LineBreak />
<Run Text="{x:Bind domain:Translator.IMAPSetupDialog_CertificateAllowanceRequired_Row1}" />
</TextBlock>
<!-- Cert details -->
<StackPanel Grid.Row="2" Spacing="6">
<TextBlock TextWrapping="Wrap">
<Run FontWeight="SemiBold" Text="{x:Bind domain:Translator.IMAPSetupDialog_CertificateIssuer}" />
<LineBreak />
<Run x:Name="CertIssuer" />
</TextBlock>
<TextBlock>
<Run FontWeight="SemiBold" Text="{x:Bind domain:Translator.IMAPSetupDialog_CertificateValidFrom}" />
<LineBreak />
<Run x:Name="CertValidFrom" />
</TextBlock>
<TextBlock>
<Run FontWeight="SemiBold" Text="{x:Bind domain:Translator.IMAPSetupDialog_CertificateValidTo}" />
<LineBreak />
<Run x:Name="CertValidTo" />
</TextBlock>
</StackPanel>
<Grid
Grid.Row="3"
Margin="0,16"
ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
x:Name="DenyCertificateButton"
HorizontalAlignment="Stretch"
Click="DenyClicked"
Content="{x:Bind domain:Translator.Buttons_Deny}" />
<Button
x:Name="AllowCertificateButton"
Grid.Column="1"
HorizontalAlignment="Stretch"
Click="AllowClicked"
Content="{x:Bind domain:Translator.Buttons_Allow}"
Style="{StaticResource AccentButtonStyle}" />
</Grid>
</Grid>
</Grid> </Grid>
</Page> </Page>

View File

@@ -4,35 +4,29 @@ using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities; using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Exceptions; using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.AutoDiscovery; using Wino.Core.Domain.Models.AutoDiscovery;
using Wino.Core.Domain.Models.Connectivity;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
namespace Wino.Views.ImapSetup namespace Wino.Views.ImapSetup
{ {
public sealed partial class TestingImapConnectionPage : Page public sealed partial class TestingImapConnectionPage : Page
{ {
private IImapTestService _imapTestService = App.Current.Services.GetService<IImapTestService>(); private IWinoServerConnectionManager _winoServerConnectionManager = App.Current.Services.GetService<IWinoServerConnectionManager>();
private AutoDiscoverySettings autoDiscoverySettings;
private CustomServerInformation serverInformationToTest;
public TestingImapConnectionPage() public TestingImapConnectionPage()
{ {
InitializeComponent(); InitializeComponent();
} }
private async Task TryTestConnectionAsync(CustomServerInformation serverInformation)
{
await Task.Delay(1000);
await _imapTestService.TestImapConnectionAsync(serverInformation);
// All success. Finish setup with validated server information.
WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(serverInformation));
}
protected override async void OnNavigatedTo(NavigationEventArgs e) protected override async void OnNavigatedTo(NavigationEventArgs e)
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
@@ -47,9 +41,6 @@ namespace Wino.Views.ImapSetup
{ {
// Test connection // Test connection
CustomServerInformation serverInformationToTest = null;
AutoDiscoverySettings autoDiscoverySettings = null;
// Discovery settings are passed. // Discovery settings are passed.
// Create server information out of the discovery settings. // Create server information out of the discovery settings.
if (e.Parameter is AutoDiscoverySettings parameterAutoDiscoverySettings) if (e.Parameter is AutoDiscoverySettings parameterAutoDiscoverySettings)
@@ -63,19 +54,80 @@ namespace Wino.Views.ImapSetup
serverInformationToTest = customServerInformation; serverInformationToTest = customServerInformation;
} }
try // Make sure that certificate dialog must be present in case of SSL handshake fails.
{ await PerformTestAsync(allowSSLHandshake: false);
await TryTestConnectionAsync(serverInformationToTest);
} }
catch (Exception ex) }
private async Task PerformTestAsync(bool allowSSLHandshake)
{ {
string protocolLog = ex is ImapClientPoolException clientPoolException ? clientPoolException.ProtocolLog : string.Empty; CertificateDialog.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
TestingConnectionPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
var failurePackage = new ImapConnectionFailedPackage(ex, protocolLog, autoDiscoverySettings); await Task.Delay(1000);
var testResultResponse = await _winoServerConnectionManager
.GetResponseAsync<ImapConnectivityTestResults, ImapConnectivityTestRequested>(new ImapConnectivityTestRequested(serverInformationToTest, allowSSLHandshake));
if (!testResultResponse.IsSuccess)
{
// Wino Server is connection is failed.
ReturnWithError(testResultResponse.Message);
}
else
{
var testResultData = testResultResponse.Data;
if (testResultData.IsSuccess)
{
// All success. Finish setup with validated server information.
ReturnWithSuccess();
}
else
{
// Check if certificate UI is required.
if (testResultData.IsCertificateUIRequired)
{
// Certificate UI is required. Show certificate dialog.
CertIssuer.Text = testResultData.CertificateIssuer;
CertValidFrom.Text = testResultData.CertificateValidFromDateString;
CertValidTo.Text = testResultData.CertificateExpirationDateString;
TestingConnectionPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
CertificateDialog.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
else
{
// Connection test failed. Show error dialog.
var protocolLog = testResultData.FailureProtocolLog;
ReturnWithError(testResultData.FailedReason, protocolLog);
}
}
}
}
private void ReturnWithError(string error, string protocolLog = "")
{
var failurePackage = new ImapConnectionFailedPackage(error, protocolLog, autoDiscoverySettings);
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage)); WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
} }
}
private void ReturnWithSuccess()
=> WeakReferenceMessenger.Default.Send(new ImapSetupDismissRequested(serverInformationToTest));
private void DenyClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
=> ReturnWithError(Translator.IMAPSetupDialog_CertificateDenied, string.Empty);
private async void AllowClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
// Run the test again, but this time allow SSL handshake.
// Any authentication error will be shown to the user after this test.
await PerformTestAsync(allowSSLHandshake: true);
} }
} }
} }

View File

@@ -59,7 +59,7 @@ namespace Wino.Views.ImapSetup
{ {
// Couldn't find settings. // Couldn't find settings.
var failurePackage = new ImapConnectionFailedPackage(new Exception(Translator.Exception_ImapAutoDiscoveryFailed), string.Empty, discoverySettings); var failurePackage = new ImapConnectionFailedPackage(Translator.Exception_ImapAutoDiscoveryFailed, string.Empty, discoverySettings);
WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage)); WeakReferenceMessenger.Default.Send(new ImapSetupBackNavigationRequested(typeof(ImapConnectionFailedPage), failurePackage));
} }

View File

@@ -291,6 +291,7 @@
Unchecked="SelectAllCheckboxUnchecked" Unchecked="SelectAllCheckboxUnchecked"
Visibility="{x:Bind helpers:XamlHelpers.IsSelectionModeMultiple(MailListView.SelectionMode), Mode=OneWay}" /> Visibility="{x:Bind helpers:XamlHelpers.IsSelectionModeMultiple(MailListView.SelectionMode), Mode=OneWay}" />
<!-- Folders --> <!-- Folders -->
<toolkit:Segmented <toolkit:Segmented
Grid.Row="1" Grid.Row="1"

View File

@@ -0,0 +1,7 @@
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Interfaces;
namespace Wino.Messaging.Server
{
public record ImapConnectivityTestRequested(CustomServerInformation ServerInformation, bool IsSSLHandshakeAllowed) : IClientMessage;
}

View File

@@ -1,6 +1,4 @@
using Wino.Messaging.UI; namespace Wino.Messaging.UI
namespace Wino.Messaging.Server
{ {
/// <summary> /// <summary>
/// When authenticators are proposed to copy the auth URL on the UI. /// When authenticators are proposed to copy the auth URL on the UI.

View File

@@ -22,6 +22,7 @@ namespace Wino.Server.Core
nameof(SynchronizationExistenceCheckRequest) => App.Current.Services.GetService<SyncExistenceHandler>(), nameof(SynchronizationExistenceCheckRequest) => App.Current.Services.GetService<SyncExistenceHandler>(),
nameof(ServerTerminationModeChanged) => App.Current.Services.GetService<ServerTerminationModeHandler>(), nameof(ServerTerminationModeChanged) => App.Current.Services.GetService<ServerTerminationModeHandler>(),
nameof(TerminateServerRequested) => App.Current.Services.GetService<TerminateServerRequestHandler>(), nameof(TerminateServerRequested) => App.Current.Services.GetService<TerminateServerRequestHandler>(),
nameof(ImapConnectivityTestRequested) => App.Current.Services.GetService<ImapConnectivityTestHandler>(),
_ => throw new Exception($"Server handler for {typeName} is not registered."), _ => throw new Exception($"Server handler for {typeName} is not registered."),
}; };
} }
@@ -38,6 +39,7 @@ namespace Wino.Server.Core
serviceCollection.AddTransient<SyncExistenceHandler>(); serviceCollection.AddTransient<SyncExistenceHandler>();
serviceCollection.AddTransient<ServerTerminationModeHandler>(); serviceCollection.AddTransient<ServerTerminationModeHandler>();
serviceCollection.AddTransient<TerminateServerRequestHandler>(); serviceCollection.AddTransient<TerminateServerRequestHandler>();
serviceCollection.AddTransient<ImapConnectivityTestHandler>();
} }
} }
} }

View File

@@ -0,0 +1,50 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Connectivity;
using Wino.Core.Domain.Models.Server;
using Wino.Messaging.Server;
using Wino.Server.Core;
namespace Wino.Server.MessageHandlers
{
public class ImapConnectivityTestHandler : ServerMessageHandler<ImapConnectivityTestRequested, ImapConnectivityTestResults>
{
private readonly IImapTestService _imapTestService;
public override WinoServerResponse<ImapConnectivityTestResults> FailureDefaultResponse(Exception ex)
=> WinoServerResponse<ImapConnectivityTestResults>.CreateErrorResponse(ex.Message);
public ImapConnectivityTestHandler(IImapTestService imapTestService)
{
_imapTestService = imapTestService;
}
protected override async Task<WinoServerResponse<ImapConnectivityTestResults>> HandleAsync(ImapConnectivityTestRequested message, CancellationToken cancellationToken = default)
{
try
{
await _imapTestService.TestImapConnectionAsync(message.ServerInformation, message.IsSSLHandshakeAllowed);
return WinoServerResponse<ImapConnectivityTestResults>.CreateSuccessResponse(ImapConnectivityTestResults.Success());
}
catch (ImapTestSSLCertificateException sslTestException)
{
// User must confirm to continue ignoring the SSL certificate.
return WinoServerResponse<ImapConnectivityTestResults>.CreateSuccessResponse(ImapConnectivityTestResults.CertificateUIRequired(sslTestException.Issuer, sslTestException.ExpirationDateString, sslTestException.ValidFromDateString));
}
catch (ImapClientPoolException clientPoolException)
{
// Connectivity failed with protocol log.
return WinoServerResponse<ImapConnectivityTestResults>.CreateSuccessResponse(ImapConnectivityTestResults.Failure(clientPoolException, clientPoolException.ProtocolLog));
}
catch (Exception exception)
{
// Unknown error
return WinoServerResponse<ImapConnectivityTestResults>.CreateSuccessResponse(ImapConnectivityTestResults.Failure(exception, string.Empty));
}
}
}
}

View File

@@ -315,7 +315,9 @@ namespace Wino.Server
case nameof(ServerTerminationModeChanged): case nameof(ServerTerminationModeChanged):
await ExecuteServerMessageSafeAsync(args, JsonSerializer.Deserialize<ServerTerminationModeChanged>(messageJson, _jsonSerializerOptions)); await ExecuteServerMessageSafeAsync(args, JsonSerializer.Deserialize<ServerTerminationModeChanged>(messageJson, _jsonSerializerOptions));
break; break;
case nameof(ImapConnectivityTestRequested):
await ExecuteServerMessageSafeAsync(args, JsonSerializer.Deserialize<ImapConnectivityTestRequested>(messageJson, _jsonSerializerOptions));
break;
case nameof(TerminateServerRequested): case nameof(TerminateServerRequested):
await ExecuteServerMessageSafeAsync(args, JsonSerializer.Deserialize<TerminateServerRequested>(messageJson, _jsonSerializerOptions)); await ExecuteServerMessageSafeAsync(args, JsonSerializer.Deserialize<TerminateServerRequested>(messageJson, _jsonSerializerOptions));