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:
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI.Notifications;
|
||||
using Microsoft.AppCenter;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
@@ -10,26 +13,36 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.Storage;
|
||||
using Windows.System.Profile;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Notifications;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Activation;
|
||||
using Wino.Core;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Domain.Models.Synchronization;
|
||||
using Wino.Core.Services;
|
||||
using Wino.Core.UWP;
|
||||
using Wino.Core.UWP.Services;
|
||||
using Wino.Mail.ViewModels;
|
||||
using Wino.Messaging.Client.Connection;
|
||||
using Wino.Messaging.Server;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino
|
||||
{
|
||||
public sealed partial class App : Application
|
||||
public sealed partial class App : Application, IRecipient<NewSynchronizationRequested>
|
||||
{
|
||||
private const string WinoLaunchLogPrefix = "[Wino Launch] ";
|
||||
private const string AppCenterKey = "90deb1d0-a77f-47d0-8a6b-7eaf111c6b72";
|
||||
@@ -37,20 +50,25 @@ namespace Wino
|
||||
public new static App Current => (App)Application.Current;
|
||||
public IServiceProvider Services { get; }
|
||||
|
||||
private BackgroundTaskDeferral connectionBackgroundTaskDeferral;
|
||||
private BackgroundTaskDeferral toastActionBackgroundTaskDeferral;
|
||||
|
||||
private readonly IWinoServerConnectionManager<AppServiceConnection> _appServiceConnectionManager;
|
||||
private readonly ILogInitializer _logInitializer;
|
||||
private readonly IThemeService _themeService;
|
||||
private readonly IDatabaseService _databaseService;
|
||||
private readonly IAppInitializerService _appInitializerService;
|
||||
private readonly IWinoSynchronizerFactory _synchronizerFactory;
|
||||
private readonly IApplicationConfiguration _appInitializerService;
|
||||
private readonly ITranslationService _translationService;
|
||||
private readonly IApplicationConfiguration _applicationFolderConfiguration;
|
||||
private readonly IDialogService _dialogService;
|
||||
|
||||
// Order matters.
|
||||
private List<IInitializeAsync> initializeServices => new List<IInitializeAsync>()
|
||||
{
|
||||
_translationService,
|
||||
_databaseService,
|
||||
_appServiceConnectionManager,
|
||||
_translationService,
|
||||
_themeService,
|
||||
_synchronizerFactory
|
||||
};
|
||||
|
||||
public App()
|
||||
@@ -61,6 +79,9 @@ namespace Wino
|
||||
EnteredBackground += OnEnteredBackground;
|
||||
LeavingBackground += OnLeavingBackground;
|
||||
|
||||
Resuming += OnResuming;
|
||||
Suspending += OnSuspending;
|
||||
|
||||
Services = ConfigureServices();
|
||||
|
||||
_logInitializer = Services.GetService<ILogInitializer>();
|
||||
@@ -70,13 +91,37 @@ namespace Wino
|
||||
ConfigurePrelaunch();
|
||||
ConfigureXbox();
|
||||
|
||||
_applicationFolderConfiguration = Services.GetService<IApplicationConfiguration>();
|
||||
|
||||
// Make sure the paths are setup on app start.
|
||||
_applicationFolderConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path;
|
||||
_applicationFolderConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path;
|
||||
|
||||
_appServiceConnectionManager = Services.GetService<IWinoServerConnectionManager<AppServiceConnection>>();
|
||||
_themeService = Services.GetService<IThemeService>();
|
||||
_databaseService = Services.GetService<IDatabaseService>();
|
||||
_appInitializerService = Services.GetService<IAppInitializerService>();
|
||||
_synchronizerFactory = Services.GetService<IWinoSynchronizerFactory>();
|
||||
_appInitializerService = Services.GetService<IApplicationConfiguration>();
|
||||
_translationService = Services.GetService<ITranslationService>();
|
||||
_dialogService = Services.GetService<IDialogService>();
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
WeakReferenceMessenger.Default.Register(this);
|
||||
}
|
||||
|
||||
private async void OnResuming(object sender, object e)
|
||||
{
|
||||
// App Service connection was lost on suspension.
|
||||
// We must restore it.
|
||||
// Server might be running already, but re-launching it will trigger a new connection attempt.
|
||||
|
||||
await _appServiceConnectionManager.ConnectAsync();
|
||||
}
|
||||
|
||||
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
deferral.Complete();
|
||||
}
|
||||
|
||||
private void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}");
|
||||
@@ -101,7 +146,7 @@ namespace Wino
|
||||
private void RegisterActivationHandlers(IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<ProtocolActivationHandler>();
|
||||
services.AddTransient<BackgroundActivationHandler>();
|
||||
// services.AddTransient<BackgroundActivationHandler>();
|
||||
services.AddTransient<ToastNotificationActivationHandler>();
|
||||
services.AddTransient<FileActivationHandler>();
|
||||
}
|
||||
@@ -138,13 +183,18 @@ namespace Wino
|
||||
services.AddTransient(typeof(ReadComposePanePageViewModel));
|
||||
services.AddTransient(typeof(MergedAccountDetailsPageViewModel));
|
||||
services.AddTransient(typeof(LanguageTimePageViewModel));
|
||||
services.AddTransient(typeof(AppPreferencesPageViewModel));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc Configuration
|
||||
|
||||
private void ConfigureLogger() => _logInitializer.SetupLogger(ApplicationData.Current.LocalFolder.Path);
|
||||
private void ConfigureLogger()
|
||||
{
|
||||
string logFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.ClientLogFile);
|
||||
_logInitializer.SetupLogger(logFilePath);
|
||||
}
|
||||
|
||||
private void ConfigureAppCenter() => AppCenter.Start(AppCenterKey, typeof(Analytics), typeof(Crashes));
|
||||
|
||||
@@ -221,9 +271,79 @@ namespace Wino
|
||||
{
|
||||
base.OnBackgroundActivated(args);
|
||||
|
||||
LogActivation($"OnBackgroundActivated -> {args.GetType().Name}, TaskInstanceIdName -> {args.TaskInstance?.Task?.Name ?? "NA"}");
|
||||
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails appServiceTriggerDetails)
|
||||
{
|
||||
// Only accept connections from callers in the same package
|
||||
if (appServiceTriggerDetails.CallerPackageFamilyName == Package.Current.Id.FamilyName)
|
||||
{
|
||||
// Connection established from the fulltrust process
|
||||
|
||||
await ActivateWinoAsync(args);
|
||||
connectionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
|
||||
args.TaskInstance.Canceled += OnConnectionBackgroundTaskCanceled;
|
||||
|
||||
_appServiceConnectionManager.Connection = appServiceTriggerDetails.AppServiceConnection;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new WinoServerConnectionEstrablished());
|
||||
}
|
||||
}
|
||||
else if (args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail)
|
||||
{
|
||||
await InitializeServicesAsync();
|
||||
|
||||
// Notification action is triggered and the app is not running.
|
||||
|
||||
toastActionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
|
||||
|
||||
args.TaskInstance.Canceled += OnToastActionClickedBackgroundTaskCanceled;
|
||||
|
||||
var toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument);
|
||||
|
||||
// All toast activation mail actions are handled here like mark as read or delete.
|
||||
// This should not launch the application on the foreground.
|
||||
|
||||
// Get the action and mail item id.
|
||||
// Prepare package and send to delegator.
|
||||
|
||||
if (toastArguments.TryGetValue(Constants.ToastActionKey, out MailOperation action) &&
|
||||
toastArguments.TryGetValue(Constants.ToastMailUniqueIdKey, out string mailUniqueIdString) &&
|
||||
Guid.TryParse(mailUniqueIdString, out Guid mailUniqueId))
|
||||
{
|
||||
|
||||
// At this point server should've already been connected.
|
||||
|
||||
var processor = Services.GetService<IWinoRequestProcessor>();
|
||||
var delegator = Services.GetService<IWinoRequestDelegator>();
|
||||
var mailService = Services.GetService<IMailService>();
|
||||
|
||||
var mailItem = await mailService.GetSingleMailItemAsync(mailUniqueId);
|
||||
|
||||
if (mailItem != null)
|
||||
{
|
||||
var package = new MailOperationPreperationRequest(action, mailItem);
|
||||
|
||||
await delegator.ExecuteAsync(package);
|
||||
}
|
||||
}
|
||||
|
||||
toastActionBackgroundTaskDeferral.Complete();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other background activations might have handlers.
|
||||
// AppServiceTrigger is handled here because delegating it to handlers somehow make it not work...
|
||||
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnToastActionClickedBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
sender.Canceled -= OnToastActionClickedBackgroundTaskCanceled;
|
||||
|
||||
Log.Information($"Toast action background task was canceled. Reason: {reason}");
|
||||
|
||||
toastActionBackgroundTaskDeferral?.Complete();
|
||||
toastActionBackgroundTaskDeferral = null;
|
||||
}
|
||||
|
||||
private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
@@ -244,9 +364,17 @@ namespace Wino
|
||||
|
||||
private bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs;
|
||||
|
||||
private async Task InitializeServicesAsync()
|
||||
{
|
||||
foreach (var service in initializeServices)
|
||||
{
|
||||
await service.InitializeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ActivateWinoAsync(object args)
|
||||
{
|
||||
await PreInitializationAsync();
|
||||
await InitializeServicesAsync();
|
||||
|
||||
if (IsInteractiveLaunchArgs(args))
|
||||
{
|
||||
@@ -270,37 +398,6 @@ namespace Wino
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tasks that must run before the activation and launch.
|
||||
/// Regardless of whether it's an interactive launch or not.
|
||||
/// </summary>
|
||||
private async Task PreInitializationAsync()
|
||||
{
|
||||
// Handle migrations.
|
||||
// TODO: Automate migration process with more proper way.
|
||||
|
||||
if (!ApplicationData.Current.LocalSettings.Values.ContainsKey("Migration_169"))
|
||||
{
|
||||
try
|
||||
{
|
||||
await _appInitializerService.MigrateAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"{WinoLaunchLogPrefix}Migration_169 failed.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
ApplicationData.Current.LocalSettings.Values["Migration_169"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var service in initializeServices)
|
||||
{
|
||||
await service.InitializeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleActivationAsync(object activationArgs)
|
||||
{
|
||||
var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs));
|
||||
@@ -323,9 +420,35 @@ namespace Wino
|
||||
private IEnumerable<ActivationHandler> GetActivationHandlers()
|
||||
{
|
||||
yield return Services.GetService<ProtocolActivationHandler>();
|
||||
yield return Services.GetService<BackgroundActivationHandler>();
|
||||
yield return Services.GetService<ToastNotificationActivationHandler>();
|
||||
yield return Services.GetService<FileActivationHandler>();
|
||||
}
|
||||
|
||||
public async void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
sender.Canceled -= OnConnectionBackgroundTaskCanceled;
|
||||
|
||||
Log.Information($"Background task {sender.Task.Name} was canceled. Reason: {reason}");
|
||||
|
||||
await _appServiceConnectionManager.DisconnectAsync();
|
||||
|
||||
connectionBackgroundTaskDeferral?.Complete();
|
||||
connectionBackgroundTaskDeferral = null;
|
||||
|
||||
_appServiceConnectionManager.Connection = null;
|
||||
}
|
||||
|
||||
public async void Receive(NewSynchronizationRequested message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var synchronizationResultResponse = await _appServiceConnectionManager.GetResponseAsync<SynchronizationResult, NewSynchronizationRequested>(message);
|
||||
synchronizationResultResponse.ThrowIfFailed();
|
||||
}
|
||||
catch (WinoServerException serverException)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.Info_SyncFailedTitle, serverException.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user