Reworked IMAP setup flow. Implemented easy way to share protocol log on failure if possible.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MailKit;
|
||||
using MailKit.Net.Imap;
|
||||
using MailKit.Net.Proxy;
|
||||
using MailKit.Security;
|
||||
@@ -20,7 +23,7 @@ namespace Wino.Core.Integration
|
||||
/// TODO: Listens to the Inbox folder for new messages.
|
||||
/// </summary>
|
||||
/// <param name="customServerInformation">Connection/Authentication info to be used to configure ImapClient.</param>
|
||||
public class ImapClientPool
|
||||
public class ImapClientPool : IDisposable
|
||||
{
|
||||
// Hardcoded implementation details for ID extension if the server supports.
|
||||
// Some providers like Chinese 126 require Id to be sent before authentication.
|
||||
@@ -40,11 +43,13 @@ namespace Wino.Core.Integration
|
||||
private readonly ConcurrentBag<ImapClient> _clients = [];
|
||||
private readonly SemaphoreSlim _semaphore = new(MaxPoolSize);
|
||||
private readonly CustomServerInformation _customServerInformation;
|
||||
private readonly Stream _protocolLogStream;
|
||||
private readonly ILogger _logger = Log.ForContext<ImapClientPool>();
|
||||
|
||||
public ImapClientPool(CustomServerInformation customServerInformation)
|
||||
public ImapClientPool(CustomServerInformation customServerInformation, Stream protocolLogStream = null)
|
||||
{
|
||||
_customServerInformation = customServerInformation;
|
||||
_protocolLogStream = protocolLogStream;
|
||||
}
|
||||
|
||||
private async Task EnsureConnectivityAsync(ImapClient client, bool isCreatedNew)
|
||||
@@ -75,7 +80,7 @@ namespace Wino.Core.Integration
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ImapClientPoolException(ex);
|
||||
throw new ImapClientPoolException(ex, GetProtocolLogContent());
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -84,6 +89,18 @@ namespace Wino.Core.Integration
|
||||
}
|
||||
}
|
||||
|
||||
public string GetProtocolLogContent()
|
||||
{
|
||||
if (_protocolLogStream == null) return default;
|
||||
|
||||
// Set the position to the beginning of the stream in case it is not already at the start
|
||||
if (_protocolLogStream.CanSeek)
|
||||
_protocolLogStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using var reader = new StreamReader(_protocolLogStream, Encoding.UTF8, true, 1024, leaveOpen: true);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
public async Task<ImapClient> GetClientAsync()
|
||||
{
|
||||
await _semaphore.WaitAsync();
|
||||
@@ -113,7 +130,17 @@ namespace Wino.Core.Integration
|
||||
|
||||
public ImapClient CreateNewClient()
|
||||
{
|
||||
var client = new ImapClient();
|
||||
ImapClient client = null;
|
||||
|
||||
// Make sure to create a ImapClient with a protocol logger if enabled.
|
||||
if (_protocolLogStream != null)
|
||||
{
|
||||
client = new ImapClient(new ProtocolLogger(_protocolLogStream));
|
||||
}
|
||||
else
|
||||
{
|
||||
client = new ImapClient();
|
||||
}
|
||||
|
||||
HttpProxyClient proxyClient = null;
|
||||
|
||||
@@ -175,5 +202,20 @@ namespace Wino.Core.Integration
|
||||
|
||||
await client.AuthenticateAsync(_customServerInformation.IncomingServerUsername, _customServerInformation.IncomingServerPassword);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var client in _clients)
|
||||
{
|
||||
client.Disconnect(true);
|
||||
client.Dispose();
|
||||
}
|
||||
|
||||
if (_protocolLogStream != null)
|
||||
{
|
||||
_protocolLogStream.Flush();
|
||||
_protocolLogStream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ namespace Wino.Core.Messages.Mails
|
||||
{
|
||||
/// <summary>
|
||||
/// When IMAP setup dialog requestes back breadcrumb navigation.
|
||||
/// Not providing PageType will go back to previous page by doing back navigation.
|
||||
/// </summary>
|
||||
/// <param name="PageType">Type to go back.</param>
|
||||
/// <param name="Parameter">Back parameters.</param>
|
||||
public record ImapSetupBackNavigationRequested(Type PageType, object Parameter);
|
||||
public record ImapSetupBackNavigationRequested(Type PageType = null, object Parameter = null);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MailKit;
|
||||
using MailKit.Net.Imap;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Integration;
|
||||
|
||||
namespace Wino.Core.Services
|
||||
{
|
||||
@@ -14,39 +13,40 @@ namespace Wino.Core.Services
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
private readonly IAppInitializerService _appInitializerService;
|
||||
|
||||
private Stream _protocolLogStream;
|
||||
|
||||
public ImapTestService(IPreferencesService preferencesService, IAppInitializerService appInitializerService)
|
||||
{
|
||||
_preferencesService = preferencesService;
|
||||
_appInitializerService = appInitializerService;
|
||||
}
|
||||
|
||||
private void EnsureProtocolLogFileExists()
|
||||
{
|
||||
// Create new file for protocol logger.
|
||||
var localAppFolderPath = _appInitializerService.GetApplicationDataFolder();
|
||||
|
||||
var logFile = Path.Combine(localAppFolderPath, ProtocolLogFileName);
|
||||
|
||||
if (File.Exists(logFile))
|
||||
File.Delete(logFile);
|
||||
|
||||
_protocolLogStream = File.Create(logFile);
|
||||
}
|
||||
|
||||
public async Task TestImapConnectionAsync(CustomServerInformation serverInformation)
|
||||
{
|
||||
ImapClient client = null;
|
||||
EnsureProtocolLogFileExists();
|
||||
|
||||
if (_preferencesService.IsMailkitProtocolLoggerEnabled)
|
||||
var clientPool = new ImapClientPool(serverInformation, _protocolLogStream);
|
||||
|
||||
using (clientPool)
|
||||
{
|
||||
// Create new file for protocol logger.
|
||||
// This call will make sure that everything is authenticated + connected successfully.
|
||||
var client = await clientPool.GetClientAsync();
|
||||
|
||||
var localAppFolderPath = _appInitializerService.GetApplicationDataFolder();
|
||||
|
||||
var logFile = Path.Combine(localAppFolderPath, ProtocolLogFileName);
|
||||
|
||||
if (File.Exists(logFile))
|
||||
File.Delete(logFile);
|
||||
|
||||
var stream = File.Create(logFile);
|
||||
|
||||
client = new ImapClient(new ProtocolLogger(stream));
|
||||
}
|
||||
else
|
||||
client = new ImapClient();
|
||||
|
||||
using (client)
|
||||
{
|
||||
// todo: test connection
|
||||
// await client.InitializeAsync(serverInformation);
|
||||
await client.DisconnectAsync(true);
|
||||
client.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,14 +59,14 @@ namespace Wino.Core.Synchronizers
|
||||
private ImapClient _inboxIdleClient;
|
||||
|
||||
public override uint BatchModificationSize => 1000;
|
||||
public override uint InitialMessageDownloadCountPerFolder => 500;
|
||||
public override uint InitialMessageDownloadCountPerFolder => 250;
|
||||
|
||||
public ImapSynchronizer(MailAccount account, IImapChangeProcessor imapChangeProcessor) : base(account)
|
||||
{
|
||||
_clientPool = new ImapClientPool(Account.ServerInformation);
|
||||
_imapChangeProcessor = imapChangeProcessor;
|
||||
|
||||
idleDoneToken = new CancellationTokenSource();
|
||||
_imapChangeProcessor = imapChangeProcessor;
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
Reference in New Issue
Block a user