Full trust Wino Server implementation. (#295)

* Separation of messages. Introducing Wino.Messages library.

* Wino.Server and Wino.Packaging projects. Enabling full trust for UWP and app service connection manager basics.

* Remove debug code.

* Enable generating assembly info to deal with unsupported os platform warnings.

* Fix server-client connection.

* UIMessage communication. Single instancing for server and re-connection mechanism on suspension.

* Removed IWinoSynchronizerFactory from UWP project.

* Removal of background task service from core.

* Delegating changes to UI and triggering new background synchronization.

* Fix build error.

* Moved core lib messages to Messaging project.

* Better client-server communication. Handling of requests in the server. New synchronizer factory in the server.

* WAM broker and MSAL token caching for OutlookAuthenticator. Handling account creation for Outlook.

* WinoServerResponse basics.

* Delegating protocol activation for Gmail authenticator.

* Adding margin to searchbox to match action bar width.

* Move libraries into lib folder.

* Storing base64 encoded mime on draft creation instead of MimeMessage object. Fixes serialization/deserialization issue with S.T.Json

* Scrollbar adjustments

* WınoExpander for thread expander layout ıssue.

* Handling synchronizer state changes.

* Double init on background activation.

* FIxing packaging issues and new Wino Mail launcher protocol for activation from full thrust process.

* Remove debug deserialization.

* Remove debug code.

* Making sure the server connection is established when the app is launched.

* Thrust -> Trust string replacement...

* Rename package to Wino Mail

* Enable translated values in the server.

* Fixed an issue where toast activation can't find the clicked mail after the folder is initialized.

* Revert debug code.

* Change server background sync to every 3 minute and Inbox only synchronization.

* Revert google auth changes.

* App preferences page.

* Changing tray icon visibility on preference change.

* Start the server with invisible tray icon if set to invisible.

* Reconnect button on the title bar.

* Handling of toast actions.

* Enable x86 build for server during packaging.

* Get rid of old background tasks and v180 migration.

* Terminate client when Exit clicked in server.

* Introducing SynchronizationSource to prevent notifying UI after server tick synchronization.

* Remove confirmAppClose restricted capability and unused debug code in manifest.

* Closing the reconnect info popup when reconnect is clicked.

* Custom RetryHandler for OutlookSynchronizer and separating client/server logs.

* Running server on Windows startup.

* Fix startup exe.

* Fix for expander list view item paddings.

* Force full sync on app launch instead of Inbox.

* Fix draft creation.

* Fix an issue with custom folder sync logic.

* Reporting back account sync progress from server.

* Fix sending drafts and missing notifications for imap.

* Changing imap folder sync requirements.

* Retain file  count is set to 3.

* Disabled swipe gestures temporarily due to native crash
 with SwipeControl

* Save all attachments implementation.

* Localization for save all attachments button.

* Fix logging dates for logs.

* Fixing ARM64 build.

* Add ARM64 build config to packaging project.

* Comment out OutOfProcPDB for ARM64.

* Hnadling GONE response for Outlook folder synchronization.
This commit is contained in:
Burak Kaan Köse
2024-08-05 00:36:26 +02:00
committed by GitHub
parent 4dc225184d
commit ff77b2b3dc
275 changed files with 4986 additions and 2381 deletions

View File

@@ -8,8 +8,10 @@
public const string WinoLocalDraftHeader = "X-Wino-Draft-Id";
public const string LocalDraftStartPrefix = "localDraft_";
public const string ToastMailItemIdKey = nameof(ToastMailItemIdKey);
public const string ToastMailItemRemoteFolderIdKey = nameof(ToastMailItemRemoteFolderIdKey);
public const string ToastMailUniqueIdKey = nameof(ToastMailUniqueIdKey);
public const string ToastActionKey = nameof(ToastActionKey);
public const string ClientLogFile = "Client_.log";
public const string ServerLogFile = "Server_.log";
}
}

View File

@@ -11,6 +11,10 @@ namespace Wino.Core.Domain.Entities
public Guid AccountId { get; set; }
/// <summary>
/// Unique object storage for authenticators if needed.
/// </summary>
public string UniqueId { get; set; }
public string Address { get; set; }
public void RefreshTokens(TokenInformationBase tokenInformationBase)

View File

@@ -0,0 +1,12 @@
namespace Wino.Core.Domain.Enums
{
/// <summary>
/// What should happen to server app when the client is terminated.
/// </summary>
public enum ServerBackgroundMode
{
MinimizedTray, // Still runs, tray icon is visible.
Invisible, // Still runs, tray icon is invisible.
Terminate // Server is terminated as Wino terminates.
}
}

View File

@@ -0,0 +1,11 @@
namespace Wino.Core.Domain.Enums
{
public enum StartupBehaviorResult
{
Enabled,
Disabled,
DisabledByUser,
DisabledByPolicy,
Fatal
}
}

View File

@@ -0,0 +1,12 @@
namespace Wino.Core.Domain.Enums
{
/// <summary>
/// Enumeration for the source of synchronization.
/// Right now it can either be from the client or the server.
/// </summary>
public enum SynchronizationSource
{
Client,
Server
}
}

View File

@@ -21,6 +21,7 @@
MailListPage,
ReadComposePanePage,
LanguageTimePage,
AppPreferencesPage,
SettingOptionsPage,
}
}

View File

@@ -0,0 +1,11 @@
namespace Wino.Core.Domain.Enums
{
public enum WinoServerConnectionStatus
{
None,
Connecting,
Connected,
Disconnected,
Failed
}
}

View File

@@ -1,9 +0,0 @@
using System;
namespace Wino.Core.Domain.Exceptions
{
/// <summary>
/// An exception thrown when the background task execution policies are denied for some reason.
/// </summary>
public class BackgroundTaskExecutionRequestDeniedException : Exception { }
}

View File

@@ -0,0 +1,12 @@
using System;
namespace Wino.Core.Domain.Exceptions
{
/// <summary>
/// All server crash types. Wino Server ideally should not throw anything else than this Exception type.
/// </summary>
public class WinoServerException : Exception
{
public WinoServerException(string message) : base(message) { }
}
}

View File

@@ -0,0 +1,20 @@
using System;
namespace Wino.Core.Domain.Extensions
{
public static class MimeExtensions
{
public static string GetBase64MimeMessage(this MimeKit.MimeMessage message)
{
using System.IO.MemoryStream memoryStream = new();
message.WriteTo(MimeKit.FormatOptions.Default, memoryStream);
byte[] buffer = memoryStream.GetBuffer();
int count = (int)memoryStream.Length;
return Convert.ToBase64String(buffer);
}
public static MimeKit.MimeMessage GetMimeMessageFromBase64(this string base64)
=> MimeKit.MimeMessage.Load(new System.IO.MemoryStream(Convert.FromBase64String(base64)));
}
}

View File

@@ -1,12 +0,0 @@
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces
{
public interface IAppInitializerService
{
string GetApplicationDataFolder();
string GetPublisherSharedFolder();
Task MigrateAsync();
}
}

View File

@@ -0,0 +1,21 @@
namespace Wino.Core.Domain.Interfaces
{
/// <summary>
/// Singleton object that holds the application data folder path and the publisher shared folder path.
/// Load the values before calling any service.
/// App data folder is used for storing files.
/// Pubhlisher cache folder is only used for database file so other apps can access it in the same package by same publisher.
/// </summary>
public interface IApplicationConfiguration
{
/// <summary>
/// Application data folder.
/// </summary>
string ApplicationDataFolderPath { get; set; }
/// <summary>
/// Publisher shared folder path.
/// </summary>
string PublisherSharedFolderPath { get; set; }
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Enums;
@@ -23,28 +22,12 @@ namespace Wino.Core.Domain.Interfaces
/// <summary>
/// Initial creation of token. Requires user interaction.
/// This will save token into database, but still returns for account creation
/// This will cache the token but still returns for account creation
/// since account address is required.
/// </summary>
/// <param name="expectedAccountAddress">Token cache might ask for regeneration of token for specific
/// account address. If one is provided and re-generation native token doesn't belong to this address
/// token saving to database won't happen.</param>
/// <returns>Freshly created TokenInformation..</returns>
Task<TokenInformation> GenerateTokenAsync(MailAccount account, bool saveToken);
/// <summary>
/// Required for external authorization on launched browser to continue.
/// Used for Gmail.
/// </summary>
/// <param name="authorizationResponseUri">Response's redirect uri.</param>
void ContinueAuthorization(Uri authorizationResponseUri);
/// <summary>
/// For external browser required authentications.
/// Canceling Gmail authentication dialog etc.
/// </summary>
void CancelAuthorization();
/// <summary>
/// ClientId in case of needed for authorization/authentication.
/// </summary>

View File

@@ -0,0 +1,6 @@
namespace Wino.Core.Domain.Interfaces
{
public interface IOutlookAuthenticator : IAuthenticator { }
public interface IGmailAuthenticator : IAuthenticator { }
public interface IImapAuthenticator : IAuthenticator { }
}

View File

@@ -0,0 +1,54 @@
using System.Threading;
using System.Threading.Tasks;
using MailKit;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Synchronization;
namespace Wino.Core.Domain.Interfaces
{
public interface IBaseSynchronizer
{
/// <summary>
/// Account that is assigned for this synchronizer.
/// </summary>
MailAccount Account { get; }
/// <summary>
/// Synchronizer state.
/// </summary>
AccountSynchronizerState State { get; }
/// <summary>
/// Queues a single request to be executed in the next synchronization.
/// </summary>
/// <param name="request">Request to queue.</param>
void QueueRequest(IRequestBase request);
/// <summary>
/// TODO
/// </summary>
/// <returns>Whether active synchronization is stopped or not.</returns>
bool CancelActiveSynchronization();
/// <summary>
/// Performs a full synchronization with the server with given options.
/// This will also prepares batch requests for execution.
/// Requests are executed in the order they are queued and happens before the synchronization.
/// Result of the execution queue is processed during the synchronization.
/// </summary>
/// <param name="options">Options for synchronization.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Result summary of synchronization.</returns>
Task<SynchronizationResult> SynchronizeAsync(SynchronizationOptions options, CancellationToken cancellationToken = default);
/// <summary>
/// Downloads a single MIME message from the server and saves it to disk.
/// </summary>
/// <param name="mailItem">Mail item to download from server.</param>
/// <param name="transferProgress">Optional progress reporting for download operation.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task DownloadMissingMimeMessageAsync(IMailItem mailItem, ITransferProgress transferProgress, CancellationToken cancellationToken = default);
}
}

View File

@@ -0,0 +1,8 @@
namespace Wino.Core.Domain.Interfaces
{
/// <summary>
/// All messages that Client sends to Server and awaits a response in return.
/// For example; triggering a new synchronization request.
/// </summary>
public interface IClientMessage;
}

View File

@@ -2,7 +2,7 @@
{
public interface ILogInitializer
{
void SetupLogger(string logFolderPath);
void SetupLogger(string fullLogFilePath);
void RefreshLoggingLevel();
}

View File

@@ -11,7 +11,7 @@ namespace Wino.Core.Domain.Interfaces
{
Task<MailCopy> GetSingleMailItemAsync(string mailCopyId, string remoteFolderId);
Task<MailCopy> GetSingleMailItemAsync(Guid uniqueMailId);
Task<MailCopy> CreateDraftAsync(MailAccount composerAccount, MimeMessage generatedReplyMime, MimeMessage replyingMimeMessage = null, IMailItem replyingMailItem = null);
Task<MailCopy> CreateDraftAsync(MailAccount composerAccount, string generatedReplyMimeMessageBase64, MimeMessage replyingMimeMessage = null, IMailItem replyingMailItem = null);
Task<List<IMailItem>> FetchMailsAsync(MailListInitializationOptions options);
/// <summary>
@@ -51,7 +51,16 @@ namespace Wino.Core.Domain.Interfaces
/// <param name="newThreadId"></param>
Task MapLocalDraftAsync(string newMailCopyId, string newDraftId, string newThreadId);
Task<MimeMessage> CreateDraftMimeMessageAsync(Guid accountId, DraftCreationOptions options);
/// <summary>
/// Creates a draft message with the given options.
/// </summary>
/// <param name="accountId">Account to create draft for.</param>
/// <param name="options">Draft creation options.</param>
/// <returns>
/// Base64 encoded string of MimeMessage object.
/// This is mainly for serialization purposes.
/// </returns>
Task<string> CreateDraftMimeBase64Async(Guid accountId, DraftCreationOptions options);
Task UpdateMailAsync(MailCopy mailCopy);
/// <summary>
@@ -92,5 +101,14 @@ namespace Wino.Core.Domain.Interfaces
/// </summary>
/// <param name="folderId">Folder id to get unread mails for.</param>
Task<List<MailCopy>> GetUnreadMailsByFolderIdAsync(Guid folderId);
/// <summary>
/// Checks whether the mail exists in the folder.
/// When deciding Create or Update existing mail, we need to check if the mail exists in the folder.
/// </summary>
/// <param name="messageId">Message id</param>
/// <param name="folderId">Folder's local id.</param>
/// <returns>Whether mail exists in the folder or not.</returns>
Task<bool> IsMailExistsAsync(string mailCopyId, Guid folderId);
}
}

View File

@@ -11,6 +11,20 @@ namespace Wino.Core.Domain.Interfaces
Task<string> GetEditorBundlePathAsync();
Task LaunchFileAsync(string filePath);
Task LaunchUriAsync(Uri uri);
/// <summary>
/// Launches the default browser with the specified uri and waits for protocol activation to finish.
/// </summary>
/// <param name="authenticator"></param>
/// <returns>Response callback from the browser.</returns>
Task<Uri> GetAuthorizationResponseUriAsync(IAuthenticator authenticator, string authorizationUri);
/// <summary>
/// Finalizes GetAuthorizationResponseUriAsync for current IAuthenticator.
/// </summary>
/// <param name="authorizationResponseUri"></param>
void ContinueAuthorization(Uri authorizationResponseUri);
bool IsAppRunning();
string GetFullAppVersion();
@@ -21,5 +35,11 @@ namespace Wino.Core.Domain.Interfaces
/// Some cryptographic shit is needed for requesting Google authentication in UWP.
/// </summary>
GoogleAuthorizationRequest GetGoogleAuthorizationRequest();
/// <summary>
/// Gets or sets the function that returns a pointer for main window hwnd for UWP.
/// This is used to display WAM broker dialog on running UWP app called by a windowless server code.
/// </summary>
Func<IntPtr> GetCoreWindowHwnd { get; set; }
}
}

View File

@@ -150,5 +150,10 @@ namespace Wino.Core.Domain.Interfaces
/// Setting: Whether the next item should be automatically selected once the current item is moved or removed.
/// </summary>
bool AutoSelectNextItem { get; set; }
/// <summary>
/// Setting: Gets or sets what should happen to server app when the client is terminated.
/// </summary>
ServerBackgroundMode ServerTerminationBehavior { get; set; }
}
}

View File

@@ -22,7 +22,7 @@ namespace Wino.Core.Domain.Interfaces
TRequest NativeRequest { get; }
}
public interface IRequestBase
public interface IRequestBase : IClientMessage
{
/// <summary>
/// Synchronizer option to perform.

View File

@@ -0,0 +1,20 @@
using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces
{
public interface IStartupBehaviorService
{
/// <summary>
/// Gets whether Wino Server is set to launch on startup or not.
/// </summary>
Task<StartupBehaviorResult> GetCurrentStartupBehaviorAsync();
/// <summary>
/// Enables/disables the current startup behavior for Wino Server.
/// </summary>
/// <param name="isEnabled">Whether to launch enabled or disabled.</param>
/// <returns>True if operation success, false if not.</returns>
Task<StartupBehaviorResult> ToggleStartupBehavior(bool isEnabled);
}
}

View File

@@ -1,19 +0,0 @@
using System;
namespace Wino.Core.Domain.Interfaces
{
/// <summary>
/// An interface for reporting progress of the synchronization.
/// Gmail does not support reporting folder progress.
/// For others, account progress is calculated based on the number of folders.
/// </summary>
public interface ISynchronizationProgress
{
/// <summary>
/// Reports account synchronization progress.
/// </summary>
/// <param name="accountId">Account id for the report.</param>
/// <param name="progress">Value. This is always between 0 - 100</param>
void AccountProgressUpdated(Guid accountId, int progress);
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces
{
public interface ISynchronizerFactory
{
Task<IBaseSynchronizer> GetAccountSynchronizerAsync(Guid accountId);
Task InitializeAsync();
}
}

View File

@@ -1,4 +1,4 @@
namespace Wino.Core.Domain.Models.Requests
namespace Wino.Core.Domain.Interfaces
{
/// <summary>
/// Interface for all messages to report UI changes from synchronizers to UI.
@@ -6,5 +6,6 @@
/// They are sent either from processor or view models to signal some other
/// parts of the application.
/// </summary>
public interface IUIMessage;
}

View File

@@ -0,0 +1,60 @@
using System;
using System.Threading.Tasks;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Server;
namespace Wino.Core.Domain.Interfaces
{
public interface IWinoServerConnectionManager
{
/// <summary>
/// When the connection status changes, this event will be triggered.
/// </summary>
event EventHandler<WinoServerConnectionStatus> StatusChanged;
/// <summary>
/// Gets the connection status.
/// </summary>
WinoServerConnectionStatus Status { get; }
/// <summary>
/// Launches Full Trust process (Wino Server) and awaits connection completion.
/// If connection is not established in 5 seconds, it will return false.
/// If the server process is already running, it'll connect to existing one.
/// If the server process is not running, it'll be launched and connection establishment is awaited.
/// </summary>
/// <returns>Whether connection is established or not.</returns>
Task<bool> ConnectAsync();
/// <summary>
/// Disconnects from existing connection and disposes the connection.
/// </summary>
/// <returns>Whether disconnection is succesfull or not.</returns>
Task<bool> DisconnectAsync();
/// <summary>
/// Queues a new user request to be processed by Wino Server.
/// Healthy connection must present before calling this method.
/// </summary>
/// <param name="request">Request to queue for synchronizer in the server.</param>
/// <param name="accountId">Account id to queueu request for.</param>
Task QueueRequestAsync(IRequestBase request, Guid accountId);
/// <summary>
/// Returns response from server for the given request.
/// </summary>
/// <typeparam name="TResponse">Response type.</typeparam>
/// <typeparam name="TRequestType">Request type.</typeparam>
/// <param name="clientMessage">Request type.</param>
/// <returns>Response received from the server for the given TResponse type.</returns>
Task<WinoServerResponse<TResponse>> GetResponseAsync<TResponse, TRequestType>(TRequestType clientMessage) where TRequestType : IClientMessage;
}
public interface IWinoServerConnectionManager<TAppServiceConnection> : IWinoServerConnectionManager, IInitializeAsync
{
/// <summary>
/// Existing connection handle to the server of TAppServiceConnection type.
/// </summary>
TAppServiceConnection Connection { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
using System;
using Wino.Core.Domain.Entities;
namespace Wino.Core.Domain.Interfaces
{
public interface IWinoSynchronizerFactory : IInitializeAsync
{
IBaseSynchronizer GetAccountSynchronizer(Guid accountId);
IBaseSynchronizer CreateNewSynchronizer(MailAccount account);
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Specialized;
using System.Linq;
using System.Text.Json.Serialization;
using MimeKit;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Enums;
@@ -8,6 +9,7 @@ namespace Wino.Core.Domain.Models.MailItem
{
public class DraftCreationOptions
{
[JsonIgnore]
public MimeMessage ReferenceMimeMessage { get; set; }
public MailCopy ReferenceMailCopy { get; set; }
public DraftCreationReason Reason { get; set; }

View File

@@ -1,23 +1,49 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization;
using MimeKit;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Extensions;
namespace Wino.Core.Domain.Models.MailItem
{
public class DraftPreperationRequest : DraftCreationOptions
{
public DraftPreperationRequest(MailAccount account, MailCopy createdLocalDraftCopy, MimeMessage createdLocalDraftMimeMessage)
public DraftPreperationRequest(MailAccount account, MailCopy createdLocalDraftCopy, string base64EncodedMimeMessage)
{
Account = account ?? throw new ArgumentNullException(nameof(account));
CreatedLocalDraftCopy = createdLocalDraftCopy ?? throw new ArgumentNullException(nameof(createdLocalDraftCopy));
CreatedLocalDraftMimeMessage = createdLocalDraftMimeMessage ?? throw new ArgumentNullException(nameof(createdLocalDraftMimeMessage));
// MimeMessage is not serializable with System.Text.Json. Convert to base64 string.
// This is additional work when deserialization needed, but not much to do atm.
Base64LocalDraftMimeMessage = base64EncodedMimeMessage;
}
[JsonConstructor]
private DraftPreperationRequest() { }
public MailCopy CreatedLocalDraftCopy { get; set; }
public MimeMessage CreatedLocalDraftMimeMessage { get; set; }
public string Base64LocalDraftMimeMessage { get; set; }
[JsonIgnore]
private MimeMessage createdLocalDraftMimeMessage;
[JsonIgnore]
public MimeMessage CreatedLocalDraftMimeMessage
{
get
{
if (createdLocalDraftMimeMessage == null)
{
createdLocalDraftMimeMessage = Base64LocalDraftMimeMessage.GetMimeMessageFromBase64();
}
return createdLocalDraftMimeMessage;
}
}
public MailAccount Account { get; }
}
}

View File

@@ -1,7 +1,49 @@
using MimeKit;
using System.Text.Json.Serialization;
using MimeKit;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Extensions;
namespace Wino.Core.Domain.Models.MailItem
{
public record SendDraftPreparationRequest(MailCopy MailItem, MimeMessage Mime, MailItemFolder DraftFolder, MailItemFolder SentFolder, MailAccountPreferences AccountPreferences);
public class SendDraftPreparationRequest
{
public MailCopy MailItem { get; set; }
public string Base64MimeMessage { get; set; }
public MailItemFolder SentFolder { get; set; }
public MailItemFolder DraftFolder { get; set; }
public MailAccountPreferences AccountPreferences { get; set; }
public SendDraftPreparationRequest(MailCopy mailItem,
MailItemFolder sentFolder,
MailItemFolder draftFolder,
MailAccountPreferences accountPreferences,
string base64MimeMessage)
{
MailItem = mailItem;
SentFolder = sentFolder;
DraftFolder = draftFolder;
AccountPreferences = accountPreferences;
Base64MimeMessage = base64MimeMessage;
}
[JsonConstructor]
private SendDraftPreparationRequest() { }
[JsonIgnore]
private MimeMessage mime;
[JsonIgnore]
public MimeMessage Mime
{
get
{
if (mime == null)
{
mime = Base64MimeMessage.GetMimeMessageFromBase64();
}
return mime;
}
}
}
}

View File

@@ -1,8 +1,7 @@
using System;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.MailItem;
namespace Wino.Core.Domain.Models.Requests
namespace Wino.Core.Domain.Models.MailItem
{
/// <summary>
/// Defines a single rule for toggling user actions if needed.

View File

@@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace Wino.Core.Domain.Models.Reader
{
/// <summary>
/// Used to pass messages from the webview to the app.
/// </summary>
public class WebViewMessage
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.Domain.Models.Requests
{
/// <summary>
/// Encapsulates request to queue and account for synchronizer.
/// </summary>
/// <param name="AccountId"><inheritdoc/></param>
/// <param name="Request"></param>
/// <param name="Request">Prepared request for the server.</param>
/// <param name="AccountId">Whihc account to execute this request for.</param>
public record ServerRequestPackage(Guid AccountId, IRequestBase Request) : IClientMessage
{
public override string ToString() => $"Server Package: {Request.GetType().Name}";
}
}

View File

@@ -1,9 +0,0 @@
namespace Wino.Core.Domain.Models.Requests
{
// Used to pass messages from the webview to the app.
public class WebViewMessage
{
public string type { get; set; }
public string value { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
using Wino.Core.Domain.Exceptions;
namespace Wino.Core.Domain.Models.Server
{
/// <summary>
/// Encapsulates responses from the Wino server.
/// Exceptions are stored separately in the Message and StackTrace properties due to serialization issues.
/// </summary>
/// <typeparam name="T">Type of the expected response.</typeparam>
public class WinoServerResponse<T>
{
public bool IsSuccess { get; set; }
public string Message { get; set; }
public T Data { get; set; }
// protected WinoServerResponse() { }
public static WinoServerResponse<T> CreateSuccessResponse(T data)
{
return new WinoServerResponse<T>
{
IsSuccess = true,
Data = data
};
}
public static WinoServerResponse<T> CreateErrorResponse(string message)
{
return new WinoServerResponse<T>
{
IsSuccess = false,
Message = message
};
}
public void ThrowIfFailed()
{
if (!IsSuccess)
throw new WinoServerException(Message);
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
namespace Wino.Core.Domain.Models.Synchronization
{
@@ -27,11 +26,6 @@ namespace Wino.Core.Domain.Models.Synchronization
/// </summary>
public List<Guid> SynchronizationFolderIds { get; set; }
/// <summary>
/// A listener to be notified about the progress of the synchronization.
/// </summary>
public ISynchronizationProgress ProgressListener { get; set; }
/// <summary>
/// When doing a linked inbox synchronization, we must ignore reporting completion to the caller for each folder.
/// This Id will help tracking that. Id is unique, but this one can be the same for all sync requests

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.MailItem;
@@ -6,8 +7,14 @@ namespace Wino.Core.Domain.Models.Synchronization
{
public class SynchronizationResult
{
protected SynchronizationResult() { }
public SynchronizationResult() { }
/// <summary>
/// Gets the new downloaded messages from synchronization.
/// Server will create notifications for these messages.
/// It's ignored in serialization. Client should not react to this.
/// </summary>
[JsonIgnore]
public IEnumerable<IMailItem> DownloadedMessages { get; set; } = new List<IMailItem>();
public SynchronizationCompletedState CompletedState { get; set; }

View File

@@ -105,6 +105,7 @@
"ElementTheme_Default": "Use system setting",
"ElementTheme_Light": "Light mode",
"Emoji": "Emoji",
"Exception_WinoServerException": "Wino server failed.",
"Exception_ImapAutoDiscoveryFailed": "Couldn't find mailbox settings.",
"Exception_ImapClientPoolFailed": "IMAP Client Pool failed.",
"Exception_AuthenticationCanceled": "Authentication canceled",
@@ -335,6 +336,7 @@
"ProtocolLogAvailable_Message": "Protocol logs are available for diagnostics.",
"Results": "Results",
"Right": "Right",
"Reader_SaveAllAttachmentButtonText": "Save all attachments",
"SynchronizationFolderReport_Success": "up to date",
"SynchronizationFolderReport_Failed": "synchronization is failed",
"SearchBarPlaceholder": "Search",
@@ -418,6 +420,19 @@
"SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail",
"SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.",
"SettingsManageAccountSettings_Title": "Manage Account Settings",
"SettingsAppPreferences_Title": "App Preferences",
"SettingsAppPreferences_Description": "General settings / preferences for Wino Mail.",
"SettingsAppPreferences_CloseBehavior_Title": "Application close behavior",
"SettingsAppPreferences_CloseBehavior_Description": "What should happen when you close the app?",
"SettingsAppPreferences_StartupBehavior_Title": "Start minimized on Windows startup",
"SettingsAppPreferences_StartupBehavior_Description": "Allow Wino Mail to launch minimized when Windows starts. Always allow it to receive notifications.",
"SettingsAppPreferences_StartupBehavior_Enabled": "Wino Mail successfully set to be launched in the background on Windows startup.",
"SettingsAppPreferences_StartupBehavior_Disabled": "Wino Mail will not be launched on Windows startup. This will cause you to miss notifications when you restart your computer.",
"SettingsAppPreferences_StartupBehavior_DisabledByPolicy": "Your administrator or group policies disabled running applications on startup. Thus, Wino Mail can't be set to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_DisabledByUser": "Please go to Task Manager -> Startup tab to allow Wino Mail to launch on Windows startup.",
"SettingsAppPreferences_StartupBehavior_FatalError": "Fatal error occurred while changing the startup mode for Wino Mail.",
"SettingsAppPreferences_StartupBehavior_Enable": "Enable",
"SettingsAppPreferences_StartupBehavior_Disable": "Disable",
"SettingsReorderAccounts_Title": "Reorder Accounts",
"SettingsReorderAccounts_Description": "Change the order of accounts in the account list.",
"SettingsManageLink_Description": "Move items to add new link or remove existing link.",
@@ -523,6 +538,16 @@
"SettingsSignature_AddCustomSignature_Button": "Add signature",
"SettingsSignature_EditSignature_Title": "Edit signature",
"SettingsSignature_DeleteSignature_Title": "Delete signature",
"SettingsSignature_NoneSignatureName": "None"
"SettingsSignature_NoneSignatureName": "None",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title": "Minimize to system tray",
"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description": "Wino Mail will keep running on the system tray. Available to launch by clicking on an icon. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title": "Run in the background",
"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description": "Wino Mail will keep running in the background. You will be notified as new mails arrive.",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title": "Terminate",
"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description": "Wino Mail will not keep running anywhere. You will not be notified as new mails arrive. Launch Wino Mail again to continue mail synchronization.",
"TitleBarServerDisconnectedButton_Title": "no connection",
"TitleBarServerDisconnectedButton_Description": "Wino is disconnected from the network. Click reconnect to restore connection.",
"TitleBarServerReconnectButton_Title": "reconnect",
"TitleBarServerReconnectingButton_Title": "connecting"
}

View File

@@ -548,6 +548,11 @@ namespace Wino.Core.Domain
/// </summary>
public static string Emoji => Resources.GetTranslatedString(@"Emoji");
/// <summary>
/// Wino server failed.
/// </summary>
public static string Exception_WinoServerException => Resources.GetTranslatedString(@"Exception_WinoServerException");
/// <summary>
/// Couldn't find mailbox settings.
/// </summary>
@@ -1698,6 +1703,11 @@ namespace Wino.Core.Domain
/// </summary>
public static string Right => Resources.GetTranslatedString(@"Right");
/// <summary>
/// Save all attachments
/// </summary>
public static string Reader_SaveAllAttachmentButtonText => Resources.GetTranslatedString(@"Reader_SaveAllAttachmentButtonText");
/// <summary>
/// up to date
/// </summary>
@@ -2113,6 +2123,71 @@ namespace Wino.Core.Domain
/// </summary>
public static string SettingsManageAccountSettings_Title => Resources.GetTranslatedString(@"SettingsManageAccountSettings_Title");
/// <summary>
/// App Preferences
/// </summary>
public static string SettingsAppPreferences_Title => Resources.GetTranslatedString(@"SettingsAppPreferences_Title");
/// <summary>
/// General settings / preferences for Wino Mail.
/// </summary>
public static string SettingsAppPreferences_Description => Resources.GetTranslatedString(@"SettingsAppPreferences_Description");
/// <summary>
/// Application close behavior
/// </summary>
public static string SettingsAppPreferences_CloseBehavior_Title => Resources.GetTranslatedString(@"SettingsAppPreferences_CloseBehavior_Title");
/// <summary>
/// What should happen when you close the app?
/// </summary>
public static string SettingsAppPreferences_CloseBehavior_Description => Resources.GetTranslatedString(@"SettingsAppPreferences_CloseBehavior_Description");
/// <summary>
/// Start minimized on Windows startup
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_Title => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_Title");
/// <summary>
/// Allow Wino Mail to launch minimized when Windows starts. Always allow it to receive notifications.
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_Description => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_Description");
/// <summary>
/// Wino Mail successfully set to be launched in the background on Windows startup.
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_Enabled => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_Enabled");
/// <summary>
/// Wino Mail will not be launched on Windows startup. This will cause you to miss notifications when you restart your computer.
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_Disabled => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_Disabled");
/// <summary>
/// Your administrator or group policies disabled running applications on startup. Thus, Wino Mail can't be set to launch on Windows startup.
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_DisabledByPolicy => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_DisabledByPolicy");
/// <summary>
/// Please go to Task Manager -> Startup tab to allow Wino Mail to launch on Windows startup.
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_DisabledByUser => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_DisabledByUser");
/// <summary>
/// Fatal error occurred while changing the startup mode for Wino Mail.
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_FatalError => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_FatalError");
/// <summary>
/// Enable
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_Enable => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_Enable");
/// <summary>
/// Disable
/// </summary>
public static string SettingsAppPreferences_StartupBehavior_Disable => Resources.GetTranslatedString(@"SettingsAppPreferences_StartupBehavior_Disable");
/// <summary>
/// Reorder Accounts
/// </summary>
@@ -2642,5 +2717,55 @@ namespace Wino.Core.Domain
/// None
/// </summary>
public static string SettingsSignature_NoneSignatureName => Resources.GetTranslatedString(@"SettingsSignature_NoneSignatureName");
/// <summary>
/// Minimize to system tray
/// </summary>
public static string SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title => Resources.GetTranslatedString(@"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title");
/// <summary>
/// Wino Mail will keep running on the system tray. Available to launch by clicking on an icon. You will be notified as new mails arrive.
/// </summary>
public static string SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description => Resources.GetTranslatedString(@"SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Description");
/// <summary>
/// Run in the background
/// </summary>
public static string SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title => Resources.GetTranslatedString(@"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title");
/// <summary>
/// Wino Mail will keep running in the background. You will be notified as new mails arrive.
/// </summary>
public static string SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description => Resources.GetTranslatedString(@"SettingsAppPreferences_ServerBackgroundingMode_Invisible_Description");
/// <summary>
/// Terminate
/// </summary>
public static string SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title => Resources.GetTranslatedString(@"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title");
/// <summary>
/// Wino Mail will not keep running anywhere. You will not be notified as new mails arrive. Launch Wino Mail again to continue mail synchronization.
/// </summary>
public static string SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description => Resources.GetTranslatedString(@"SettingsAppPreferences_ServerBackgroundingMode_Terminate_Description");
/// <summary>
/// no connection
/// </summary>
public static string TitleBarServerDisconnectedButton_Title => Resources.GetTranslatedString(@"TitleBarServerDisconnectedButton_Title");
/// <summary>
/// Wino is disconnected from the network. Click reconnect to restore connection.
/// </summary>
public static string TitleBarServerDisconnectedButton_Description => Resources.GetTranslatedString(@"TitleBarServerDisconnectedButton_Description");
/// <summary>
/// reconnect
/// </summary>
public static string TitleBarServerReconnectButton_Title => Resources.GetTranslatedString(@"TitleBarServerReconnectButton_Title");
/// <summary>
/// connecting
/// </summary>
public static string TitleBarServerReconnectingButton_Title => Resources.GetTranslatedString(@"TitleBarServerReconnectingButton_Title");
}
}

View File

@@ -4,8 +4,19 @@
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<LangVersion>12.0</LangVersion>
<Platforms>AnyCPU;x64;x86</Platforms>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Models\Communication\**" />
<EmbeddedResource Remove="Models\Communication\**" />
<None Remove="Models\Communication\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Interfaces\IWinoSynchronizerFactory.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="Translations\ca_ES\resources.json" />
<None Remove="Translations\cs_CZ\resources.json" />
@@ -49,9 +60,11 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MimeKit" Version="4.4.0" />
<PackageReference Include="MimeKit" Version="4.7.1" />
<PackageReference Include="MailKit" Version="4.7.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />