projectsecuritychecker.cs :  » Installers-Generators » WiX » Microsoft » VisualStudio » Package » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Installers Generators » WiX 
WiX » Microsoft » VisualStudio » Package » projectsecuritychecker.cs
/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/

namespace Microsoft.VisualStudio.Package{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.VisualStudio.Build.ComInteropWrapper;
    using Microsoft.VisualStudio.Shell.Interop;
    using System.Diagnostics;
    using Microsoft.VisualStudio.Shell;
    using System.IO;
    using Microsoft.Win32;
    using System.Security;
    using System.Globalization;
    using System.Windows.Forms.Design;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Collections;
    using System.Security.Permissions;

    /// <summary>
    /// Does security validation of a project before loading the project
    /// </summary>
    public class ProjectSecurityChecker : IDisposable
    {
        #region constants
        /// <summary>
        /// The dangereous target property.
        /// </summary>
        internal const string DangerousTargetProperty = "LoadTimeSensitiveTargets";

        /// <summary>
        /// The dangereous properties property.
        /// </summary>
        internal const string DangerousPropertyProperty = "LoadTimeSensitiveProperties";

        /// <summary>
        /// The dangereous items property.
        /// </summary>
        internal const string DangerousItemsProperty = "LoadTimeSensitiveItems";

        /// <summary>
        /// The check item locations property.
        /// </summary>
        internal const string CheckItemLocationProperty = "LoadTimeCheckItemLocation";

        /// <summary>
        /// The dangereous list item separator.
        /// </summary>
        internal const string DangerousListSeparator = ";";

        /// <summary>
        /// The project directory property.
        /// </summary>
        internal const string ProjectDirectoryProperty = "MSBuildProjectDirectory";

        /// <summary>
        /// The default dangereous properties.
        /// </summary>
        internal const string DefaultDangerousProperties = "LoadTimeSensitiveTargets;LoadTimeSensitiveProperties;LoadTimeSensitiveItems;LoadTimeCheckItemLocation;";

        /// <summary>
        /// The default dangereous targets.
        /// </summary>
        internal const string DefaultDangerousTargets = "Compile;GetFrameworkPaths;AllProjectOutputGroups;AllProjectOutputGroupsDependencies;CopyRunEnvironmentFiles;ResolveComReferences;ResolveAssemblyReferences;ResolveNativeReferences;";

        /// <summary>
        /// The default dangereous items.
        /// </summary>
        internal const string DefaultDangerousItems = ";";

        /// <summary>
        /// Defined the safe imports subkey in the registry.
        /// </summary>
        internal const string SafeImportsSubkey = @"MSBuild\SafeImports";
        #endregion

        #region fields
        /// <summary>
        /// Defines an object that will be a mutex for this object for synchronizing thread calls.
        /// </summary>
        private static volatile object Mutex = new object();

        /// <summary>
        /// Flag determining if the object has been disposed.
        /// </summary>
        private bool isDisposed;

        /// <summary>
        /// The associated project shim for the project file
        /// </summary>
        private ProjectShim projectShim;
      
        /// <summary>
        /// The security check helper object used to call out to do necessary security checkings.
        /// </summary>
        private SecurityCheckHelper securityCheckHelper = new SecurityCheckHelper();

        /// <summary>
        /// The associated service provider.
        /// </summary>
        private IServiceProvider serviceProvider;
     
        #endregion

        #region properties
        /// <summary>
        /// The associated project shim for the project file
        /// </summary>
        /// <devremark>The project shim is made internal in order to be able to be passed to the user project.</devremark>
        internal protected ProjectShim ProjectShim
        {
            get { return this.projectShim; }
        }

        /// <summary>
        /// The security check helper that will be used to perform the necessary checkings.
        /// </summary>
        protected SecurityCheckHelper SecurityCheckHelper
        {
            get { return this.securityCheckHelper; }
        }
        
        /// <summary>
        /// The associated service provider.
        /// </summary>
        protected IServiceProvider ServiceProvider
        {
            get
            {
                return this.serviceProvider;
            }
        }
        #endregion

        #region ctors
        /// <summary>
        /// Overloaded Constructor 
        /// </summary>
        /// <param name="projectFilePath">path to the project file</param>
        /// <param name="serviceProvider">A service provider.</param>
        public ProjectSecurityChecker(IServiceProvider serviceProvider, string projectFilePath)
        {
            if (serviceProvider == null)
            {
                throw new ArgumentNullException("serviceProvider");
            }

            if (String.IsNullOrEmpty(projectFilePath))
            {
                throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "projectFilePath");
            }

            this.serviceProvider = serviceProvider;

            // Instantiate a new project shim that we are going to use for security checkings.
            EngineShim engine = new EngineShim();
            this.projectShim = engine.CreateNewProject();
            this.projectShim.Load(projectFilePath);
        }
        #endregion

        #region IDisposable Members

        /// <summary>
        /// The IDispose interface Dispose method for disposing the object determinastically.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        #region virtual methods
        /// <summary>
        /// Check if the project is safe at load/design time
        /// </summary>
        /// <param name="securityErrorMessage">If the project is not safe contains an error message, describing the reason.</param>
        /// <returns>true if the project is safe, false otherwise</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#",
            Justification="The error message needs to be an out parameter. We are following here the Try... method patterns.")]
        public virtual bool IsProjectSafeAtLoadTime(out string securityErrorMessage)
        {
            securityErrorMessage = String.Empty;

            StringBuilder securityMessageMaker = new StringBuilder() ;
            int counter = 0;
            string tempMessage;

            // STEP 1: Check direct imports.
            if (!this.IsProjectSafeWithImports(out tempMessage))
            {
                ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage);
                securityErrorMessage = tempMessage;
            }
           
            // STEP 2: Check dangerous properties
            if (!this.IsProjectSafeWithProperties(out tempMessage))
            {
                ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage);
                securityErrorMessage = tempMessage;
            }

            // STEP 3: Check dangerous targets
            if (!this.IsProjectSafeWithTargets(out tempMessage))
            {
                ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage);
                securityErrorMessage = tempMessage;
            }

            // STEP 4: Check dangerous items
            if (!this.IsProjectSafeWithItems(out tempMessage))
            {
                ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage);
                securityErrorMessage = tempMessage;
            }

            // STEP 5: Check UsingTask tasks
            if (!this.IsProjectSafeWithUsingTasks(out tempMessage))
            {
                ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage);
                securityErrorMessage = tempMessage;
            }
                      
            // STEP 6: Check for items defined within the LoadTimeCheckItemLocation, whether they are defined in safe locations
            if (!this.CheckItemsLocation(out tempMessage))
            {
                securityMessageMaker.AppendFormat(CultureInfo.CurrentCulture, "{0}: ", (++counter).ToString(CultureInfo.CurrentCulture));
                securityMessageMaker.AppendLine(tempMessage);
                securityErrorMessage = tempMessage;
            }

            if (counter > 1)
            {
                securityErrorMessage = securityMessageMaker.ToString();
            }

            return String.IsNullOrEmpty(securityErrorMessage);
        }

        /// <summary>
        /// Checks if the project is safe with imports. The project file is considered
        /// unsafe if it contains any imports not registered in the safe import regkey.
        /// </summary>
        /// <param name="securityErrorMessage">At return describes the reason why the projects is not considered safe.</param>
        /// <returns>true if the project is safe regarding imports.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#",
            Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")]
        protected virtual bool IsProjectSafeWithImports(out string securityErrorMessage)
        {
            securityErrorMessage = String.Empty;

            // Now get the directly imports and do the comparision.
            string[] directImports = this.securityCheckHelper.GetDirectlyImportedProjects(this.projectShim);
            if (directImports != null && directImports.Length > 0)
            {
                IList<string> safeImportList = ProjectSecurityChecker.GetSafeImportList();

                for (int i = 0; i < directImports.Length; i++)
                {
                    string fileToCheck = directImports[i];
                    if (!ProjectSecurityChecker.IsSafeImport(safeImportList, fileToCheck))
                    {
                        using (RegistryKey root = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration))
                        {
                            securityErrorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DetailsImport, CultureInfo.CurrentUICulture), Path.GetFileName(this.projectShim.FullFileName), fileToCheck, Path.Combine(root.Name, SafeImportsSubkey));
                        }

                        return false;
                    }
                }
            }

            return true;
        }

       

        /// <summary>
        /// Checks if the project is safe regarding properties.
        /// </summary>
        /// <param name="securityErrorMessage">At return describes the reason why the projects is not considered safe.</param>       
        /// <returns>true if the project has only safe properties.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#",
            Justification="The error message needs to be an out parameter. We are following here the Try... method patterns.")]
        protected virtual bool IsProjectSafeWithProperties(out string securityErrorMessage)
        {
            securityErrorMessage = String.Empty;

            // Now ask the security check heper for the safe properties.
            string reasonForFailure;
            bool isUserFile;
            bool isProjectSafe = this.securityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousPropertyProperty,
                                                                        ProjectSecurityChecker.DefaultDangerousProperties,
                                                                        this.projectShim,
                                                                        null,
                                                                        SecurityCheckPass.Properties,
                                                                        out reasonForFailure,
                                                                        out isUserFile);

            if (!isProjectSafe)
            {
                securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsProperty);
            }

            return isProjectSafe;
        }

        /// <summary>
        /// Checks if the project is safe regarding targets.
        /// </summary>
        /// <param name="securityErrorMessage">At return describes the reason why the projects is not considered safe.</param>       
        /// <returns>true if the project has only safe targets.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#",
            Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")]
        protected virtual bool IsProjectSafeWithTargets(out string securityErrorMessage)
        {
            securityErrorMessage = String.Empty;

            // Now ask the security check heper for the safe targets.
            string reasonForFailure;
            bool isUserFile;
            bool isProjectSafe = this.securityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousTargetProperty,
                                                                        ProjectSecurityChecker.DefaultDangerousTargets,
                                                                        this.projectShim,
                                                                        null,
                                                                        SecurityCheckPass.Targets,
                                                                        out reasonForFailure,
                                                                        out isUserFile);

            if (!isProjectSafe)
            {
                securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsTarget);
            }

            return isProjectSafe;
        }

        /// <summary>
        /// Checks if the project is safe regarding items.
        /// </summary>
        /// <param name="securityErrorMessage">At return describes the reason why the projects is not considered safe.</param>       
        /// <returns>true if the project has only safe items.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#",
            Justification="The error message needs to be an out parameter. We are following here the Try... method patterns.")]
        protected virtual bool IsProjectSafeWithItems(out string securityErrorMessage)
        {
            securityErrorMessage = String.Empty;

            // Now ask the security check heper for the safe items.
            string reasonForFailure;
            bool isUserFile;
            bool isProjectSafe = this.securityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousItemsProperty,
                                                                        ProjectSecurityChecker.DefaultDangerousItems,
                                                                        this.projectShim,
                                                                        null,
                                                                        SecurityCheckPass.Items,
                                                                        out reasonForFailure,
                                                                        out isUserFile);

            if (!isProjectSafe)
            {
                securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsItem);
            }

            return isProjectSafe;
        }

        /// <summary>
        /// Checks if the project is safe with using tasks.
        /// </summary>
        /// <param name="securityErrorMessage">At return describes the reason why the projects is not considered safe.</param>
        /// <returns>true if the project has no using tasks defined in the project file.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#",
            Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")]
        protected virtual bool IsProjectSafeWithUsingTasks(out string securityErrorMessage)
        {
            securityErrorMessage = String.Empty;

            string[] usingTasks = this.securityCheckHelper.GetNonImportedUsingTasks(this.projectShim);

            if (usingTasks != null && usingTasks.Length > 0)
            {
                securityErrorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DetailsUsingTask, CultureInfo.CurrentUICulture), Path.GetFileName(this.projectShim.FullFileName), usingTasks[0]);
                return false;
            }

            return true;
        }
    
        /// <summary>
        ///  If the project contains the LoadTimeCheckItemsWithinProjectCone property, the method verifies that all the items listed in there are within the project cone.
        ///  Also checks that the project is not in Program Files or Windows if the property was there.
        /// </summary>
        /// <param name="securityErrorMessage">At return describes the reason why the projects is not considered safe.</param>
        /// <returns>true if the project has no badly defined project items.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", 
            Justification="The error message needs to be an out parameter. We are following here the Try... method patterns.")]
        protected virtual bool CheckItemsLocation(out string securityErrorMessage)
        {
            securityErrorMessage = String.Empty;

            // Get the <LoadTimeCheckItemLocation> property from the project
            string itemLocationProperty = this.projectShim.GetEvaluatedProperty(ProjectSecurityChecker.CheckItemLocationProperty);

            if (String.IsNullOrEmpty(itemLocationProperty))
            {
                return true;
            }

            // Takes a semicolon separated list of entries, splits them and puts them into a list with values trimmed.
            string[] items = itemLocationProperty.Split(ProjectSecurityChecker.DangerousListSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            IList<string> itemsToCheck = new List<string>();
            foreach (string item in items)
            {
                itemsToCheck.Add(item.Trim());
            }

            // Now check the items for being defined in a safe location.
            string reasonForFailure;
            ItemSecurityChecker itemsSecurityChecker = new ItemSecurityChecker(this.serviceProvider, this.projectShim.FullFileName);
            if (!itemsSecurityChecker.CheckItemsSecurity(this.projectShim, itemsToCheck, out reasonForFailure))
            {
                securityErrorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DetailsItemLocation, CultureInfo.CurrentUICulture), Path.GetFileName(this.projectShim.FullFileName), reasonForFailure);
                return false;
            }

            return true;
        }


        /// <summary>
        /// The method that does the cleanup.
        /// </summary>
        /// <param name="disposing">true if called from IDispose.Dispose; false if called from Finalizer.</param>
        protected virtual void Dispose(bool disposing)
        {
            // Everybody can go here.
            if (!this.isDisposed)
            {
                // Synchronize calls to the Dispose simultaniously.
                lock (Mutex)
                {
                    if (disposing)
                    {
                        this.projectShim.ParentEngine.UnloadProject(this.projectShim);                        
                    }

                    this.isDisposed = true;
                }
            }
        }
        #endregion

        #region helper methods
        /// <summary>
        /// Gets a message string that has an associated format with a reason for failure.
        /// </summary>
        /// <param name="reasonForFailure"></param>
        /// <param name="resourceID"></param>
        /// <returns></returns>
        internal string GetMessageString(string reasonForFailure, string resourceID)
        {
            Debug.Assert(!String.IsNullOrEmpty(reasonForFailure), "The reason for failure should not be empty or null");
            Debug.Assert(!String.IsNullOrEmpty(resourceID), "The resource id string cannot be empty");

            return String.Format(CultureInfo.CurrentCulture, SR.GetString(resourceID, Path.GetFileName(this.projectShim.FullFileName), reasonForFailure));
        }

        /// <summary>
        /// Generates a format string that will be pushed to the More Detailed dialog.
        /// </summary>
        /// <param name="securityMessageMaker">The Stringbuilder object containing the formatted message.</param>
        /// <param name="counter">The 'issue' number.</param>
        /// <param name="securityErrorMessage">The message to format.</param>
        private static void FormatMessage(StringBuilder securityMessageMaker, int counter, string securityErrorMessage)
        {
            securityMessageMaker.AppendFormat(CultureInfo.CurrentCulture, "{0}: ", counter.ToString(CultureInfo.CurrentCulture));
            securityMessageMaker.AppendLine(securityErrorMessage);
            securityMessageMaker.Append(Environment.NewLine);
        }

        /// <summary>
        /// Returns a set of file info's describing the files in the SafeImports registry location.
        /// </summary>
        /// <returns>A set of FileInfo objects describing the files in the SafeImports location.</returns>
        private static IList<string> GetSafeImportList()
        {
            List<string> importsList = new List<string>();

            using (RegistryKey root = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration))
            {
                if (root != null)
                {
                    using (RegistryKey key = root.OpenSubKey(SafeImportsSubkey))
                    {
                        if (key != null)
                        {
                            foreach (string value in key.GetValueNames())
                            {
                                string keyValue = key.GetValue(value, String.Empty, RegistryValueOptions.None) as string;
                                // Make sure that the environment variables are expanded.
                                keyValue = System.Environment.ExpandEnvironmentVariables(keyValue);
                                Uri uri;
                                if (!String.IsNullOrEmpty(keyValue) && Uri.TryCreate(keyValue, UriKind.Absolute, out uri) && uri.IsAbsoluteUri)
                                {
                                    importsList.Add(keyValue);
                                }
                            }
                        }
                    }
                }
            }

            return importsList;
        }

        /// <summary>
        /// Checks if an import is a safe import.
        /// </summary>
        /// <param name="safeImportsList">A list of safe imports from teh registry.</param>
        /// <param name="fileToCheck">The file to check.</param>
        /// <returns>true if the file to check can be found in the safe import list</returns>
        private static bool IsSafeImport(IList<string> safeImportsList, string fileToCheck)
        {
            foreach (string safeImport in safeImportsList)
            {
                if (NativeMethods.IsSamePath(safeImport, fileToCheck))
                {
                    return true;
                }
            }

            return false;
        }       
        #endregion

        #region nested types
        /// <summary>
        /// Class for checking that the items defined in LoadTimeCheckItemLocation are being defined in safe locations.
        /// </summary>
        private class ItemSecurityChecker
        {
            #region fields
            /// <summary>
            /// The associated service provider.
            /// </summary>
            private IServiceProvider serviceProvider;
         
            /// <summary>
            /// The solutionFolder;
            /// </summary>
            private Uri solutionFolder;

            /// <summary>
            /// The project folder
            /// </summary>
            private Uri projectFolder;

            /// <summary>
            /// The set of special folders.
            /// </summary>
            private IList<Uri> specialFolders;
            #endregion

            #region ctors
            /// <summary>
            /// Overloaded Constructor 
            /// </summary>
            /// <param name="projectFilePath">path to the project file</param>
            /// <param name="serviceProvider">A service provider.</param>
            internal ItemSecurityChecker(IServiceProvider serviceProvider, string projectFullPath)
            {
                this.serviceProvider = serviceProvider;                

                // Initialize the project and solution folders.
                this.SetProjectFolder(projectFullPath);
                this.SetSolutionFolder();

                // Set the special folders. Maybe this should be a static.
                this.specialFolders = ItemSecurityChecker.SetSpecialFolders();
            }
            #endregion

            #region methods
            /// <summary>
            /// Checks whether a set of project items described by the LoadTimeCheckItemLocation are in a safe location.
            /// </summary>
            /// <param name="projectShim">The project shim containing the items to be checked.</param>
            /// <param name="itemsToCheck">The list of items to check if they are in the project cone.</param>
            /// <param name="reasonForFailure">The reason for failure if any of the files fails</param>
            /// <returns>true if all project items are in the project cone. Otherwise false.</returns>
            internal bool CheckItemsSecurity(ProjectShim projectShim, IList<string> itemsToCheck, out string reasonForFailure)
            {
                reasonForFailure = String.Empty;
                
                // If nothing to check assume that everything is ok.
                if (itemsToCheck == null)
                {
                    return true;
                }

                Debug.Assert(projectShim != null, "Cannot check the items if no project has been defined!");

                foreach (string itemName in itemsToCheck)
                {
                    BuildItemGroupShim group = projectShim.GetEvaluatedItemsByNameIgnoringCondition(itemName);
                    if (group != null)
                    {
                        IEnumerator enumerator = group.GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            BuildItemShim item = enumerator.Current as BuildItemShim;

                            string finalItem = item.FinalItemSpec;

                            if (!String.IsNullOrEmpty(finalItem))
                            {
                                // Perform the actual check - start with normalizing the path.  Relative paths
                                // should be treated as relative to the project file.
                                string fullPath = this.GetFullPath(finalItem);

                                // If the fullpath of the item is suspiciously short do not check it.
                                if (fullPath.Length >= 3)
                                {
                                    Uri uri = null;

                                    // If we cannot create a uri from the item path return with the error
                                    if (!Uri.TryCreate(fullPath, UriKind.Absolute, out uri))
                                    {
                                        reasonForFailure = fullPath;
                                        return false;
                                    }
                                    
                                    // Check if the item points to a network share
                                    if (uri.IsUnc)
                                    {
                                        reasonForFailure = fullPath;
                                        return false;
                                    }

                                    // Check if the item is located in a drive root directory
                                    if (uri.Segments.Length == 3 && uri.Segments[1] == ":" && uri.Segments[2][0] == Path.DirectorySeparatorChar)
                                    {
                                        reasonForFailure = fullPath;
                                        return false;
                                    }

                                    //Check if the item is not in a special folder.
                                    foreach (Uri specialFolder in this.specialFolders)
                                    {
                                        if (ItemSecurityChecker.IsItemInCone(uri, specialFolder))
                                        {
                                            reasonForFailure = fullPath;
                                            return false;
                                        }
                                    }
                                }
                                else
                                {
                                    reasonForFailure = fullPath;
                                    return false;
                                }
                            }
                        }
                    }
                }

                return true;
            }
        

            /// <summary>
            /// Gets the list of special directories. This method should be optimized if called more then once.
            /// </summary>
            /// <returns>The list of special directories</returns>
            private static IList<Uri> SetSpecialFolders()
            {
                string[] specialFolderArray = new string[5]
                {
                   Environment.GetFolderPath(Environment.SpecialFolder.System),
                   Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
                   Environment.GetFolderPath(Environment.SpecialFolder.Startup),
                   ItemSecurityChecker.GetSpecialDirectoryFromNative(NativeMethods.ExtendedSpecialFolder.Windows),
                   ItemSecurityChecker.GetSpecialDirectoryFromNative(NativeMethods.ExtendedSpecialFolder.CommonStartup)
                };

                List<Uri> specialFolders = new List<Uri>(5);
                
                // Add trailing backslash to the folders.
                foreach (string specialFolder in specialFolderArray)
                {
                    string tempFolder = specialFolder;
                    if (!tempFolder.EndsWith("\\", StringComparison.Ordinal))
                    {
                        tempFolder += "\\";
                    }

                    specialFolders.Add(new Uri(tempFolder));
                }
                
                return specialFolders;
            }

            /// <summary>
            /// Some special folders are not supported by System.Environment.GetFolderPath. Get these special folders using p/invoke.
            /// </summary>
            /// <param name="specialFolder">The type of special folder to retrieve.</param>
            /// <returns>The folder path</returns>
            private static string GetSpecialDirectoryFromNative(NativeMethods.ExtendedSpecialFolder extendedSpecialFolder)
            {
                string specialFolder = null;
                IntPtr buffer = IntPtr.Zero;

                // Demand Unmanaged code permission. It should be normal to demand UnmanagedCodePermission from an assembly integrating into VS.
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
                try
                {                       
                    buffer = Marshal.AllocHGlobal((NativeMethods.MAX_PATH + 1) * 2);
                    IntPtr[] pathIdentifier = new IntPtr[1];

                    if (ErrorHandler.Succeeded(UnsafeNativeMethods.SHGetSpecialFolderLocation(IntPtr.Zero, (int)extendedSpecialFolder, pathIdentifier)) && UnsafeNativeMethods.SHGetPathFromIDList(pathIdentifier[0], buffer))
                    {
                        specialFolder = Marshal.PtrToStringAuto(buffer);
                    }
                }
                finally
                {
                    if (buffer != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(buffer);
                    }
                }
                         

                return specialFolder;
            }
           

            /// <summary>
            /// Checks if the itemToCheck is in the cone of the baseUri.
            /// </summary>
            /// <param name="itemToCheck">The item to check</param>
            /// <param name="baseUri">The base to the item. This should define a folder.</param>
            /// <returns>true if the item to check is in the cone of the baseUri.</returns>
            private static bool IsItemInCone(Uri itemToCheck, Uri baseUri)
            {
                Debug.Assert(itemToCheck != null && baseUri != null, "Cannot check for items since the input is wrong");
                Debug.Assert(!NativeMethods.IsSamePath(Path.GetDirectoryName(baseUri.LocalPath), baseUri.LocalPath), "The " + baseUri.LocalPath + " is not a folder!");

                return (itemToCheck.IsFile && baseUri.IsFile &&
                    String.Compare(itemToCheck.LocalPath, 0, baseUri.LocalPath, 0, baseUri.LocalPath.Length, StringComparison.OrdinalIgnoreCase) == 0);
            }

            /// <summary>
            /// Sets the solution folder.
            /// </summary>
            private void SetSolutionFolder()
            {
                if (this.solutionFolder != null)
                {
                    return;
                }

                IVsSolution solution = this.serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution;
                Debug.Assert(solution != null, "Could not retrieve the solution service from the global service provider");

                string solutionDirectory, solutionFile, userOptionsFile;

                // We do not want to throw. If we cannot set the solution related constants we set them to empty string.
                ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptionsFile));

                if (String.IsNullOrEmpty(solutionDirectory))
                {
                    return;
                }

                // Make sure the solution dir ends with a backslash
                if (solutionDirectory[solutionDirectory.Length - 1] != Path.DirectorySeparatorChar)
                {
                    solutionDirectory += Path.DirectorySeparatorChar;
                }

                Uri.TryCreate(solutionDirectory, UriKind.Absolute, out this.solutionFolder);

                Debug.Assert(this.solutionFolder != null, "Could not create the Uri for the solution folder");
            }

            /// <summary>
            /// Sets the project folder.
            /// </summary>
            /// <param name="projectFullPath">The path to the project</param>
            private void SetProjectFolder(string projectFullPath)
            {
                if (this.projectFolder != null)
                {
                    return;
                }

                string tempProjectFolder = Path.GetDirectoryName(projectFullPath);

                // Make sure the project dir ends with a backslash
                if (!tempProjectFolder.EndsWith("\\", StringComparison.Ordinal) && !tempProjectFolder.EndsWith("/", StringComparison.Ordinal))
                {
                    tempProjectFolder += "\\";
                }

                Uri.TryCreate(tempProjectFolder, UriKind.Absolute, out this.projectFolder);

                Debug.Assert(this.projectFolder != null, "Could not create the Uri for the project folder");
            }

            /// <summary>
            /// Gets the fullpath of an item. 
            /// Relative pathes are treated as relative to the project file.
            /// </summary>
            /// <param name="item">The item.</param>
            /// <returns>The ful path of the item.</returns>
            private string GetFullPath(string item)
            {
                Url url;
                if (Path.IsPathRooted(item))
                {
                    // Use absolute path
                    url = new Microsoft.VisualStudio.Shell.Url(item);
                }
                else
                {
                    // Path is relative, so make it relative to project path
                    url = new Url(new Url(this.projectFolder.LocalPath), item);
                }

                return url.AbsoluteUrl;
            }
            #endregion
        }
        #endregion
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.