File: MS\Internal\Documents\Application\DocumentProperties.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationUI\PresentationUI_njlx241o_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: This is a wrapper for DocumentPropertiesDialog, which caches the values which
//              are displayed in the Dialog, and controls security access.
 
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.IO.Packaging;                  // For Package
using System.Security;                      // For CriticalData
using System.Windows.TrustUI;               // For string resources
using System.Windows.Xps.Packaging;         // For XpsDocument
 
namespace MS.Internal.Documents.Application
{
    /// <summary>
    /// Singleton wrapper class for the DocumentPropertiesDialog.
    /// </summary>
    internal sealed class DocumentProperties
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
        #region Constructors
 
        /// <summary>
        /// Constructs the DocumentProperties class.
        /// </summary>
        /// <param name="uri"></param>
        private DocumentProperties(Uri uri)
        {
            ArgumentNullException.ThrowIfNull(uri);
 
            _uri = uri;
        }
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
        #region Internal Properties
        /// <summary>
        /// Current package CoreProperties
        /// </summary>
        internal PackageProperties CoreProperties
        {
            get
            {
                // multiple returns are bad, however allocating another local
                // simply to return is worse
                // also we can not cache this decision because the values
                // may change when RM is on / off
                if (_rmProperties != null)
                {
                    return _rmProperties;
                }
                else
                {
                    return _xpsProperties;
                }
            }
        }
        /// <summary>
        /// Gets the current singleton instance of DocumentProperties.
        /// </summary>
        internal static DocumentProperties Current
        {
            get
            {
                return _current;
            }
        }
        /// <summary>
        /// Image representing an XPS Document
        /// </summary>
        internal Image Image
        {
            get
            {
                return _image;
            }
            set
            {
                _image = value;
            }
        }
        /// <summary>
        /// Filename of the package.
        /// </summary>
        internal string Filename
        {
            get
            {
                if (_filename is null && _uri is not null)
                {
                    _filename = Path.GetFileName(_uri.LocalPath);
                }
 
                return _filename;
            }
        }
        /// <summary>
        /// Size of the package.
        /// </summary>
        internal long FileSize
        {
            get
            {
                long fileSize = 0;
 
                if (IsFileInfoValid)
                {
                    fileSize = _fileInfo.Length;
                }
 
                return fileSize;
            }
        }
        /// <summary>
        /// Date package was created.
        /// </summary>
        internal DateTime? FileCreated
        {
            get
            {
                DateTime? fileCreated = null;
 
                if (IsFileInfoValid)
                {
                    fileCreated = _fileInfo.CreationTime;
                }
 
                return fileCreated;
            }
        }
        /// <summary>
        /// Date package was last modified.
        /// </summary>
        internal DateTime? FileModified
        {
            get
            {
                DateTime? fileModified = null;
 
                if (IsFileInfoValid)
                {
                    fileModified = _fileInfo.LastWriteTime;
                }
 
                return fileModified;
            }
        }
        /// <summary>
        /// Date package was last accessed.
        /// </summary>
        internal DateTime? FileAccessed
        {
            get
            {
                DateTime? fileAccessed = null;
 
                if (IsFileInfoValid)
                {
                    fileAccessed = _fileInfo.LastAccessTime;
                }
 
                return fileAccessed;
            }
        }
 
        #endregion Internal Properties
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
        #region Internal Methods
 
        /// <summary>
        /// This method copies members from one PackageProperties object to another.
        /// </summary>
        /// <remarks>
        /// This design is very fragile as if new properties are added by PackageProperties
        /// we will quietly lose data.  An improved implementation would be to have
        /// PackageProperties implement this behavior or have a KeyValue pair exposed
        /// that we would iterate through and copy.
        /// </remarks>
        internal static void Copy(PackageProperties source, PackageProperties target)
        {
            target.Category = source.Category;
            target.ContentStatus = source.ContentStatus;
            target.ContentType = source.ContentType;
            target.Created = source.Created;
            target.Creator = source.Creator;
            target.Description = source.Description;
            target.Identifier = source.Identifier;
            target.Keywords = source.Keywords;
            target.Language = source.Language;
            target.LastModifiedBy = source.LastModifiedBy;
            target.LastPrinted = source.LastPrinted;
            target.Modified = source.Modified;
            target.Revision = source.Revision;
            target.Subject = source.Subject;
            target.Title = source.Title;
            target.Version = source.Version;
        }        
 
        /// <summary>
        /// Constructs and Initializes the static 'Current' DocumentProperties object which
        /// is the singleton instance.
        /// </summary>
        /// <param name="uri">The URI for the package.</param>
        internal static void InitializeCurrentDocumentProperties(Uri uri)
        {
            // Ensure that DocumentProperties has not been constructed yet, and initialize a
            // new instance.
            System.Diagnostics.Debug.Assert(
                Current == null,
                "DocumentProperties initialized twice.");
 
            if (Current == null)
            {
                _current = new DocumentProperties(uri);
            }
        }
 
        internal void SetXpsProperties(PackageProperties properties)
        {
            _xpsProperties = properties;
        }
 
        internal void SetRightsManagedProperties(PackageProperties properties)
        {
            _rmProperties = properties;
        }
 
        /// <summary>
        /// Confirms whether the XPS properties exactly match the RM properties
        /// for purposes of determining whether the OPC properties have been changed
        /// in violation of signing policy.        
        /// </summary>
        /// <returns>
        /// This will return true if the properties match
        /// or there are no RM properties in the package,
        /// otherwise it will return false.
        /// </returns>
        internal bool VerifyPropertiesUnchanged()
        {
            if (_xpsProperties == null )
            {
                return false;
            }
 
            if (_rmProperties == null)
            {
                return true;
            }
                       
            return
               (String.Equals(_xpsProperties.Category, _rmProperties.Category, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.ContentStatus, _rmProperties.ContentStatus, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.ContentType, _rmProperties.ContentType, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Creator, _rmProperties.Creator, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Description, _rmProperties.Description, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Identifier, _rmProperties.Identifier, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Keywords, _rmProperties.Keywords, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Language, _rmProperties.Language, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.LastModifiedBy, _rmProperties.LastModifiedBy, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Revision, _rmProperties.Revision, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Subject, _rmProperties.Subject, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Title, _rmProperties.Title, StringComparison.Ordinal) &&
                String.Equals(_xpsProperties.Version, _rmProperties.Version, StringComparison.Ordinal) &&
                _xpsProperties.Created == _rmProperties.Created &&
                _xpsProperties.LastPrinted == _rmProperties.LastPrinted &&
                _xpsProperties.Modified == _rmProperties.Modified);
                               
        }
 
        /// <summary>
        /// ShowDialog:  Displays the DocumentProperties dialog.
        /// </summary>
        internal void ShowDialog()
        {
            // Setup the dialog data once and cache it for future calls.
            if (!_isDataAcquired)
            {
                AcquireData();
                _isDataAcquired = true;
            }
 
            // Refresh file information before showing the dialog.
            if (_fileInfo != null)
            {
                // We intentionally `swallow exceptions here, since any failure
                // will make _fileInfo invalid as determined by the property
                // IsFileInfoValid. We also don't set _fileInfo to null so that
                // we can call Refresh again. This is useful since the file may
                // be inaccessible now but come back later.
                try
                {
                    _fileInfo.Refresh();
                }
                catch (ArgumentException)
                {
                }
                catch (IOException)
                {
                }
            }
 
            DocumentPropertiesDialog dialog = null;
            dialog = new DocumentPropertiesDialog();
            dialog.ShowDialog();
            if (dialog != null)
            {
                dialog.Dispose();
            }
        }
        #endregion Internal Methods
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        #region Private Methods
        /// <summary>
        /// AcquireData:  Setup the URI related data for the dialog.
        /// </summary>
        private void AcquireData()
        {
            // Ensure URI exists.
            if (_uri is null)
            {
                return;
            }
 
            // Determine if the URI represents a file
            if (_uri.IsFile)
            {
                // Determine the full path and assert for file permission
                string filePath = _uri.LocalPath;
 
                // Get the FileInfo for the current file
                FileInfo fileInfo = new FileInfo(filePath);
 
                // Check that FileInfo is valid, and save it.
                if (fileInfo.Exists)
                {
                    _fileInfo = fileInfo;
                }
            }
        }
        #endregion Private Methods
 
        //------------------------------------------------------
        //
        //  Private Properties
        //
        //------------------------------------------------------
        #region Private Properties
 
        /// <summary>
        /// True if the stored FileInfo is valid and can be used to determine
        /// file properties.
        /// </summary>
        /// <remarks>
        /// If the file has become completely inaccessible (i.e. the drive has
        /// been disconnected, the UNC path is unreachable, etc.), according to
        /// MSDN the Refresh call should throw an ArgumentException or
        /// IOException. In reality it doesn't appear to ever throw. Instead,
        /// if Refresh fails, any future calls to _fileInfo.Exists return
        /// false. Accessing any of the file information properties at that
        /// point throws an exception. As a result, if _fileInfo.Refresh()
        /// fails, we can leave _fileInfo as is and simply check
        /// _fileInfo.Exists before accessing it.
        /// </remarks>
        private bool IsFileInfoValid
        {
            get
            {
                return ((_fileInfo != null) && (_fileInfo.Exists));
            }
        }
 
        #endregion Private Properties
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //  These must be checked for null prior to use.  If available, use the CLR properties
        //  above before these.
        //
        //------------------------------------------------------
        #region Private Fields
        private static DocumentProperties       _current = null;
        private Uri _uri;
 
        /// <summary>
        /// The properties in the XpsPackage (OPC).
        /// </summary>
        private PackageProperties _xpsProperties = null;
 
        /// <summary>
        /// The properties for RightsManaged encrypted packages (OLE).
        /// </summary>
        private PackageProperties _rmProperties = null;
 
        private bool _isDataAcquired;
        private Image _image = null;
        private string _filename = null;
 
        private FileInfo _fileInfo;
 
        #endregion Private Fields
    }
}