Files
libremetaverse/LibreMetaverse.RLV/LockedFolderManager.cs
nooperation d905210ecf Initial commit of LibreMetaverse.RLV and LibreMetaverse.RLV.Tests.
This library provides RLV command processing and ease of use for checking current RLV permissions and restrictions
2025-08-17 19:55:33 -04:00

276 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace LibreMetaverse.RLV
{
internal sealed class LockedFolderManager
{
private readonly IRlvQueryCallbacks _queryCallbacks;
private readonly RlvRestrictionManager _restrictionManager;
private readonly Dictionary<Guid, LockedFolder> _lockedFolders = [];
private readonly object _lockedFoldersLock = new();
internal LockedFolderManager(IRlvQueryCallbacks queryCallbacks, RlvRestrictionManager restrictionManager)
{
_queryCallbacks = queryCallbacks;
_restrictionManager = restrictionManager;
}
public IReadOnlyDictionary<Guid, LockedFolderPublic> GetLockedFolders()
{
lock (_lockedFoldersLock)
{
return _lockedFolders
.Select(n => new LockedFolderPublic(n.Value))
.ToImmutableDictionary(k => k.Id, v => v);
}
}
public bool TryGetLockedFolder(Guid folderId, [NotNullWhen(true)] out LockedFolderPublic? lockedFolder)
{
lock (_lockedFoldersLock)
{
if (_lockedFolders.TryGetValue(folderId, out var lockedFolderPrivate))
{
lockedFolder = new LockedFolderPublic(lockedFolderPrivate);
return true;
}
lockedFolder = default;
return false;
}
}
private void AddLockedFolder(RlvSharedFolder folder, RlvRestriction restriction)
{
lock (_lockedFoldersLock)
{
if (!_lockedFolders.TryGetValue(folder.Id, out var existingLockedFolder))
{
existingLockedFolder = new LockedFolder(folder);
_lockedFolders[folder.Id] = existingLockedFolder;
}
if (restriction.Behavior is RlvRestrictionType.DetachAllThis or RlvRestrictionType.DetachThis)
{
existingLockedFolder.DetachRestrictions.Add(restriction);
}
else if (restriction.Behavior is RlvRestrictionType.AttachAllThis or RlvRestrictionType.AttachThis)
{
existingLockedFolder.AttachRestrictions.Add(restriction);
}
else if (restriction.Behavior is RlvRestrictionType.DetachAllThisExcept or RlvRestrictionType.DetachThisExcept)
{
existingLockedFolder.DetachExceptions.Add(restriction);
}
else if (restriction.Behavior is RlvRestrictionType.AttachAllThisExcept or RlvRestrictionType.AttachThisExcept)
{
existingLockedFolder.AttachExceptions.Add(restriction);
}
if (restriction.Behavior is RlvRestrictionType.DetachAllThis or
RlvRestrictionType.AttachAllThis or
RlvRestrictionType.AttachAllThisExcept or
RlvRestrictionType.DetachAllThisExcept)
{
foreach (var child in folder.Children)
{
AddLockedFolder(child, restriction);
}
}
}
}
internal async Task RebuildLockedFolders(CancellationToken cancellationToken)
{
// AttachThis/DetachThis - Only search within the #RLV root
// Attachment:
// Find attachment object
// Get folder object exists in (assuming it exists in the #RLV folder) and add it to the locked folder list
//
// Shared Folder:
// Just add the shared folder to the locked folder list
//
// Attachment point / Wearable Type:
// Find and all all the folders for all of the attachments in the specified attachment point or of the wearable type.
// Add those folders to the locked folder list
var (hasSharedFolder, sharedFolder) = await _queryCallbacks.TryGetSharedFolderAsync(cancellationToken).ConfigureAwait(false);
if (!hasSharedFolder || sharedFolder == null)
{
return;
}
lock (_lockedFoldersLock)
{
_lockedFolders.Clear();
var inventoryMap = new InventoryMap(sharedFolder);
var detachThisRestrictions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.DetachThis);
var detachAllThisRestrictions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.DetachAllThis);
var attachThisRestrictions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.AttachThis);
var attachAllThisRestrictions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.AttachAllThis);
var detachThisExceptions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.DetachThisExcept);
var detachAllThisExceptions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.DetachAllThisExcept);
var attachThisExceptions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.AttachThisExcept);
var attachAllThisExceptions = _restrictionManager.GetRestrictionsByType(RlvRestrictionType.AttachAllThisExcept);
foreach (var restriction in detachThisRestrictions)
{
ProcessFolderRestrictions(restriction, sharedFolder, inventoryMap);
}
foreach (var restriction in detachAllThisRestrictions)
{
ProcessFolderRestrictions(restriction, sharedFolder, inventoryMap);
}
foreach (var restriction in attachThisRestrictions)
{
ProcessFolderRestrictions(restriction, sharedFolder, inventoryMap);
}
foreach (var restriction in attachAllThisRestrictions)
{
ProcessFolderRestrictions(restriction, sharedFolder, inventoryMap);
}
foreach (var exception in detachThisExceptions)
{
ProcessFolderException(exception, inventoryMap);
}
foreach (var exception in detachAllThisExceptions)
{
ProcessFolderException(exception, inventoryMap);
}
foreach (var exception in attachThisExceptions)
{
ProcessFolderException(exception, inventoryMap);
}
foreach (var exception in attachAllThisExceptions)
{
ProcessFolderException(exception, inventoryMap);
}
}
}
internal async Task<bool> ProcessFolderException(RlvRestriction restriction, bool isException, CancellationToken cancellationToken)
{
var (hasSharedFolder, sharedFolder) = await _queryCallbacks.TryGetSharedFolderAsync(cancellationToken).ConfigureAwait(false);
if (!hasSharedFolder || sharedFolder == null)
{
return false;
}
var inventoryMap = new InventoryMap(sharedFolder);
if (isException)
{
return ProcessFolderException(restriction, inventoryMap);
}
else
{
return ProcessFolderRestrictions(restriction, sharedFolder, inventoryMap);
}
}
private bool ProcessFolderException(RlvRestriction exception, InventoryMap inventoryMap)
{
if (exception.Args.Count == 0)
{
return false;
}
else if (exception.Args[0] is string path)
{
if (!inventoryMap.TryGetFolderFromPath(path, false, out var folder))
{
return false;
}
AddLockedFolder(folder, exception);
}
return true;
}
private bool ProcessFolderRestrictions(RlvRestriction restriction, RlvSharedFolder sharedFolder, InventoryMap inventoryMap)
{
if (restriction.Args.Count == 0)
{
var senderItem = inventoryMap.Items
.Where(n => n.Value.AttachedPrimId == restriction.Sender)
.Select(n => n.Value)
.FirstOrDefault();
if (senderItem == null)
{
return false;
}
if (!inventoryMap.Items.TryGetValue(senderItem.Id, out var item))
{
return false;
}
if (!item.FolderId.HasValue || !inventoryMap.Folders.TryGetValue(item.FolderId.Value, out var folder))
{
return false;
}
AddLockedFolder(folder, restriction);
}
else if (restriction.Args[0] is RlvWearableType wearableType)
{
var wornItems = sharedFolder.GetWornItems(wearableType);
var foldersToLock = wornItems
.Where(n => n.Folder != null)
.Select(n => n.Folder);
foreach (var folder in foldersToLock)
{
if (folder == null)
{
continue;
}
AddLockedFolder(folder, restriction);
}
}
else if (restriction.Args[0] is RlvAttachmentPoint attachmentPoint)
{
var attachedItems = sharedFolder.GetAttachedItems(attachmentPoint);
var foldersToLock = attachedItems
.Where(n => n.Folder != null)
.Select(n => n.Folder);
foreach (var folder in foldersToLock)
{
if (folder == null)
{
continue;
}
AddLockedFolder(folder, restriction);
}
}
else if (restriction.Args[0] is string path)
{
if (!inventoryMap.TryGetFolderFromPath(path, false, out var folder))
{
return false;
}
AddLockedFolder(folder, restriction);
}
else
{
return false;
}
return true;
}
}
}