Files
duplicati/proprietary/Office365/RestoreProvider/RestoreProvider.Email.cs
T

125 lines
5.5 KiB
C#

// Copyright (c) 2026 Duplicati Inc. All rights reserved.
using System.Text.Json;
using Duplicati.Library.Logging;
using Duplicati.Proprietary.Office365.SourceItems;
namespace Duplicati.Proprietary.Office365;
partial class RestoreProvider
{
private EmailRestoreHelper? _emailRestoreHelper = null;
internal EmailRestoreHelper EmailRestore => _emailRestoreHelper ??= new EmailRestoreHelper(this);
internal class EmailRestoreHelper(RestoreProvider Provider)
{
private string? _targetUserId = null;
private string? _targetMailboxId = null;
private bool _hasLoadedTargetInfo = false;
public async Task<(string? UserId, string? MailboxId)> GetUserIdAndMailboxTarget(CancellationToken cancel)
{
if (_hasLoadedTargetInfo)
{
if (string.IsNullOrWhiteSpace(_targetUserId) || string.IsNullOrWhiteSpace(_targetMailboxId))
return (null, null);
return (_targetUserId!, _targetMailboxId!);
}
var target = Provider.RestoreTarget;
if (target == null)
throw new InvalidOperationException("Restore target is not set");
if (target.Type == SourceItemType.User)
{
_targetUserId = target.Metadata.GetValueOrDefault("o365:Id");
if (!string.IsNullOrWhiteSpace(_targetUserId))
_targetMailboxId = await GetDefaultRestoreTargetMailbox(_targetUserId, cancel);
}
else if (target.Type == SourceItemType.UserMailbox)
{
_targetUserId = target.Path.TrimStart(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Skip(1).FirstOrDefault();
if (string.IsNullOrWhiteSpace(_targetUserId))
throw new InvalidOperationException("User ID is not set");
_targetMailboxId = await GetDefaultRestoreTargetMailbox(_targetUserId, cancel);
}
else if (target.Type == SourceItemType.UserMailboxFolder)
{
_targetUserId = target.Path.TrimStart(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Skip(1).FirstOrDefault();
_targetMailboxId = target.Metadata.GetValueOrDefault("o365:Id");
}
else
{
Log.WriteWarningMessage(LOGTAG, "RestoreUserEmailsInvalidTargetType", null, $"Restore target type {target.Type} is not valid for restoring emails.");
}
// Don't try to load again as that could lead to repeated warnings being logged
_hasLoadedTargetInfo = true;
if (string.IsNullOrWhiteSpace(_targetUserId) || string.IsNullOrWhiteSpace(_targetMailboxId))
{
Log.WriteWarningMessage(LOGTAG, "RestoreUserEmailsMissingIds", null, $"Missing target userId or mailboxId for restoring emails.");
return (null, null);
}
return (_targetUserId, _targetMailboxId);
}
private async Task<string> GetDefaultRestoreTargetMailbox(string userId, CancellationToken cancel)
{
const string RESTORED_FOLDER_NAME = "Restored";
var targetNames = new[] { RESTORED_FOLDER_NAME };
var path = string.Join(Path.DirectorySeparatorChar, new[] {
Uri.EscapeDataString(Office365MetaType.Users.ToString().ToLowerInvariant()),
Uri.EscapeDataString(userId),
Uri.EscapeDataString(Office365UserType.Mailbox.ToString().ToLowerInvariant())
});
var mailbox = await Provider.SourceProvider.GetEntry(path, true, cancel);
if (mailbox == null)
throw new InvalidOperationException($"Mailbox not found for user {userId}");
await foreach (var folder in mailbox.Enumerate(cancel))
{
if (cancel.IsCancellationRequested)
break;
var folderMetadata = await folder.GetMinorMetadata(cancel);
if (!Enum.TryParse<SourceItemType>(folderMetadata.GetValueOrDefault("o365:Type"), out var type))
continue;
if (type != SourceItemType.UserMailboxFolder)
continue;
var folderId = folderMetadata.GetValueOrDefault("o365:Id");
if (string.IsNullOrWhiteSpace(folderId))
continue;
var folderName = folderMetadata.GetValueOrDefault("o365:Name");
if (!string.IsNullOrWhiteSpace(folderName) && targetNames.Contains(folderName, StringComparer.OrdinalIgnoreCase))
return folderId;
}
var restoredFolder = await Provider.EmailApi.CreateMailFolderAsync(userId, "msgfolderroot", RESTORED_FOLDER_NAME, cancel);
return restoredFolder.Id;
}
public async Task RestoreMailboxSettings(string userId, Stream settingsStream, CancellationToken cancel)
{
var settings = await JsonSerializer.DeserializeAsync<GraphMailboxSettings>(settingsStream, cancellationToken: cancel);
if (settings == null) return;
await Provider.EmailApi.UpdateMailboxSettingsAsync(userId, settings, cancel);
}
public async Task RestoreMessageRule(string userId, Stream ruleStream, CancellationToken cancel)
{
var rule = await JsonSerializer.DeserializeAsync<GraphMessageRule>(ruleStream, cancellationToken: cancel);
if (rule == null) return;
await Provider.EmailApi.CreateMessageRuleAsync(userId, rule, cancel);
}
}
}