File: winforms\Managed\System\WinForms\OpenFileDialog.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="OpenFileDialog.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms
{
 
    using System.Diagnostics;
 
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Drawing;
    using CodeAccessPermission = System.Security.CodeAccessPermission;
    using System.Security.Permissions;
    using System.IO;
    using System.ComponentModel;
    using Microsoft.Win32;
    using System.Runtime.Versioning;
 
    /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Represents a common dialog box
    ///       that displays the control that allows the user to open a file. This class
    ///       cannot be inherited.
    ///    </para>
    /// </devdoc>
    [SRDescription(SR.DescriptionOpenFileDialog)]
    public sealed class OpenFileDialog : FileDialog
    {
 
        /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog.CheckFileExists"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the dialog box displays a
        ///       warning if the user specifies a file name that does not exist.
        ///    </para>
        /// </devdoc>
        [
        DefaultValue(true),
        SRDescription(SR.OFDcheckFileExistsDescr)
        ]
        public override bool CheckFileExists
        {
            get
            {
                return base.CheckFileExists;
            }
            set
            {
                base.CheckFileExists = value;
            }
        }
 
        /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog.Multiselect"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value
        ///       indicating whether the dialog box allows multiple files to be selected.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(false),
        SRDescription(SR.OFDmultiSelectDescr)
        ]
        public bool Multiselect
        {
            get
            {
                return GetOption(NativeMethods.OFN_ALLOWMULTISELECT);
            }
            set
            {
                SetOption(NativeMethods.OFN_ALLOWMULTISELECT, value);
            }
        }
 
        /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog.ReadOnlyChecked"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether
        ///       the read-only check box is selected.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(false),
        SRDescription(SR.OFDreadOnlyCheckedDescr)
        ]
        public bool ReadOnlyChecked
        {
            get
            {
                return GetOption(NativeMethods.OFN_READONLY);
            }
            set
            {
                SetOption(NativeMethods.OFN_READONLY, value);
            }
        }
 
        /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog.ShowReadOnly"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the dialog contains a read-only check box.
        ///    </para>
        /// </devdoc>
        [
        SRCategory(SR.CatBehavior),
        DefaultValue(false),
        SRDescription(SR.OFDshowReadOnlyDescr)
        ]
        public bool ShowReadOnly
        {
            get
            {
                return !GetOption(NativeMethods.OFN_HIDEREADONLY);
            }
            set
            {
                SetOption(NativeMethods.OFN_HIDEREADONLY, !value);
            }
        }
 
        /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog.OpenFile"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Opens the file selected by the user with read-only permission.  The file
        ///       attempted is specified by the <see cref='System.Windows.Forms.FileDialog.FileName'/> property.
        ///    </para>
        /// </devdoc>        
        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
        /// SECREVIEW: ReviewImperativeSecurity
        ///   vulnerability to watch out for: A method uses imperative security and might be constructing the permission using state information or return values that can change while the demand is active.
        ///   reason for exclude: filename is snapped directly at the beginning of the function.
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public Stream OpenFile()
        {
            Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogOpenFile Demanded");
            IntSecurity.FileDialogOpenFile.Demand();
 
            string filename = FileNamesInternal[0];
 
            if (filename == null || (filename.Length == 0))
                throw new ArgumentNullException("FileName");
 
            Stream s = null;
 
            // SECREVIEW : We demanded the FileDialog permission above, so it is safe
            //           : to assert this here. Since the user picked the file, it
            //           : is OK to give them readonly access to the stream.
            //
            new FileIOPermission(FileIOPermissionAccess.Read, IntSecurity.UnsafeGetFullPath(filename)).Assert();
            try
            {
                s = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
            return s;
        }
 
        /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog.Reset"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Resets all properties to their default values.
        ///    </para>
        /// </devdoc>
        public override void Reset()
        {
            base.Reset();
            SetOption(NativeMethods.OFN_FILEMUSTEXIST, true);
        }
 
        internal override void EnsureFileDialogPermission()
        {
            Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogOpenFile Demanded in OpenFileDialog.RunFileDialog");
            IntSecurity.FileDialogOpenFile.Demand();
        }
 
        /// <include file='doc\OpenFileDialog.uex' path='docs/doc[@for="OpenFileDialog.RunFileDialog"]/*' />
        /// <devdoc>
        ///     Displays a file open dialog.
        /// </devdoc>
        /// <internalonly/>
        internal override bool RunFileDialog(NativeMethods.OPENFILENAME_I ofn)
        {
            //We have already done the demand in EnsureFileDialogPermission but it doesn't hurt to do it again
            Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileDialogOpenFile Demanded in OpenFileDialog.RunFileDialog");
            IntSecurity.FileDialogOpenFile.Demand();
 
            bool result = UnsafeNativeMethods.GetOpenFileName(ofn);
            if (!result)
            {
                // Something may have gone wrong - check for error condition
                //
                int errorCode = SafeNativeMethods.CommDlgExtendedError();
                switch (errorCode)
                {
                    case NativeMethods.FNERR_INVALIDFILENAME:
                        throw new InvalidOperationException(SR.GetString(SR.FileDialogInvalidFileName, FileName));
 
                    case NativeMethods.FNERR_SUBCLASSFAILURE:
                        throw new InvalidOperationException(SR.GetString(SR.FileDialogSubLassFailure));
 
                    case NativeMethods.FNERR_BUFFERTOOSMALL:
                        throw new InvalidOperationException(SR.GetString(SR.FileDialogBufferTooSmall));
                }
            }
            return result;
        }
 
        internal override string[] ProcessVistaFiles(FileDialogNative.IFileDialog dialog)
        {
            FileDialogNative.IFileOpenDialog openDialog = (FileDialogNative.IFileOpenDialog)dialog;
            if (Multiselect)
            {
                FileDialogNative.IShellItemArray results;
                openDialog.GetResults(out results);
                uint count;
                results.GetCount(out count);
                string[] files = new string[count];
                for (uint i = 0; i < count; ++i)
                { 
                    FileDialogNative.IShellItem item;
                    results.GetItemAt(i, out item);
                    files[unchecked((int)i)] = GetFilePathFromShellItem(item);
                }
                return files;
            }
            else
            { 
                FileDialogNative.IShellItem item;
                openDialog.GetResult(out item);
                return new string[] { GetFilePathFromShellItem(item) };
            }
        }
 
        internal override FileDialogNative.IFileDialog CreateVistaDialog()
        {
            return new FileDialogNative.NativeFileOpenDialog();
        }
 
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
            SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")
        ]
        public string SafeFileName
        {
            get
            {
                new FileIOPermission(PermissionState.Unrestricted).Assert();
                string fullPath = FileName;
                CodeAccessPermission.RevertAssert();
                if (string.IsNullOrEmpty(fullPath))
                {
                    return "";
                }
 
                string safePath = RemoveSensitivePathInformation(fullPath);
                return safePath;
            }
        }
 
        private static string RemoveSensitivePathInformation(string fullPath)
        {
            return System.IO.Path.GetFileName(fullPath);
        }
 
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
            SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays"),
            SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")
        ]
        public string[] SafeFileNames
        {
            get
            {
                new FileIOPermission(PermissionState.Unrestricted).Assert();
                string[] fullPaths = FileNames;
                CodeAccessPermission.RevertAssert();
                if (null == fullPaths || 0 == fullPaths.Length)
                { return new string[0]; }
                string[] safePaths = new string[fullPaths.Length];
                for (int i = 0; i < safePaths.Length; ++i)
                {
                    safePaths[i] = RemoveSensitivePathInformation(fullPaths[i]);
                }
                return safePaths;
            }
        }
 
        internal override bool SettingsSupportVistaDialog
        { 
            get
            {
                return base.SettingsSupportVistaDialog && !this.ShowReadOnly;
            }
        }
    }
}