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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="ClientSettingsStore.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. using System.Diagnostics.CodeAnalysis;
  16. [assembly: SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Scope = "member", Target = "System.Configuration.ClientSettingsStore+QuotaEnforcedStream.Dispose(System.Boolean):System.Void")]
  17. namespace System.Configuration
  18. {
  19.     using System;
  20.     using System.Collections;
  21.     using System.Collections.Specialized;
  22.     using System.Configuration;
  23.     using System.Configuration.Internal;
  24.     using System.Configuration.Provider;
  25.     using System.Diagnostics;
  26.     using System.IO;
  27.     using System.Security;
  28.     using System.Security.Permissions;
  29.     using System.Xml;
  30.    
  31.    
  32.     /// <devdoc>
  33.     /// This class abstracts the details of config system away from the LocalFileSettingsProvider. It talks to
  34.     /// the configuration API and the relevant Sections to read and write settings.
  35.     /// It understands sections of type ClientSettingsSection.
  36.     ///
  37.     /// NOTE: This API supports reading from app.exe.config and user.config, but writing only to
  38.     /// user.config.
  39.     /// </devdoc>
  40.     internal sealed class ClientSettingsStore
  41.     {
  42.         private const string ApplicationSettingsGroupName = "applicationSettings";
  43.         private const string UserSettingsGroupName = "userSettings";
  44.         private const string ApplicationSettingsGroupPrefix = ApplicationSettingsGroupName + "/";
  45.         private const string UserSettingsGroupPrefix = UserSettingsGroupName + "/";
  46.        
  47.         private Configuration GetUserConfig(bool isRoaming)
  48.         {
  49.             ConfigurationUserLevel userLevel = isRoaming ? ConfigurationUserLevel.PerUserRoaming : ConfigurationUserLevel.PerUserRoamingAndLocal;
  50.            
  51.             return ClientSettingsConfigurationHost.OpenExeConfiguration(userLevel);
  52.         }
  53.        
  54.         private ClientSettingsSection GetConfigSection(Configuration config, string sectionName, bool declare)
  55.         {
  56.             string fullSectionName = UserSettingsGroupPrefix + sectionName;
  57.             ClientSettingsSection section = null;
  58.            
  59.             if (config != null) {
  60.                 section = config.GetSection(fullSectionName) as ClientSettingsSection;
  61.                
  62.                 if (section == null && declare) {
  63.                     // Looks like the section isn't declared - let's declare it and try again.
  64.                     DeclareSection(config, sectionName);
  65.                     section = config.GetSection(fullSectionName) as ClientSettingsSection;
  66.                 }
  67.             }
  68.            
  69.             return section;
  70.         }
  71.        
  72.         // Declares the section handler of a given section in its section group, if a declaration isn't already
  73.         // present.
  74.         private void DeclareSection(Configuration config, string sectionName)
  75.         {
  76.             ConfigurationSectionGroup settingsGroup = config.GetSectionGroup(UserSettingsGroupName);
  77.            
  78.             if (settingsGroup == null) {
  79.                 //Declare settings group
  80.                 ConfigurationSectionGroup group = new UserSettingsGroup();
  81.                 config.SectionGroups.Add(UserSettingsGroupName, group);
  82.             }
  83.            
  84.             settingsGroup = config.GetSectionGroup(UserSettingsGroupName);
  85.            
  86.             Debug.Assert(settingsGroup != null, "Failed to declare settings group");
  87.            
  88.             if (settingsGroup != null) {
  89.                 ConfigurationSection section = settingsGroup.Sections[sectionName];
  90.                 if (section == null) {
  91.                     section = new ClientSettingsSection();
  92.                     section.SectionInformation.AllowExeDefinition = ConfigurationAllowExeDefinition.MachineToLocalUser;
  93.                     section.SectionInformation.RequirePermission = false;
  94.                     settingsGroup.Sections.Add(sectionName, section);
  95.                 }
  96.             }
  97.         }
  98.        
  99.         internal IDictionary ReadSettings(string sectionName, bool isUserScoped)
  100.         {
  101.             IDictionary settings = new Hashtable();
  102.            
  103.             if (isUserScoped && !ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
  104.                 return settings;
  105.             }
  106.            
  107.             string prefix = isUserScoped ? UserSettingsGroupPrefix : ApplicationSettingsGroupPrefix;
  108.             ConfigurationManager.RefreshSection(prefix + sectionName);
  109.             ClientSettingsSection section = ConfigurationManager.GetSection(prefix + sectionName) as ClientSettingsSection;
  110.            
  111.             if (section != null) {
  112.                 foreach (SettingElement setting in section.Settings) {
  113.                     settings[setting.Name] = new StoredSetting(setting.SerializeAs, setting.Value.ValueXml);
  114.                 }
  115.             }
  116.            
  117.             return settings;
  118.         }
  119.        
  120.         static internal IDictionary ReadSettingsFromFile(string configFileName, string sectionName, bool isUserScoped)
  121.         {
  122.             IDictionary settings = new Hashtable();
  123.            
  124.             if (isUserScoped && !ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
  125.                 return settings;
  126.             }
  127.            
  128.             string prefix = isUserScoped ? UserSettingsGroupPrefix : ApplicationSettingsGroupPrefix;
  129.             ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
  130.            
  131.             // NOTE: When isUserScoped is true, we don't care if configFileName represents a roaming file or
  132.             // a local one. All we want is three levels of configuration. So, we use the PerUserRoaming level.
  133.             ConfigurationUserLevel userLevel = isUserScoped ? ConfigurationUserLevel.PerUserRoaming : ConfigurationUserLevel.None;
  134.            
  135.             if (isUserScoped) {
  136.                 fileMap.ExeConfigFilename = ConfigurationManagerInternalFactory.Instance.ApplicationConfigUri;
  137.                 fileMap.RoamingUserConfigFilename = configFileName;
  138.             }
  139.             else {
  140.                 fileMap.ExeConfigFilename = configFileName;
  141.             }
  142.            
  143.             Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel);
  144.             ClientSettingsSection section = config.GetSection(prefix + sectionName) as ClientSettingsSection;
  145.            
  146.             if (section != null) {
  147.                 foreach (SettingElement setting in section.Settings) {
  148.                     settings[setting.Name] = new StoredSetting(setting.SerializeAs, setting.Value.ValueXml);
  149.                 }
  150.             }
  151.            
  152.             return settings;
  153.         }
  154.        
  155.         internal ConnectionStringSettingsCollection ReadConnectionStrings()
  156.         {
  157.             return PrivilegedConfigurationManager.ConnectionStrings;
  158.         }
  159.        
  160.         internal void RevertToParent(string sectionName, bool isRoaming)
  161.         {
  162.             if (!ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
  163.                 throw new ConfigurationErrorsException(SR.GetString(SR.UserSettingsNotSupported));
  164.             }
  165.            
  166.             Configuration config = GetUserConfig(isRoaming);
  167.             ClientSettingsSection section = GetConfigSection(config, sectionName, false);
  168.            
  169.             // If the section is null, there is nothing to revert.
  170.             if (section != null) {
  171.                 section.SectionInformation.RevertToParent();
  172.                 config.Save();
  173.             }
  174.         }
  175.        
  176.         internal void WriteSettings(string sectionName, bool isRoaming, IDictionary newSettings)
  177.         {
  178.             if (!ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
  179.                 throw new ConfigurationErrorsException(SR.GetString(SR.UserSettingsNotSupported));
  180.             }
  181.            
  182.             Configuration config = GetUserConfig(isRoaming);
  183.             ClientSettingsSection section = GetConfigSection(config, sectionName, true);
  184.            
  185.             if (section != null) {
  186.                 SettingElementCollection sec = section.Settings;
  187.                 foreach (DictionaryEntry entry in newSettings) {
  188.                     SettingElement se = sec.Get((string)entry.Key);
  189.                    
  190.                     if (se == null) {
  191.                         se = new SettingElement();
  192.                         se.Name = (string)entry.Key;
  193.                         sec.Add(se);
  194.                     }
  195.                    
  196.                     StoredSetting ss = (StoredSetting)entry.Value;
  197.                     se.SerializeAs = ss.SerializeAs;
  198.                     se.Value.ValueXml = ss.Value;
  199.                 }
  200.                
  201.                 try {
  202.                     config.Save();
  203.                 }
  204.                 catch (ConfigurationErrorsException ex) {
  205.                     // We wrap this in an exception with our error message and throw again.
  206.                     throw new ConfigurationErrorsException(SR.GetString(SR.SettingsSaveFailed, ex.Message), ex);
  207.                 }
  208.             }
  209.             else {
  210.                 throw new ConfigurationErrorsException(SR.GetString(SR.SettingsSaveFailedNoSection));
  211.             }
  212.         }
  213.        
  214.         /// <devdoc>
  215.         /// A private configuration host that we use to write settings to config. We need this so we
  216.         /// can enforce a quota on the size of stuff written out.
  217.         /// </devdoc>
  218.         private sealed class ClientSettingsConfigurationHost : DelegatingConfigHost
  219.         {
  220.             private const string ClientConfigurationHostTypeName = "System.Configuration.ClientConfigurationHost," + AssemblyRef.SystemConfiguration;
  221.             private const string InternalConfigConfigurationFactoryTypeName = "System.Configuration.Internal.InternalConfigConfigurationFactory," + AssemblyRef.SystemConfiguration;
  222.             private static IInternalConfigConfigurationFactory s_configFactory;
  223.            
  224.             /// <devdoc>
  225.             /// ClientConfigurationHost implements this - a way of getting some info from it without
  226.             /// depending too much on its internals.
  227.             /// </devdoc>
  228.             private IInternalConfigClientHost ClientHost {
  229.                 get { return (IInternalConfigClientHost)Host; }
  230.             }
  231.            
  232.             static internal IInternalConfigConfigurationFactory ConfigFactory {
  233.                 get {
  234.                     if (s_configFactory == null) {
  235.                         s_configFactory = (IInternalConfigConfigurationFactory)TypeUtil.CreateInstanceWithReflectionPermission(InternalConfigConfigurationFactoryTypeName);
  236.                     }
  237.                     return s_configFactory;
  238.                 }
  239.             }
  240.            
  241.             private ClientSettingsConfigurationHost()
  242.             {
  243.             }
  244.            
  245.             public override void Init(IInternalConfigRoot configRoot, params object[] hostInitParams)
  246.             {
  247.                 Debug.Fail("Did not expect to get called here");
  248.             }
  249.            
  250.             /// <devdoc>
  251.             /// We delegate this to the ClientConfigurationHost. The only thing we need to do here is to
  252.             /// build a configPath from the ConfigurationUserLevel we get passed in.
  253.             /// </devdoc>
  254.             public override void InitForConfiguration(ref string locationSubPath, out string configPath, out string locationConfigPath, IInternalConfigRoot configRoot, params object[] hostInitConfigurationParams)
  255.             {
  256.                
  257.                 ConfigurationUserLevel userLevel = (ConfigurationUserLevel)hostInitConfigurationParams[0];
  258.                 string desiredConfigPath = null;
  259.                 Host = (IInternalConfigHost)TypeUtil.CreateInstanceWithReflectionPermission(ClientConfigurationHostTypeName);
  260.                
  261.                 switch (userLevel) {
  262.                     case ConfigurationUserLevel.None:
  263.                         desiredConfigPath = ClientHost.GetExeConfigPath();
  264.                         break;
  265.                     case ConfigurationUserLevel.PerUserRoaming:
  266.                        
  267.                         desiredConfigPath = ClientHost.GetRoamingUserConfigPath();
  268.                         break;
  269.                     case ConfigurationUserLevel.PerUserRoamingAndLocal:
  270.                        
  271.                         desiredConfigPath = ClientHost.GetLocalUserConfigPath();
  272.                         break;
  273.                     default:
  274.                        
  275.                         throw new ArgumentException(SR.GetString(SR.UnknownUserLevel));
  276.                         break;
  277.                 }
  278.                
  279.                
  280.                 Host.InitForConfiguration(ref locationSubPath, out configPath, out locationConfigPath, configRoot, null, null, desiredConfigPath);
  281.             }
  282.            
  283.             private bool IsKnownConfigFile(string filename)
  284.             {
  285.                 return String.Equals(filename, ConfigurationManagerInternalFactory.Instance.MachineConfigPath, StringComparison.OrdinalIgnoreCase) || String.Equals(filename, ConfigurationManagerInternalFactory.Instance.ApplicationConfigUri, StringComparison.OrdinalIgnoreCase) || String.Equals(filename, ConfigurationManagerInternalFactory.Instance.ExeLocalConfigPath, StringComparison.OrdinalIgnoreCase) || String.Equals(filename, ConfigurationManagerInternalFactory.Instance.ExeRoamingConfigPath, StringComparison.OrdinalIgnoreCase);
  286.                
  287.             }
  288.            
  289.             static internal Configuration OpenExeConfiguration(ConfigurationUserLevel userLevel)
  290.             {
  291.                 return ConfigFactory.Create(typeof(ClientSettingsConfigurationHost), userLevel);
  292.             }
  293.            
  294.             /// <devdoc>
  295.             /// If the stream we are asked for represents a config file that we know about, we ask
  296.             /// the host to assert appropriate permissions.
  297.             /// </devdoc>
  298.             public override Stream OpenStreamForRead(string streamName)
  299.             {
  300.                 if (IsKnownConfigFile(streamName)) {
  301.                     return Host.OpenStreamForRead(streamName, true);
  302.                 }
  303.                 else {
  304.                     return Host.OpenStreamForRead(streamName);
  305.                 }
  306.             }
  307.            
  308.             /// <devdoc>
  309.             /// If the stream we are asked for represents a user.config file that we know about, we wrap it in a
  310.             /// QuotaEnforcedStream, after asking the host to assert appropriate permissions.///
  311.             /// </devdoc>
  312.             public override Stream OpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext)
  313.             {
  314.                 Stream stream = null;
  315.                
  316.                 if (String.Equals(streamName, ConfigurationManagerInternalFactory.Instance.ExeLocalConfigPath, StringComparison.OrdinalIgnoreCase)) {
  317.                     stream = new QuotaEnforcedStream(Host.OpenStreamForWrite(streamName, templateStreamName, ref writeContext, true), false);
  318.                 }
  319.                 else if (String.Equals(streamName, ConfigurationManagerInternalFactory.Instance.ExeRoamingConfigPath, StringComparison.OrdinalIgnoreCase)) {
  320.                     stream = new QuotaEnforcedStream(Host.OpenStreamForWrite(streamName, templateStreamName, ref writeContext, true), true);
  321.                 }
  322.                 else {
  323.                     stream = Host.OpenStreamForWrite(streamName, templateStreamName, ref writeContext);
  324.                 }
  325.                
  326.                 return stream;
  327.             }
  328.            
  329.             /// <devdoc>
  330.             /// If this is a stream that represents a user.config file that we know about, we ask
  331.             /// the host to assert appropriate permissions.
  332.             /// </devdoc>
  333.             public override void WriteCompleted(string streamName, bool success, object writeContext)
  334.             {
  335.                 if (String.Equals(streamName, ConfigurationManagerInternalFactory.Instance.ExeLocalConfigPath, StringComparison.OrdinalIgnoreCase) || String.Equals(streamName, ConfigurationManagerInternalFactory.Instance.ExeRoamingConfigPath, StringComparison.OrdinalIgnoreCase)) {
  336.                    
  337.                     Host.WriteCompleted(streamName, success, writeContext, true);
  338.                 }
  339.                 else {
  340.                     Host.WriteCompleted(streamName, success, writeContext);
  341.                 }
  342.             }
  343.         }
  344.        
  345.         /// <devdoc>
  346.         /// A private stream class that wraps a stream and enforces a quota. The quota enforcement uses
  347.         /// IsolatedStorageFilePermission. We override nearly all methods on the Stream class so we can
  348.         /// forward to the wrapped stream. In the methods that affect stream length, we verify that the
  349.         /// quota is respected before forwarding.
  350.         /// </devdoc>
  351.         private sealed class QuotaEnforcedStream : Stream
  352.         {
  353.             private Stream _originalStream;
  354.             private bool _isRoaming;
  355.            
  356.             internal QuotaEnforcedStream(Stream originalStream, bool isRoaming)
  357.             {
  358.                 _originalStream = originalStream;
  359.                 _isRoaming = isRoaming;
  360.                
  361.                 Debug.Assert(_originalStream != null, "originalStream was null.");
  362.             }
  363.            
  364.             public override bool CanRead {
  365.                 get { return _originalStream.CanRead; }
  366.             }
  367.            
  368.             public override bool CanWrite {
  369.                 get { return _originalStream.CanWrite; }
  370.             }
  371.            
  372.             public override bool CanSeek {
  373.                 get { return _originalStream.CanSeek; }
  374.             }
  375.            
  376.             public override long Length {
  377.                 get { return _originalStream.Length; }
  378.             }
  379.            
  380.             public override long Position {
  381.                
  382.                 get { return _originalStream.Position; }
  383.                
  384.                 set {
  385.                     if (value < 0) {
  386.                         throw new ArgumentOutOfRangeException("value", SR.GetString(SR.PositionOutOfRange));
  387.                     }
  388.                    
  389.                     Seek(value, SeekOrigin.Begin);
  390.                 }
  391.             }
  392.            
  393.             public override void Close()
  394.             {
  395.                 _originalStream.Close();
  396.             }
  397.            
  398.             protected override void Dispose(bool disposing)
  399.             {
  400.                 if (disposing) {
  401.                     if (_originalStream != null) {
  402.                         ((IDisposable)_originalStream).Dispose();
  403.                         _originalStream = null;
  404.                     }
  405.                    
  406.                 }
  407.                
  408.                 base.Dispose(disposing);
  409.             }
  410.            
  411.             public override void Flush()
  412.             {
  413.                 _originalStream.Flush();
  414.             }
  415.            
  416.             public override void SetLength(long value)
  417.             {
  418.                 long oldLen = _originalStream.Length;
  419.                 long newLen = value;
  420.                
  421.                 EnsureQuota(Math.Max(oldLen, newLen));
  422.                 _originalStream.SetLength(value);
  423.             }
  424.            
  425.             public override int Read(byte[] buffer, int offset, int count)
  426.             {
  427.                 return _originalStream.Read(buffer, offset, count);
  428.             }
  429.            
  430.             public override int ReadByte()
  431.             {
  432.                 return _originalStream.ReadByte();
  433.             }
  434.            
  435.             public override long Seek(long offset, SeekOrigin origin)
  436.             {
  437.                 if (!CanSeek) {
  438.                     throw new NotSupportedException();
  439.                 }
  440.                
  441.                 long oldLen = _originalStream.Length;
  442.                 long newLen;
  443.                
  444.                 switch (origin) {
  445.                     case SeekOrigin.Begin:
  446.                         newLen = offset;
  447.                         break;
  448.                     case SeekOrigin.Current:
  449.                         newLen = _originalStream.Position + offset;
  450.                         break;
  451.                     case SeekOrigin.End:
  452.                         newLen = oldLen + offset;
  453.                         break;
  454.                     default:
  455.                         throw new ArgumentException(SR.GetString(SR.UnknownSeekOrigin), "origin");
  456.                         break;
  457.                 }
  458.                
  459.                 EnsureQuota(Math.Max(oldLen, newLen));
  460.                 return _originalStream.Seek(offset, origin);
  461.             }
  462.            
  463.             public override void Write(byte[] buffer, int offset, int count)
  464.             {
  465.                 if (!CanWrite) {
  466.                     throw new NotSupportedException();
  467.                 }
  468.                
  469.                 long oldLen = _originalStream.Length;
  470.                 long newLen = _originalStream.CanSeek ? _originalStream.Position + (long)count : _originalStream.Length + (long)count;
  471.                 EnsureQuota(Math.Max(oldLen, newLen));
  472.                 _originalStream.Write(buffer, offset, count);
  473.             }
  474.            
  475.             public override void WriteByte(byte value)
  476.             {
  477.                 if (!CanWrite) {
  478.                     throw new NotSupportedException();
  479.                 }
  480.                
  481.                 long oldLen = _originalStream.Length;
  482.                 long newLen = _originalStream.CanSeek ? _originalStream.Position + 1 : _originalStream.Length + 1;
  483.                 EnsureQuota(Math.Max(oldLen, newLen));
  484.                
  485.                 _originalStream.WriteByte(value);
  486.             }
  487.            
  488.             public override IAsyncResult BeginRead(byte[] buffer, int offset, int numBytes, AsyncCallback userCallback, object stateObject)
  489.             {
  490.                 return _originalStream.BeginRead(buffer, offset, numBytes, userCallback, stateObject);
  491.             }
  492.            
  493.             public override int EndRead(IAsyncResult asyncResult)
  494.             {
  495.                 return _originalStream.EndRead(asyncResult);
  496.                
  497.             }
  498.            
  499.             public override IAsyncResult BeginWrite(byte[] buffer, int offset, int numBytes, AsyncCallback userCallback, object stateObject)
  500.             {
  501.                 if (!CanWrite) {
  502.                     throw new NotSupportedException();
  503.                 }
  504.                
  505.                 long oldLen = _originalStream.Length;
  506.                 long newLen = _originalStream.CanSeek ? _originalStream.Position + (long)numBytes : _originalStream.Length + (long)numBytes;
  507.                 EnsureQuota(Math.Max(oldLen, newLen));
  508.                 return _originalStream.BeginWrite(buffer, offset, numBytes, userCallback, stateObject);
  509.             }
  510.            
  511.             public override void EndWrite(IAsyncResult asyncResult)
  512.             {
  513.                 _originalStream.EndWrite(asyncResult);
  514.             }
  515.            
  516.             private void EnsureQuota(long size)
  517.             {
  518.                 IsolatedStoragePermission storagePerm = new IsolatedStorageFilePermission(PermissionState.None);
  519.                 storagePerm.UserQuota = size;
  520.                 storagePerm.UsageAllowed = _isRoaming ? IsolatedStorageContainment.DomainIsolationByRoamingUser : IsolatedStorageContainment.DomainIsolationByUser;
  521.                 storagePerm.Demand();
  522.             }
  523.         }
  524.     }
  525.    
  526.     /// <devdoc>
  527.     /// The ClientSettingsStore talks to the LocalFileSettingsProvider through a dictionary which maps from
  528.     /// setting names to StoredSetting structs. This struct contains the relevant information.
  529.     /// </devdoc>
  530.     internal struct StoredSetting
  531.     {
  532.         internal StoredSetting(SettingsSerializeAs serializeAs, XmlNode value)
  533.         {
  534.             SerializeAs = serializeAs;
  535.             Value = value;
  536.         }
  537.         internal SettingsSerializeAs SerializeAs;
  538.         internal XmlNode Value;
  539.     }
  540. }

Developer Fusion