/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectSecurityChecker.cs
C# | 852 lines | 497 code | 127 blank | 228 comment | 71 complexity | 1e7ebc0ac417432e8a001b95bbba5af9 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
- /***************************************************************************
-
- 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.
-
- ***************************************************************************/
- extern alias Shell10;
-
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Security.Permissions;
- using System.Text;
- using Microsoft.VisualStudio;
- using Microsoft.VisualStudio.Shell;
- using Microsoft.VisualStudio.Shell.Interop;
- using Microsoft.Win32;
-
- namespace Microsoft.VisualStudio.Project
- {
- using Url = Shell10.Microsoft.VisualStudio.Shell.Url;
-
- /// <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 = Shell10.Microsoft.VisualStudio.Shell.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 = Shell10.Microsoft.VisualStudio.Shell.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 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
- }
- }