The Labs \ Source Viewer \ SSCLI \ System.Configuration \ ClientConfigPaths

  1. //------------------------------------------------------------------------------
  2. // <copyright file="ClientConfigPaths.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Configuration
  16. {
  17.     using System;
  18.     using System.Collections;
  19.     using System.IO;
  20.     using System.Reflection;
  21.     using System.Runtime.CompilerServices;
  22.     using System.Runtime.InteropServices;
  23.     using System.Runtime.Serialization.Formatters.Binary;
  24.     using System.Security;
  25.     using System.Security.Cryptography;
  26.     using System.Security.Policy;
  27.     using System.Security.Permissions;
  28.     using System.Text;
  29.     using System.Globalization;
  30.     using Microsoft.Win32;
  31.    
  32.     class ClientConfigPaths
  33.     {
  34.         internal const string UserConfigFilename = "user.config";
  35.        
  36.         const string ClickOnceDataDirectory = "DataDirectory";
  37.         const string ConfigExtension = ".config";
  38.         const int MAX_PATH = 260;
  39.         const int MAX_LENGTH_TO_USE = 25;
  40.         const string FILE_URI_LOCAL = "file:///";
  41.         const string FILE_URI_UNC = "file://";
  42.         const string FILE_URI = "file:";
  43.         const string HTTP_URI = "http://";
  44.         const string StrongNameDesc = "StrongName";
  45.         const string UrlDesc = "Url";
  46.         const string PathDesc = "Path";
  47.        
  48.         static char[] s_Base32Char = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
  49.         'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
  50.         'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
  51.         '4', '5'};
  52.        
  53.         static volatile ClientConfigPaths s_current;
  54.         static volatile bool s_currentIncludesUserConfig;
  55.         static SecurityPermission s_serializationPerm;
  56.         static SecurityPermission s_controlEvidencePerm;
  57.        
  58.         bool _hasEntryAssembly;
  59.         bool _includesUserConfig;
  60.         string _applicationUri;
  61.         string _applicationConfigUri;
  62.         string _roamingConfigDirectory;
  63.         string _roamingConfigFilename;
  64.         string _localConfigDirectory;
  65.         string _localConfigFilename;
  66.         string _companyName;
  67.         string _productName;
  68.         string _productVersion;
  69.        
  70.        
  71.         [FileIOPermission(SecurityAction.Assert, AllFiles = FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read)]
  72.         [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
  73.         private ClientConfigPaths(string exePath, bool includeUserConfig)
  74.         {
  75.            
  76.             _includesUserConfig = includeUserConfig;
  77.            
  78.             Assembly exeAssembly = null;
  79.             string applicationUri = null;
  80.             string applicationFilename = null;
  81.            
  82.             // get the assembly and applicationUri for the file
  83.             if (exePath == null) {
  84.                 // First check if a configuration file has been set for this app domain. If so, we will use that.
  85.                 // The CLR would already have normalized this, so no further processing necessary.
  86.                 AppDomain domain = AppDomain.CurrentDomain;
  87.                 AppDomainSetup setup = domain.SetupInformation;
  88.                 _applicationConfigUri = setup.ConfigurationFile;
  89.                
  90.                 // Now figure out the application path.
  91.                 exeAssembly = Assembly.GetEntryAssembly();
  92.                 if (exeAssembly != null) {
  93.                     _hasEntryAssembly = true;
  94.                     applicationUri = exeAssembly.CodeBase;
  95.                    
  96.                     bool isFile = false;
  97.                    
  98.                     // If it is a local file URI, convert it to its filename, without invoking Uri class.
  99.                     // example: "file:///C:/WINNT/Microsoft.NET/Framework/v2.0.x86fre/csc.exe"
  100.                     if (StringUtil.StartsWithIgnoreCase(applicationUri, FILE_URI_LOCAL)) {
  101.                         isFile = true;
  102.                         applicationUri = applicationUri.Substring(FILE_URI_LOCAL.Length);
  103.                     }
  104.                     // If it is a UNC file URI, convert it to its filename, without invoking Uri class.
  105.                     // example: "file://server/share/csc.exe"
  106.                     else if (StringUtil.StartsWithIgnoreCase(applicationUri, FILE_URI_UNC)) {
  107.                         isFile = true;
  108.                         applicationUri = applicationUri.Substring(FILE_URI.Length);
  109.                     }
  110.                    
  111.                     if (isFile) {
  112.                         applicationUri = applicationUri.Replace('/', '\\');
  113.                         applicationFilename = applicationUri;
  114.                     }
  115.                     else {
  116.                         applicationUri = exeAssembly.EscapedCodeBase;
  117.                     }
  118.                 }
  119.                 else {
  120.                     StringBuilder sb = new StringBuilder(MAX_PATH);
  121.                     UnsafeNativeMethods.GetModuleFileName(new HandleRef(null, IntPtr.Zero), sb, sb.Capacity);
  122.                     applicationUri = Path.GetFullPath(sb.ToString());
  123.                     applicationFilename = applicationUri;
  124.                 }
  125.             }
  126.             else {
  127.                 applicationUri = Path.GetFullPath(exePath);
  128.                 if (!FileUtil.FileExists(applicationUri, false))
  129.                     throw ExceptionUtil.ParameterInvalid("exePath");
  130.                
  131.                 applicationFilename = applicationUri;
  132.             }
  133.            
  134.             // Fallback if we haven't set the app config file path yet.
  135.             if (_applicationConfigUri == null) {
  136.                 _applicationConfigUri = applicationUri + ConfigExtension;
  137.             }
  138.            
  139.             // Set application path
  140.             _applicationUri = applicationUri;
  141.            
  142.             // In the case when exePath was explicitly supplied, we will not be able to
  143.             // construct user.config paths, so quit here.
  144.             if (exePath != null) {
  145.                 return;
  146.             }
  147.            
  148.             // Skip expensive initialization of user config file information if requested.
  149.             if (!_includesUserConfig) {
  150.                 return;
  151.             }
  152.            
  153.             bool isHttp = StringUtil.StartsWithIgnoreCase(_applicationConfigUri, HTTP_URI);
  154.            
  155.             SetNamesAndVersion(applicationFilename, exeAssembly, isHttp);
  156.            
  157.             // Check if this is a clickonce deployed application. If so, point the user config
  158.             // files to the clickonce data directory.
  159.             if (this.IsClickOnceDeployed(AppDomain.CurrentDomain)) {
  160.                 string dataPath = AppDomain.CurrentDomain.GetData(ClickOnceDataDirectory) as string;
  161.                 string versionSuffix = Validate(_productVersion, false);
  162.                
  163.                 // NOTE: No roaming config for clickonce - not supported.
  164.                 if (Path.IsPathRooted(dataPath)) {
  165.                     _localConfigDirectory = CombineIfValid(dataPath, versionSuffix);
  166.                     _localConfigFilename = CombineIfValid(_localConfigDirectory, UserConfigFilename);
  167.                 }
  168.                
  169.             }
  170.             else if (!isHttp) {
  171.                 // If we get the config from http, we do not have a roaming or local config directory,
  172.                 // as it cannot be edited by the app in those cases because it does not have Full Trust.
  173.                
  174.                 // suffix for user config paths
  175.                
  176.                 string part1 = Validate(_companyName, true);
  177.                
  178.                 string validAppDomainName = Validate(AppDomain.CurrentDomain.FriendlyName, true);
  179.                 string applicationUriLower = !String.IsNullOrEmpty(_applicationUri) ? _applicationUri.ToLower(CultureInfo.InvariantCulture) : null;
  180.                 string namePrefix = !String.IsNullOrEmpty(validAppDomainName) ? validAppDomainName : Validate(_productName, true);
  181.                 string hashSuffix = string.Empty;
  182.                
  183.                 string part2 = (!String.IsNullOrEmpty(namePrefix) && !String.IsNullOrEmpty(hashSuffix)) ? namePrefix + hashSuffix : null;
  184.                
  185.                 string part3 = Validate(_productVersion, false);
  186.                
  187.                 string dirSuffix = CombineIfValid(CombineIfValid(part1, part2), part3);
  188.             }
  189.         }
  190.        
  191.         static internal ClientConfigPaths GetPaths(string exePath, bool includeUserConfig)
  192.         {
  193.             ClientConfigPaths result = null;
  194.            
  195.             if (exePath == null) {
  196.                 if (s_current == null || (includeUserConfig && !s_currentIncludesUserConfig)) {
  197.                     s_current = new ClientConfigPaths(null, includeUserConfig);
  198.                     s_currentIncludesUserConfig = includeUserConfig;
  199.                 }
  200.                
  201.                 result = s_current;
  202.             }
  203.             else {
  204.                 result = new ClientConfigPaths(exePath, includeUserConfig);
  205.             }
  206.            
  207.             return result;
  208.         }
  209.        
  210.         static internal void RefreshCurrent()
  211.         {
  212.             s_currentIncludesUserConfig = false;
  213.             s_current = null;
  214.         }
  215.        
  216.         static internal ClientConfigPaths Current {
  217.             get { return GetPaths(null, true); }
  218.         }
  219.        
  220.         internal bool HasEntryAssembly {
  221.             get { return _hasEntryAssembly; }
  222.         }
  223.        
  224.         internal string ApplicationUri {
  225.             get { return _applicationUri; }
  226.         }
  227.        
  228.         internal string ApplicationConfigUri {
  229.             get { return _applicationConfigUri; }
  230.         }
  231.        
  232.         internal string RoamingConfigFilename {
  233.             get { return _roamingConfigFilename; }
  234.         }
  235.        
  236.         internal string RoamingConfigDirectory {
  237.             get { return _roamingConfigDirectory; }
  238.         }
  239.        
  240.         internal bool HasRoamingConfig {
  241. // Assume we have roaming config if we haven't loaded user config file information.
  242.             get { return RoamingConfigFilename != null || !_includesUserConfig; }
  243.         }
  244.        
  245.         internal string LocalConfigFilename {
  246.             get { return _localConfigFilename; }
  247.         }
  248.        
  249.         internal string LocalConfigDirectory {
  250.             get { return _localConfigDirectory; }
  251.         }
  252.        
  253.         internal bool HasLocalConfig {
  254. // Assume we have roaming config if we haven't loaded user config file information.
  255.             get { return LocalConfigFilename != null || !_includesUserConfig; }
  256.         }
  257.        
  258.         internal string ProductName {
  259.             get { return _productName; }
  260.         }
  261.        
  262.         internal string ProductVersion {
  263.             get { return _productVersion; }
  264.         }
  265.        
  266.         private static SecurityPermission ControlEvidencePermission {
  267.             get {
  268.                 if (s_controlEvidencePerm == null) {
  269.                     s_controlEvidencePerm = new SecurityPermission(SecurityPermissionFlag.ControlEvidence);
  270.                 }
  271.                 return s_controlEvidencePerm;
  272.             }
  273.         }
  274.        
  275.         private static SecurityPermission SerializationFormatterPermission {
  276.             get {
  277.                 if (s_serializationPerm == null) {
  278.                     s_serializationPerm = new SecurityPermission(SecurityPermissionFlag.SerializationFormatter);
  279.                 }
  280.                 return s_serializationPerm;
  281.             }
  282.         }
  283.        
  284.         // Combines path2 with path1 if possible, else returns null.
  285.         private string CombineIfValid(string path1, string path2)
  286.         {
  287.             string returnPath = null;
  288.            
  289.             if (path1 != null && path2 != null) {
  290.                 try {
  291.                     string combinedPath = Path.Combine(path1, path2);
  292.                     if (combinedPath.Length < MAX_PATH) {
  293.                         returnPath = combinedPath;
  294.                     }
  295.                 }
  296.                 catch {
  297.                 }
  298.             }
  299.            
  300.             return returnPath;
  301.         }
  302.        
  303.        
  304.         // Mostly borrowed from IsolatedStorage, with some modifications
  305.         private static object GetEvidenceInfo(AppDomain appDomain, string exePath, out string typeName)
  306.         {
  307.             ControlEvidencePermission.Assert();
  308.             Evidence evidence = appDomain.Evidence;
  309.             StrongName sn = null;
  310.             Url url = null;
  311.            
  312.             if (evidence != null) {
  313.                 IEnumerator e = evidence.GetHostEnumerator();
  314.                 object temp = null;
  315.                
  316.                 while (e.MoveNext()) {
  317.                     temp = e.Current;
  318.                    
  319.                     if (temp is StrongName) {
  320.                         sn = (StrongName)temp;
  321.                         break;
  322.                     }
  323.                     else if (temp is Url) {
  324.                         url = (Url)temp;
  325.                     }
  326.                 }
  327.             }
  328.            
  329.             object o = null;
  330.            
  331.             // The order of preference is StrongName, Url, ExePath.
  332.             if (sn != null) {
  333.                 o = sn;
  334.                 typeName = StrongNameDesc;
  335.             }
  336.             else if (url != null) {
  337.                 // Extract the url string and normalize it to use as evidence
  338.                 o = url.Value.ToUpperInvariant();
  339.                 typeName = UrlDesc;
  340.             }
  341.             else if (exePath != null) {
  342.                 o = exePath;
  343.                 typeName = PathDesc;
  344.             }
  345.             else {
  346.                 typeName = null;
  347.             }
  348.            
  349.             return o;
  350.         }
  351.        
  352.        
  353.         private bool IsClickOnceDeployed(AppDomain appDomain)
  354.         {
  355.            
  356.             return false;
  357.         }
  358.        
  359.         private void SetNamesAndVersion(string applicationFilename, Assembly exeAssembly, bool isHttp)
  360.         {
  361.             Type mainType = null;
  362.            
  363.             //
  364.             // Get CompanyName, ProductName, and ProductVersion
  365.             // First try custom attributes on the assembly.
  366.             //
  367.             if (exeAssembly != null) {
  368.                 object[] attrs = exeAssembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
  369.                 if (attrs != null && attrs.Length > 0) {
  370.                     _companyName = ((AssemblyCompanyAttribute)attrs[0]).Company;
  371.                     if (_companyName != null) {
  372.                         _companyName = _companyName.Trim();
  373.                     }
  374.                 }
  375.                
  376.                 attrs = exeAssembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);
  377.                 if (attrs != null && attrs.Length > 0) {
  378.                     _productName = ((AssemblyProductAttribute)attrs[0]).Product;
  379.                     if (_productName != null) {
  380.                         _productName = _productName.Trim();
  381.                     }
  382.                 }
  383.                
  384.                 _productVersion = exeAssembly.GetName().Version.ToString();
  385.                 if (_productVersion != null) {
  386.                     _productVersion = _productVersion.Trim();
  387.                 }
  388.             }
  389.            
  390.             //
  391.             // If we couldn't get custom attributes, try the Win32 file version
  392.             //
  393.             if (!isHttp && (String.IsNullOrEmpty(_companyName) || String.IsNullOrEmpty(_productName) || String.IsNullOrEmpty(_productVersion))) {
  394.                 string versionInfoFileName = null;
  395.                
  396.                 if (exeAssembly != null) {
  397.                     MethodInfo entryPoint = exeAssembly.EntryPoint;
  398.                     if (entryPoint != null) {
  399.                         mainType = entryPoint.ReflectedType;
  400.                         if (mainType != null) {
  401.                             versionInfoFileName = mainType.Module.FullyQualifiedName;
  402.                         }
  403.                     }
  404.                 }
  405.                
  406.                 if (versionInfoFileName == null) {
  407.                     versionInfoFileName = applicationFilename;
  408.                 }
  409.                
  410.             }
  411.            
  412.             if (String.IsNullOrEmpty(_companyName) || String.IsNullOrEmpty(_productName)) {
  413.                 string ns = null;
  414.                 if (mainType != null) {
  415.                     ns = mainType.Namespace;
  416.                 }
  417.                
  418.                 // Desperate measures for product name
  419.                 if (String.IsNullOrEmpty(_productName)) {
  420.                     // Try the remainder of the namespace
  421.                     if (ns != null) {
  422.                         int lastDot = ns.LastIndexOf(".", StringComparison.Ordinal);
  423.                         if (lastDot != -1 && lastDot < ns.Length - 1) {
  424.                             _productName = ns.Substring(lastDot + 1);
  425.                         }
  426.                         else {
  427.                             _productName = ns;
  428.                         }
  429.                        
  430.                         _productName = _productName.Trim();
  431.                     }
  432.                    
  433.                     // Try the type of the entry assembly
  434.                     if (String.IsNullOrEmpty(_productName) && mainType != null) {
  435.                         _productName = mainType.Name.Trim();
  436.                     }
  437.                    
  438.                     // give up, return empty string
  439.                     if (_productName == null) {
  440.                         _productName = string.Empty;
  441.                     }
  442.                 }
  443.                
  444.                 // Desperate measures for company name
  445.                 if (String.IsNullOrEmpty(_companyName)) {
  446.                     // Try the first part of the namespace
  447.                     if (ns != null) {
  448.                         int firstDot = ns.IndexOf(".", StringComparison.Ordinal);
  449.                         if (firstDot != -1) {
  450.                             _companyName = ns.Substring(0, firstDot);
  451.                         }
  452.                         else {
  453.                             _companyName = ns;
  454.                         }
  455.                        
  456.                         _companyName = _companyName.Trim();
  457.                     }
  458.                    
  459.                     if (String.IsNullOrEmpty(_companyName)) {
  460.                         _companyName = _productName;
  461.                     }
  462.                 }
  463.             }
  464.            
  465.             // Desperate measures for product version - assume 1.0
  466.             if (String.IsNullOrEmpty(_productVersion)) {
  467.                 _productVersion = "1.0.0.0";
  468.             }
  469.         }
  470.        
  471.         // Borrowed from IsolatedStorage
  472.         private static string ToBase32StringSuitableForDirName(byte[] buff)
  473.         {
  474.             StringBuilder sb = new StringBuilder();
  475.             byte b0;
  476.             byte b1;
  477.             byte b2;
  478.             byte b3;
  479.             byte b4;
  480.             int l;
  481.             int i;
  482.            
  483.             l = buff.Length;
  484.             i = 0;
  485.            
  486.             // Create l chars using the last 5 bits of each byte.
  487.             // Consume 3 MSB bits 5 bytes at a time.
  488.            
  489.             do {
  490.                 b0 = (i < l) ? buff[i++] : (byte)0;
  491.                 b1 = (i < l) ? buff[i++] : (byte)0;
  492.                 b2 = (i < l) ? buff[i++] : (byte)0;
  493.                 b3 = (i < l) ? buff[i++] : (byte)0;
  494.                 b4 = (i < l) ? buff[i++] : (byte)0;
  495.                
  496.                 // Consume the 5 Least significant bits of each byte
  497.                 sb.Append(s_Base32Char[b0 & 31]);
  498.                 sb.Append(s_Base32Char[b1 & 31]);
  499.                 sb.Append(s_Base32Char[b2 & 31]);
  500.                 sb.Append(s_Base32Char[b3 & 31]);
  501.                 sb.Append(s_Base32Char[b4 & 31]);
  502.                
  503.                 // Consume 3 MSB of b0, b1, MSB bits 6, 7 of b3, b4
  504.                 sb.Append(s_Base32Char[(((b0 & 224) >> 5) | ((b3 & 96) >> 2))]);
  505.                
  506.                 sb.Append(s_Base32Char[(((b1 & 224) >> 5) | ((b4 & 96) >> 2))]);
  507.                
  508.                 // Consume 3 MSB bits of b2, 1 MSB bit of b3, b4
  509.                
  510.                 b2 >>= 5;
  511.                
  512.                 if ((b3 & 128) != 0)
  513.                     b2 |= 8;
  514.                 if ((b4 & 128) != 0)
  515.                     b2 |= 16;
  516.                
  517.                 sb.Append(s_Base32Char[b2]);
  518.                
  519.             }
  520.             while (i < l);
  521.            
  522.             return sb.ToString();
  523.         }
  524.        
  525.         // Makes the passed in string suitable to use as a path name by replacing illegal characters
  526.         // with underscores. Additionally, we do two things - replace spaces too with underscores and
  527.         // limit the resultant string's length to MAX_LENGTH_TO_USE if limitSize is true.
  528.         private string Validate(string str, bool limitSize)
  529.         {
  530.             string validated = str;
  531.            
  532.             if (!String.IsNullOrEmpty(validated)) {
  533.                 // First replace all illegal characters with underscores
  534.                 foreach (char c in Path.GetInvalidFileNameChars()) {
  535.                     validated = validated.Replace(c, '_');
  536.                 }
  537.                
  538.                 // Replace all spaces with underscores
  539.                 validated = validated.Replace(' ', '_');
  540.                
  541.                 if (limitSize) {
  542.                     validated = (validated.Length > MAX_LENGTH_TO_USE) ? validated.Substring(0, MAX_LENGTH_TO_USE) : validated;
  543.                 }
  544.             }
  545.            
  546.             return validated;
  547.         }
  548.     }
  549. }

Developer Fusion