File: MS\Internal\Documents\Application\CommandEnforcer.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationUI\PresentationUI_r5prqfe0_wpftmp.csproj (PresentationUI)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// Description: Manager for Enforcing RM Permissions on DocumentApplication-specific RoutedUICommands.
 
using MS.Internal.Documents;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows.Input;
using System.Windows.TrustUI;
 
namespace MS.Internal.Documents.Application
{
    /// <summary>
    /// A PolicyBinding represents a binding between a Command and the RMPolicy required to
    /// Invoke said command.
    /// </summary>
    internal sealed class PolicyBinding
    {
        /// <summary>
        /// Constructor for a PolicyBinding.
        /// </summary>
        /// <param name="command">The Command to bind the permission to.</param>
        /// <param name="policy">The RMPolicy to be bound to the above Command.</param>
        public PolicyBinding(RoutedUICommand command, RightsManagementPolicy policy)
        {
            ArgumentNullException.ThrowIfNull(command);
 
            _command = command;
            _policy = policy;
        }
 
        /// <summary>
        /// The Command associated with this binding.
        /// </summary>    
        public RoutedUICommand Command
        {
            get { return _command; }
        }
 
        /// <summary>
        /// The RMPolicy associated with this binding.
        /// </summary>
        public RightsManagementPolicy Policy
        {
            get { return _policy; }
        }
 
        private RoutedUICommand _command;
        private RightsManagementPolicy _policy;
    }
 
    /// <summary>
    /// The CommandEnforcer class provides a very simple central repository for RM enforced
    /// Commands used by Mongoose.
    /// </summary>
    internal sealed class CommandEnforcer
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="docViewer">The DocumentApplicationDocumentViewer parent.</param>
        internal CommandEnforcer(DocumentApplicationDocumentViewer docViewer)
        {
            //Create our bindings List.
            _bindings = new List<PolicyBinding>(_initialBindCount);
            _docViewer = docViewer;
        }
 
        /// <summary>
        /// Finalizer
        /// </summary>
        ~CommandEnforcer()
        {
            // Ensure that PrintScreen is re-enabled in native code
            DisablePrintScreen(false);
        }
 
        /// <summary>
        /// Adds a new binding to the Enforcer.
        /// NOTE: CommandEnforcer does not check for duplicate entries, so be warned
        /// that if you have duplicate entries, the newest duplicate will be the one whose
        /// enforcement is honored.
        /// </summary>
        /// <param name="bind">The binding to add</param>
        internal void AddBinding(PolicyBinding bind)
        {
            ArgumentNullException.ThrowIfNull(bind);
 
            _bindings.Add(bind);
        }
 
        /// <summary>
        /// Enforces the RMPolicy by walking through the PolicyBinding and setting the
        /// IsBlockedByRM property on the associated Commands as appropriate.    
        /// </summary>
        /// <param name="policyToEnforce"></param>
        internal void Enforce()
        {
            Invariant.Assert(
                _docViewer != null,
                "CommandEnforcer has no reference to the parent DocumentApplicationDocumentViewer.");
 
            // Get the current policy from the parent DocumentApplicationDocumentViewer
            RightsManagementPolicy policyToEnforce = _docViewer.RightsManagementPolicy;
            
            // Walk through the list of bindings
            foreach (PolicyBinding binding in _bindings)
            {
                // If the incoming policy allows this action, and it completely masks our binding policy, 
                // then we set the IsBlockedByRM property to false, as this Command is permitted.
                binding.Command.IsBlockedByRM = !((policyToEnforce & binding.Policy) == binding.Policy);
            }
 
            // Enforce PrintScreen
            //   This will disable PrintScreen if neither AllowCopy or AllowPrint
            //   permission is granted.
            bool disablePrintScreen = !(((policyToEnforce & RightsManagementPolicy.AllowCopy)
                                            == RightsManagementPolicy.AllowCopy) ||
                                        ((policyToEnforce & RightsManagementPolicy.AllowPrint)
                                            == RightsManagementPolicy.AllowPrint));
            DisablePrintScreen(disablePrintScreen);
        }
 
        /// <summary>
        /// Disable all PrintScreen related functions in the system.
        /// </summary>
        /// <param name="disable">True if PrintScreen should be disabled, false otherwise.</param>
        private void DisablePrintScreen(bool disable)
        {
            // To ensure that everytime PrintScreen is disabled it is also re-enabled we
            // only issue the system call if the value has changed.  
            if (disable != _printScreenDisabled)
            {
                // Check if currently running in 32bit and that RM is available.
                if ((!_isRMMissing) && (IntPtr.Size == 4))
                {
                    // Initialize hresult to a valid return value.
                    int hresult = UnsafeNativeMethods.FAIL;
 
                    try
                    {
                        // Make call to RM API here to change the count.
                        hresult = UnsafeNativeMethods.DRMRegisterContent(disable);
                    }
                    catch (System.DllNotFoundException)
                    {
                        // We could not find MSDRM.DLL to disable the print screen.
                        // Just silently catch this exception and fail.
                        // This is safe to catch, because it means that RM is not 
                        // installed on the machine so we don't need to worry about
                        // disabling the print screen.
                        _isRMMissing = true;
                    }
 
                    // Result should be S_OK for successful value assignment.
                    Trace.SafeWrite(
                        Trace.Presentation,
                        "CommandEnforcer: DisablePrintScreen change to: {0} hresult: {1}",
                        disable,
                        hresult);
                }
 
                // Update property with new value
                _printScreenDisabled = disable;
 
            }
        }
 
        /// <summary>
        /// The list of bindings to enforce.
        /// </summary>
        private List<PolicyBinding> _bindings;
 
        private DocumentApplicationDocumentViewer _docViewer;
        private const int _initialBindCount = 10; //We allocate space for 10 bindings initially.
        /// <summary>
        /// _printScreenDisabled is used to track the current PrintScreen state.  It begins with
        /// false since PrintScreen is initially enabled, and tracks changes from there.
        /// </summary>
        private bool _printScreenDisabled; // Trusted to be initialized to false
        /// <summary>
        /// _isRMMissing is used to track if a successful call has been made to native RM.
        /// This is used for performance, to limit the number of Exceptions being caught.
        /// </summary>
        private bool _isRMMissing; // Initially assumed that RM is installed and working.
 
        #region UnsafeNativeMethods (Private Class)
        /// <summary>
        /// This class is used to provide access to unmanaged code in order to disable
        /// the native PrintScreen support.
        /// </summary>
        private static class UnsafeNativeMethods
        {
            /// <summary>
            /// Called to toggle native PrintScreen support.  This will increment/decrement
            /// a reference counter, so it must be reset after use (for stability).
            /// </summary>
            /// <param name="register">A bool value to determine if PrintScreen should be
            /// disabled.  True means disabled, false re-enables.</param>
            /// <returns>S_OK on success, an error code otherwise.</returns>
            [DllImport("msdrm.dll", SetLastError = false, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            public static extern int DRMRegisterContent(bool register);
 
            /// <summary>
            /// Non-success value.
            /// </summary>
            //  This value can be anything other than S_OK
            internal const int FAIL = -1;
        }
        #endregion UnsafeNativeMethods
    }
}