1 //------------------------------------------------------------------------------
2 // <copyright file="BaseConfigurationRecord.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 namespace System
.Configuration
{
9 using System
.Configuration
.Internal
;
10 using System
.Collections
;
11 using System
.Collections
.Generic
;
12 using System
.Collections
.Specialized
;
13 using System
.Configuration
;
14 using System
.Globalization
;
16 using System
.Reflection
;
17 using System
.Runtime
.InteropServices
;
18 using System
.Security
.Permissions
;
19 using System
.Security
;
21 using System
.Threading
;
23 using System
.Runtime
.Versioning
;
26 // This object represents the configuration for a request path, and is cached per-path.
29 [System
.Diagnostics
.DebuggerDisplay("ConfigPath = {ConfigPath}")]
30 abstract internal class BaseConfigurationRecord
: IInternalConfigRecord
{
31 protected const string NL
= "\r\n";
33 internal const string KEYWORD_TRUE
= "true";
34 internal const string KEYWORD_FALSE
= "false";
35 protected const string KEYWORD_CONFIGURATION
= "configuration";
36 protected const string KEYWORD_CONFIGURATION_NAMESPACE
= "http://schemas.microsoft.com/.NetConfiguration/v2.0";
37 protected const string KEYWORD_CONFIGSECTIONS
= "configSections";
38 protected const string KEYWORD_SECTION
= "section";
39 protected const string KEYWORD_SECTION_NAME
= "name";
40 protected const string KEYWORD_SECTION_TYPE
= "type";
41 protected const string KEYWORD_SECTION_ALLOWLOCATION
= "allowLocation";
42 protected const string KEYWORD_SECTION_ALLOWDEFINITION
= "allowDefinition";
43 protected const string KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE
= "Everywhere";
44 protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY
= "MachineOnly";
45 protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION
= "MachineToApplication";
46 protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT
= "MachineToWebRoot";
47 protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION
= "allowExeDefinition";
48 protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOROAMING
= "MachineToRoamingUser";
49 protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOLOCAL
= "MachineToLocalUser";
50 protected const string KEYWORD_SECTION_RESTARTONEXTERNALCHANGES
= "restartOnExternalChanges";
51 protected const string KEYWORD_SECTION_REQUIREPERMISSION
= "requirePermission";
52 protected const string KEYWORD_SECTIONGROUP
= "sectionGroup";
53 protected const string KEYWORD_SECTIONGROUP_NAME
= "name";
54 protected const string KEYWORD_SECTIONGROUP_TYPE
= "type";
55 protected const string KEYWORD_REMOVE
= "remove";
56 protected const string KEYWORD_CLEAR
= "clear";
57 protected const string KEYWORD_LOCATION
= "location";
58 protected const string KEYWORD_LOCATION_PATH
= "path";
59 internal const string KEYWORD_LOCATION_ALLOWOVERRIDE
= "allowOverride";
60 protected const string KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS
= "inheritInChildApplications";
61 protected const string KEYWORD_CONFIGSOURCE
= "configSource";
62 protected const string KEYWORD_XMLNS
= "xmlns";
63 protected const string KEYWORD_CONFIG_BUILDER
= "configBuilders";
64 internal const string KEYWORD_PROTECTION_PROVIDER
= "configProtectionProvider";
65 protected const string FORMAT_NEWCONFIGFILE
= "<?xml version=\"1.0\" encoding=\"{0}\"?>\r\n";
66 protected const string FORMAT_CONFIGURATION
= "<configuration>\r\n";
67 protected const string FORMAT_CONFIGURATION_NAMESPACE
= "<configuration xmlns=\"{0}\">\r\n";
68 protected const string FORMAT_CONFIGURATION_ENDELEMENT
= "</configuration>";
69 internal const string KEYWORD_SECTION_OVERRIDEMODEDEFAULT
= "overrideModeDefault";
70 internal const string KEYWORD_LOCATION_OVERRIDEMODE
= "overrideMode";
71 internal const string KEYWORD_OVERRIDEMODE_INHERIT
= "Inherit";
72 internal const string KEYWORD_OVERRIDEMODE_ALLOW
= "Allow";
73 internal const string KEYWORD_OVERRIDEMODE_DENY
= "Deny";
76 protected const string FORMAT_LOCATION_NOPATH
= "<location {0} inheritInChildApplications=\"{1}\">\r\n";
77 protected const string FORMAT_LOCATION_PATH
= "<location path=\"{2}\" {0} inheritInChildApplications=\"{1}\">\r\n";
78 protected const string FORMAT_LOCATION_ENDELEMENT
= "</location>";
79 internal const string KEYWORD_LOCATION_OVERRIDEMODE_STRING
= "{0}=\"{1}\"";
81 protected const string FORMAT_SECTION_CONFIGSOURCE
= "<{0} configSource=\"{1}\" />";
82 protected const string FORMAT_CONFIGSOURCE_FILE
= "<?xml version=\"1.0\" encoding=\"{0}\"?>\r\n";
84 protected const string FORMAT_SECTIONGROUP_ENDELEMENT
= "</sectionGroup>";
86 // Class flags should only be used with the ClassFlags property.
87 protected const int ClassSupportsChangeNotifications
= 0x00000001;
88 protected const int ClassSupportsRefresh
= 0x00000002;
89 protected const int ClassSupportsImpersonation
= 0x00000004;
90 protected const int ClassSupportsRestrictedPermissions
= 0x00000008;
91 protected const int ClassSupportsKeepInputs
= 0x00000010;
92 protected const int ClassSupportsDelayedInit
= 0x00000020;
93 protected const int ClassIgnoreLocalErrors
= 0x00000040;
95 // Flags to use with the _flags field.
96 protected const int ProtectedDataInitialized
= 0x00000001;
97 protected const int Closed
= 0x00000002;
98 protected const int PrefetchAll
= 0x00000008;
99 protected const int IsAboveApplication
= 0x00000020;
100 private const int ContextEvaluated
= 0x00000080;
101 private const int IsLocationListResolved
= 0x00000100;
102 protected const int NamespacePresentInFile
= 0x00000200;
104 private const int RestrictedPermissionsResolved
= 0x00000800;
106 protected const int IsTrusted
= 0x00002000;
108 protected const int SupportsChangeNotifications
= 0x00010000;
109 protected const int SupportsRefresh
= 0x00020000;
110 protected const int SupportsPath
= 0x00040000;
111 protected const int SupportsKeepInputs
= 0x00080000;
112 protected const int SupportsLocation
= 0x00100000;
114 // Flags for Mgmt Configuration Record
115 protected const int ForceLocationWritten
= 0x01000000;
116 protected const int SuggestLocationRemoval
= 0x02000000;
117 protected const int NamespacePresentCurrent
= 0x04000000;
119 protected const int ConfigBuildersInitialized
= 0x08000000;
121 internal const char ConfigPathSeparatorChar
= '/';
122 internal const string ConfigPathSeparatorString
= "/";
123 static internal readonly char[] ConfigPathSeparatorParams
= new char[] {ConfigPathSeparatorChar}
;
125 private static volatile ConfigurationPermission s_unrestrictedConfigPermission
; // cached ConfigurationPermission
127 protected SafeBitVector32 _flags
; // state
128 protected BaseConfigurationRecord _parent
; // parent record
129 protected Hashtable _children
; // configName -> record
130 protected InternalConfigRoot _configRoot
; // root of configuration
131 protected string _configName
; // the last part of the config path
132 protected string _configPath
; // the full config path
133 protected string _locationSubPath
; // subPath for the config record when editing a location configuration
135 private ConfigRecordStreamInfo _configStreamInfo
; // stream info for the config record
137 private object _configContext
; // Context for config level
138 private ConfigurationBuildersSection _configBuilders
; // section containing the general config builders
139 private ProtectedConfigurationSection _protectedConfig
; // section containing the encryption providers
140 private PermissionSet _restrictedPermissions
; // cached restricted permission set
141 private ConfigurationSchemaErrors _initErrors
; // errors encountered during the parse of the configuration file
142 private BaseConfigurationRecord _initDelayedRoot
; // root of delayed initialization
144 // Records information about <configSections> present in this web.config file.
145 // config key -> FactoryRecord
146 protected Hashtable _factoryRecords
;
148 // Records information about sections that apply to this path,
149 // which may be found in this web.config file, in a parent through
150 // inheritance, or in a parent through <location>
151 // config key -> SectionRecord
152 protected Hashtable _sectionRecords
;
154 // Records information about sections in a <location> directive
155 // that do not apply to this configPath (sections where path != ".")
156 protected ArrayList _locationSections
;
158 // WOS 1955773: (Perf) 4,000 location sections in web.config file degrades working set
159 private static string s_appConfigPath
;
161 // Comparer used in sorting IndirectInputs.
162 private static IComparer
<SectionInput
> s_indirectInputsComparer
= new IndirectLocationInputComparer();
164 internal BaseConfigurationRecord() {
165 // not strictly necessary, but compiler spits out a warning without this initiailization
166 _flags
= new SafeBitVector32();
170 protected abstract SimpleBitVector32 ClassFlags {get;}
172 // Create the factory that will evaluate configuration
173 protected abstract object CreateSectionFactory(FactoryRecord factoryRecord
);
175 // Create the configuration object
176 protected abstract object CreateSection(bool inputIsTrusted
, FactoryRecord factoryRecord
, SectionRecord sectionRecord
, SectionInput sectionInput
, object parentConfig
, ConfigXmlReader reader
);
178 // Use the parent result in creating the child
179 protected abstract object UseParentResult(string configKey
, object parentResult
, SectionRecord sectionRecord
);
181 // Return the runtime object from GetSection
182 protected abstract object GetRuntimeObject(object result
);
185 // IInternalConfigRecord methods
188 public string ConfigPath
{
189 get {return _configPath;}
192 public string StreamName
{
193 get {return ConfigStreamInfo.StreamName;}
196 public bool HasInitErrors
{
198 return _initErrors
.HasErrors(ClassFlags
[ClassIgnoreLocalErrors
]);
202 public void ThrowIfInitErrors() {
203 ThrowIfParseErrors(_initErrors
);
206 public object GetSection(string configKey
) {
208 // On debug builds, the config system depends on system.diagnostics,
209 // so we must always return a valid result and never throw.
210 if (configKey
== "system.diagnostics" && !ClassFlags
[ClassIgnoreLocalErrors
]) {
211 return GetSection(configKey
, true, true);
214 return GetSection(configKey
, false, true);
218 return GetSection(configKey
, false, true);
222 public object GetLkgSection(string configKey
) {
223 return GetSection(configKey
, true, true);
226 public void RefreshSection(string configKey
) {
227 _configRoot
.ClearResult(this, configKey
, true);
230 public void Remove() {
231 _configRoot
.RemoveConfigRecord(this);
235 // end of IInternalConfigRecord methods
238 internal bool HasStream
{
239 get { return ConfigStreamInfo.HasStream; }
242 // Determine which sections should be prefetched during the first scan.
243 private bool ShouldPrefetchRawXml(FactoryRecord factoryRecord
) {
244 if (_flags
[PrefetchAll
])
247 switch (factoryRecord
.ConfigKey
) {
248 case BaseConfigurationRecord
.RESERVED_SECTION_PROTECTED_CONFIGURATION
:
249 case "system.diagnostics":
251 case "connectionStrings":
255 return Host
.PrefetchSection(factoryRecord
.Group
, factoryRecord
.Name
);
258 protected IDisposable
Impersonate() {
259 IDisposable context
= null;
260 if (ClassFlags
[ClassSupportsImpersonation
]) {
261 context
= Host
.Impersonate();
264 if (context
== null) {
265 context
= EmptyImpersonationContext
.GetStaticInstance();
271 internal PermissionSet
GetRestrictedPermissions()
273 if (!_flags
[RestrictedPermissionsResolved
])
277 if (!_flags
[RestrictedPermissionsResolved
])
279 if (AppDomain
.CurrentDomain
.IsHomogenous
)
281 // in a homogenous domain, just call PermitOnly() on the AppDomain's existing permission set
282 _restrictedPermissions
= AppDomain
.CurrentDomain
.PermissionSet
;
283 _flags
[RestrictedPermissionsResolved
] = true;
287 // in a non-homogenous domain, use Evidence to calculate the current security policy
288 PermissionSet restrictedPermissions
;
291 GetRestrictedPermissionsWithAssert(out restrictedPermissions
, out isHostReady
);
294 _restrictedPermissions
= restrictedPermissions
;
295 _flags
[RestrictedPermissionsResolved
] = true;
299 // calling PermitOnly() on an unrestricted PermissionSet is a no-op
300 if (_restrictedPermissions
!= null && _restrictedPermissions
.IsUnrestricted())
302 _restrictedPermissions
= null;
313 return _restrictedPermissions
;
315 // Getting the PermissionSet object is a FullTrust operation; we can Assert since our callers are careful not to leak it
316 [PermissionSet(SecurityAction
.Assert
, Unrestricted
= true)]
317 private void GetRestrictedPermissionsWithAssert(out PermissionSet permissionSet
, out bool isHostReady
) {
318 Host
.GetRestrictedPermissions(this, out permissionSet
, out isHostReady
);
322 IInternalConfigRoot configRoot
,
323 BaseConfigurationRecord parent
,
325 string locationSubPath
) {
327 _initErrors
= new ConfigurationSchemaErrors();
330 // try/catch here is only for unexpected exceptions due to errors in
331 // our own code, as we always want the configuration record to be
335 _configRoot
= (InternalConfigRoot
) configRoot
;
337 _configPath
= configPath
;
338 _locationSubPath
= locationSubPath
;
339 _configName
= ConfigPathUtility
.GetName(configPath
);
341 if (IsLocationConfig
) {
342 _configStreamInfo
= _parent
.ConfigStreamInfo
;
345 _configStreamInfo
= new ConfigRecordStreamInfo();
348 // no more initialization in case of root config
352 // determine what features we support
353 _flags
[SupportsChangeNotifications
] = ClassFlags
[ClassSupportsChangeNotifications
] && Host
.SupportsChangeNotifications
;
354 _flags
[SupportsRefresh
] = ClassFlags
[ClassSupportsRefresh
] && Host
.SupportsRefresh
;
355 _flags
[SupportsKeepInputs
] = ClassFlags
[ClassSupportsKeepInputs
] || _flags
[SupportsRefresh
];
356 _flags
[SupportsPath
] = Host
.SupportsPath
;
357 _flags
[SupportsLocation
] = Host
.SupportsLocation
;
359 // get static state based on the configPath
360 if (_flags
[SupportsLocation
]) {
361 _flags
[IsAboveApplication
] = Host
.IsAboveApplication(_configPath
);
364 _flags
[IsTrusted
] = Host
.IsTrustedConfigPath(_configPath
);
366 ArrayList locationSubPathInputs
= null;
368 if (_flags
[SupportsLocation
]) {
370 // Treat location inputs from parent record
371 // as though they were bonafide sections in this record.
373 if (IsLocationConfig
&& _parent
._locationSections
!= null) {
374 // Resolve paths and check for errors in location sections.
375 _parent
.ResolveLocationSections();
378 while (i
< _parent
._locationSections
.Count
) {
379 LocationSectionRecord locationSectionRecord
= (LocationSectionRecord
) _parent
._locationSections
[i
];
381 if (!StringUtil
.EqualsIgnoreCase(locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
, this.ConfigPath
)) {
386 // remove the locationSectionRecord from the list
387 _parent
._locationSections
.RemoveAt(i
);
389 if (locationSubPathInputs
== null) {
390 locationSubPathInputs
= new ArrayList();
393 locationSubPathInputs
.Add(locationSectionRecord
);
398 // Handle indirect inputs for location config record.
399 if (IsLocationConfig
&& Host
.IsLocationApplicable(_configPath
)) {
400 Dictionary
<string, List
<SectionInput
>> indirectLocationInputs
= null;
401 BaseConfigurationRecord current
= _parent
;
403 // Note that the code will also go thru all parents, just like what we did
404 // with the location inputs later. But perf-wise it's okay not to merge the two loops
405 // because a Configuration object will contain at most one location config record.
406 while (!current
.IsRootConfig
) {
407 if (current
._locationSections
!= null) {
408 current
.ResolveLocationSections();
410 foreach (LocationSectionRecord locationSectionRecord
in current
._locationSections
) {
413 // If we're initializing a location config record, we will also use those location
414 // tags that apply to the directories between the parent and the location config
415 // record's ConfigPath.
417 // This way, the result will contain all the inputs (we've parsed so far) which
418 // affect the path specified by locationSubPath.
420 // To look for this kind of input, make sure that:
421 // 1. We are creating a location config
422 // 2. The location tag's target path lies between parent's config path and ConfigPath
423 // 2.1: ConfigPath must be a child of the location tag's target path
424 // 2.2: The target path must be a child of parent's config path
426 // One example of why we need 2.2:
427 // The root web.config has a location tag with path="1", and we open a configuration
428 // at "1" with locationSubPath="sub". If we don't have 2.2, then we will incorrectly set
429 // addInput to true for this location tag. It's incorrect because that location tag
430 // is already used by configRecord at level "1".
432 // 3. The location tag shouldn't be skipped due to inheritInChildApplications setting
439 UrlPath
.IsSubpath(locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
, ConfigPath
) &&
442 UrlPath
.IsSubpath(parent
.ConfigPath
, locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
) &&
445 !ShouldSkipDueToInheritInChildApplications(locationSectionRecord
.SectionXmlInfo
.SkipInChildApps
, locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
)
449 // In order to separate these kinds of input from "file inputs" and "location inputs"
450 // we introduce a new kind of input called the "indirect location inputs".
453 // First add all indirect inputs per configKey to a local list.
454 // We will sort all lists after the while loop.
455 if (indirectLocationInputs
== null) {
456 indirectLocationInputs
= new Dictionary
<string, List
<SectionInput
>>(1);
459 string configKey
= locationSectionRecord
.SectionXmlInfo
.ConfigKey
;
461 if (!((IDictionary
)indirectLocationInputs
).Contains(configKey
)) {
462 indirectLocationInputs
.Add(configKey
, new List
<SectionInput
>(1));
465 indirectLocationInputs
[configKey
].Add(
466 new SectionInput(locationSectionRecord
.SectionXmlInfo
,
467 locationSectionRecord
.ErrorsList
));
469 // copy the initialization errors to the record
470 if (locationSectionRecord
.HasErrors
) {
471 this._initErrors
.AddSavedLocalErrors(locationSectionRecord
.Errors
);
477 current
= current
._parent
;
480 if (indirectLocationInputs
!= null) {
481 // Add indirect inputs per configKey
482 foreach(KeyValuePair
<string, List
<SectionInput
>> keyValuePair
in indirectLocationInputs
) {
483 List
<SectionInput
> inputsPerConfigKey
= keyValuePair
.Value
;
484 string configKey
= keyValuePair
.Key
;
486 // We have to sort the indirect inputs
487 // 1. First by the location tag's target config path, and if they're the same,
488 // 2. Then by the location tag's definition config path.
489 inputsPerConfigKey
.Sort(s_indirectInputsComparer
);
491 // Add them to the section record.
492 // In the sorted list, the closest parent is at the beginning of the
493 // list, which is what we'll add first.
494 SectionRecord sectionRecord
= EnsureSectionRecord(configKey
, true);
495 Debug
.Assert(inputsPerConfigKey
.Count
> 0, "We won't get here unless we have inputs.");
496 foreach(SectionInput sectionInput
in inputsPerConfigKey
) {
497 sectionRecord
.AddIndirectLocationInput(sectionInput
);
500 DebugValidateIndirectInputs(sectionRecord
);
506 // Add location inputs that apply to this path, all the way up the parent hierarchy.
508 if (Host
.IsLocationApplicable(_configPath
)) {
509 BaseConfigurationRecord current
= _parent
;
510 while (!current
.IsRootConfig
) {
511 if (current
._locationSections
!= null) {
512 current
.ResolveLocationSections();
513 foreach (LocationSectionRecord locationSectionRecord
in current
._locationSections
) {
514 // We use the location tag input if:
515 // - The path matches, and
516 // - It's not skipped due to inheritInChildApplications setting.
517 if (StringUtil
.EqualsIgnoreCase(locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
, this._configPath
) &&
518 !ShouldSkipDueToInheritInChildApplications(locationSectionRecord
.SectionXmlInfo
.SkipInChildApps
)) {
519 // add the location input for this section
520 SectionRecord sectionRecord
= EnsureSectionRecord(locationSectionRecord
.ConfigKey
, true);
521 SectionInput sectionInput
= new SectionInput(
522 locationSectionRecord
.SectionXmlInfo
, locationSectionRecord
.ErrorsList
);
524 sectionRecord
.AddLocationInput(sectionInput
);
526 // copy the initialization errors to the record
527 if (locationSectionRecord
.HasErrors
) {
528 this._initErrors
.AddSavedLocalErrors(locationSectionRecord
.Errors
);
534 current
= current
._parent
;
540 if (!IsLocationConfig
) {
542 // If config file exists, open it and parse it once so we can
543 // find what to evaluate later.
545 InitConfigFromFile();
548 // Add location sections for this record as bonafide sections.
549 if (locationSubPathInputs
!= null) {
550 foreach (LocationSectionRecord locationSectionRecord
in locationSubPathInputs
) {
551 // add the file input
552 SectionRecord sectionRecord
= EnsureSectionRecord(locationSectionRecord
.ConfigKey
, true);
553 SectionInput sectionInput
= new SectionInput(locationSectionRecord
.SectionXmlInfo
, locationSectionRecord
.ErrorsList
);
554 sectionRecord
.AddFileInput(sectionInput
);
556 // copy the initialization errors to the record
557 if (locationSectionRecord
.HasErrors
) {
558 this._initErrors
.AddSavedLocalErrors(locationSectionRecord
.Errors
);
565 // Capture all exceptions and include them in initialization errors.
567 catch (Exception e
) {
568 string streamname
= (ConfigStreamInfo
!= null) ? ConfigStreamInfo
.StreamName
: null;
570 _initErrors
.AddError(
571 ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_error_loading_XML_file
), e
, streamname
, 0),
572 ExceptionAction
.Global
);
576 // InitConfigFromFile
578 // Init the Config From the File.
580 private void InitConfigFromFile() {
581 bool implicitSectionsAdded
= false;
584 // If initialization should be delayed, do not read the file.
585 if (ClassFlags
[ClassSupportsDelayedInit
] && Host
.IsInitDelayed(this)) {
586 // determine the root of delayed initialization
587 if (_parent
._initDelayedRoot
== null) {
588 _initDelayedRoot
= this;
591 _initDelayedRoot
= _parent
._initDelayedRoot
;
596 // This parent of a record that is not initDelayed must also
597 // not be initDelayed.
599 Debug
.Assert(!_parent
.IsInitDelayed
, "!_parent.IsInitDelayed");
601 using (Impersonate()) {
604 // Get the stream name. Note that this may be an expensive operation
605 // for the client configuration host, which is why it comes after the
606 // check for delayed init.
608 ConfigStreamInfo
.StreamName
= Host
.GetStreamName(_configPath
);
609 if (!String
.IsNullOrEmpty(ConfigStreamInfo
.StreamName
)) {
610 // Lets listen to the stream
611 ConfigStreamInfo
.StreamVersion
= MonitorStream(null, null, ConfigStreamInfo
.StreamName
);
612 using (Stream stream
= Host
.OpenStreamForRead(ConfigStreamInfo
.StreamName
)) {
613 if (stream
== null) {
614 // There is no stream to load from
618 ConfigStreamInfo
.HasStream
= true;
620 // Determine whether or not to prefetch.
621 _flags
[PrefetchAll
] = Host
.PrefetchAll(_configPath
, ConfigStreamInfo
.StreamName
);
623 using (XmlUtil xmlUtil
= new XmlUtil(stream
, ConfigStreamInfo
.StreamName
, true, _initErrors
)) {
624 ConfigStreamInfo
.StreamEncoding
= xmlUtil
.Reader
.Encoding
;
626 // Read the factories
627 Hashtable factoryList
= ScanFactories(xmlUtil
);
628 _factoryRecords
= factoryList
;
630 // Add implicit sections before scanning sections
631 AddImplicitSections(null);
632 implicitSectionsAdded
= true;
634 // Read the sections themselves
635 if (xmlUtil
.Reader
.Depth
== 1) {
636 ScanSections(xmlUtil
);
645 // Capture all exceptions and include them in _initErrors so execution in Init can continue.
647 catch (XmlException e
) {
649 // Treat invalid XML as unrecoverable by replacing all errors with the XML error.
651 _initErrors
.SetSingleGlobalError(
652 ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_error_loading_XML_file
), e
, ConfigStreamInfo
.StreamName
, 0));
654 catch (Exception e
) {
655 _initErrors
.AddError(
656 ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_error_loading_XML_file
), e
, ConfigStreamInfo
.StreamName
, 0),
657 ExceptionAction
.Global
);
661 // If there are global errors that make all input invalid,
662 // reset our state so that inherited configuration can still be used,
663 // including location sections that apply to this file.
665 if (_initErrors
.HasGlobalErrors
) {
668 // Parsing of a section may have been in progress when the exception
669 // was thrown, so clear any accumulated local errors.
671 _initErrors
.ResetLocalErrors();
674 // Stop monitoring any configSource streams, but still continue
675 // to monitor the main config file if it was successfully monitored.
677 HybridDictionary streamInfos
= null;
679 if (ConfigStreamInfo
.HasStreamInfos
) {
680 streamInfos
= ConfigStreamInfo
.StreamInfos
;
681 ConfigStreamInfo
.ClearStreamInfos();
683 if (!String
.IsNullOrEmpty(ConfigStreamInfo
.StreamName
)) {
684 StreamInfo fileStreamInfo
= (StreamInfo
) streamInfos
[ConfigStreamInfo
.StreamName
];
686 // add this file's streaminfo to the now empty list
687 if (fileStreamInfo
!= null) {
688 streamInfos
.Remove(ConfigStreamInfo
.StreamName
);
689 ConfigStreamInfo
.StreamInfos
.Add(ConfigStreamInfo
.StreamName
, fileStreamInfo
);
695 // Stop monitoring streams outside the lock, to prevent deadlock.
696 if (streamInfos
!= null) {
697 foreach (StreamInfo streamInfo
in streamInfos
.Values
) {
698 if (streamInfo
.IsMonitored
) {
699 Host
.StopMonitoringStreamForChanges(streamInfo
.StreamName
, ConfigStreamInfo
.CallbackDelegate
);
705 if (_sectionRecords
!= null) {
706 List
<SectionRecord
> removes
= null;
707 foreach (SectionRecord sectionRecord
in _sectionRecords
.Values
) {
708 Debug
.Assert(!sectionRecord
.HasIndirectLocationInputs
,
709 "Don't expect to have any IndirectLocationInputs because location config record shouldn't call InitConfigFromFile");
711 if (sectionRecord
.HasLocationInputs
) {
712 // Remove any file input
713 sectionRecord
.RemoveFileInput();
716 // Remove the entire section record
717 if (removes
== null) {
718 removes
= new List
<SectionRecord
>();
721 removes
.Add(sectionRecord
);
725 if (removes
!= null) {
726 foreach (SectionRecord sectionRecord
in removes
) {
727 _sectionRecords
.Remove(sectionRecord
.ConfigKey
);
732 // Remove all location section input defined here
733 if (_locationSections
!= null) {
734 _locationSections
.Clear();
737 // Remove all factory records
738 if (_factoryRecords
!= null) {
739 _factoryRecords
.Clear();
743 if (!implicitSectionsAdded
) {
744 // Always add implicit sections no matter we have a file or not.
745 AddImplicitSections(null);
749 private bool IsInitDelayed
{
751 return _initDelayedRoot
!= null;
755 // RefreshFactoryRecord
757 // Refresh the Factory Record for a particular section.
759 private void RefreshFactoryRecord(string configKey
) {
760 Hashtable factoryList
= null;
761 FactoryRecord factoryRecord
= null;
762 ConfigurationSchemaErrors errors
;
764 errors
= new ConfigurationSchemaErrors();
766 // Get Updated Factory List from File
769 using (Impersonate()) {
770 using (Stream stream
= Host
.OpenStreamForRead(ConfigStreamInfo
.StreamName
)) {
771 if (stream
!= null) {
772 ConfigStreamInfo
.HasStream
= true;
774 using (XmlUtil xmlUtil
= new XmlUtil(stream
, ConfigStreamInfo
.StreamName
, true, errors
)) {
776 factoryList
= ScanFactories(xmlUtil
);
777 ThrowIfParseErrors(xmlUtil
.SchemaErrors
);
780 lineNumber
= xmlUtil
.LineNumber
;
788 // Add implicit sections to the factory list
789 if (factoryList
== null) {
790 // But if factoryList isn't found in this config, we still try to
791 // add implicit sections to an empty factoryList.
792 factoryList
= new Hashtable();
795 AddImplicitSections(factoryList
);
797 if (factoryList
!= null) {
798 factoryRecord
= (FactoryRecord
) factoryList
[configKey
];
802 // Guarantee that exceptions contain the name of the stream and an approximate line number if available.
804 // And don't allow frames up the stack to run exception filters while impersonated.
805 catch (Exception e
) {
807 ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_error_loading_XML_file
), e
, ConfigStreamInfo
.StreamName
, lineNumber
),
808 ExceptionAction
.Global
);
812 // Set/Add/Remove Record
813 // Note that the _factoryRecords hashtable is protected by the hierarchy lock.
814 if (factoryRecord
!= null || HasFactoryRecords
) {
815 EnsureFactories()[configKey
] = factoryRecord
;
818 // Throw accumulated errors
819 ThrowIfParseErrors(errors
);
822 internal IInternalConfigHost Host
{
823 get {return _configRoot.Host;}
826 internal IInternalConfigurationBuilderHost ConfigBuilderHost
{
827 get { return _configRoot.ConfigBuilderHost; }
830 internal BaseConfigurationRecord Parent
{
831 get {return _parent;}
834 internal bool IsRootConfig
{
835 get {return _parent == null;}
838 internal bool IsMachineConfig
{
839 get {return _parent == _configRoot.RootConfigRecord;}
842 internal string LocationSubPath
{
843 get {return _locationSubPath;}
846 internal bool IsLocationConfig
{
847 get {return _locationSubPath != null;}
850 protected ConfigRecordStreamInfo ConfigStreamInfo
{
852 if (IsLocationConfig
) {
853 return _parent
._configStreamInfo
;
856 return _configStreamInfo
;
861 private object GetSection(string configKey
, bool getLkg
, bool checkPermission
) {
863 object resultRuntimeObject
;
866 // Note that GetSectionRecursive may invalidate this record,
867 // so there should be no further references to 'this' after the call.
870 configKey
, getLkg
, checkPermission
, true /* getRuntimeObject */, true /* requestIsHere */,
871 out result
, out resultRuntimeObject
);
873 return resultRuntimeObject
;
876 private void GetSectionRecursive(
877 string configKey
, bool getLkg
, bool checkPermission
, bool getRuntimeObject
, bool requestIsHere
,
878 out object result
, out object resultRuntimeObject
) {
881 resultRuntimeObject
= null;
884 Debug
.Assert(requestIsHere
|| !checkPermission
, "requestIsHere || !checkPermission");
886 Debug
.Assert(getRuntimeObject
== true, "getRuntimeObject == true");
887 Debug
.Assert(requestIsHere
== true, "requestIsHere == true");
892 // Store results in temporary variables, because we don't want to return
893 // results if an exception is thrown by CheckPermissionAllowed.
895 object tmpResult
= null;
896 object tmpResultRuntimeObject
= null;
897 bool requirePermission
= true;
898 bool isResultTrustedWithoutAptca
= true;
900 // Throw errors from initial parse, if any.
906 // check for a cached result
908 bool hasResult
= false;
909 SectionRecord sectionRecord
= GetSectionRecord(configKey
, getLkg
);
910 if (sectionRecord
!= null && sectionRecord
.HasResult
) {
911 // Results should never be stored if the section has errors.
912 Debug
.Assert(!sectionRecord
.HasErrors
, "!sectionRecord.HasErrors");
914 // Create the runtime object if requested and does not yet exist.
915 if (getRuntimeObject
&& !sectionRecord
.HasResultRuntimeObject
) {
917 sectionRecord
.ResultRuntimeObject
= GetRuntimeObject(sectionRecord
.Result
);
921 // Ignore the error if we are attempting to retreive
922 // the last known good configuration.
930 // Get the cached result.
931 if (!getRuntimeObject
|| sectionRecord
.HasResultRuntimeObject
) {
932 requirePermission
= sectionRecord
.RequirePermission
;
933 isResultTrustedWithoutAptca
= sectionRecord
.IsResultTrustedWithoutAptca
;
934 tmpResult
= sectionRecord
.Result
;
935 if (getRuntimeObject
) {
936 tmpResultRuntimeObject
= sectionRecord
.ResultRuntimeObject
;
944 // If there is no cached result, get the parent's section,
945 // then merge it with our own input if we have any.
948 FactoryRecord factoryRecord
= null;
949 bool hasInput
= (sectionRecord
!= null && sectionRecord
.HasInput
);
952 // We want to cache results in a section record if:
953 // - The request is made at this level, and so is likely to be
956 // - The section has input, in which case we want to
957 // avoid evaluating the same input multiple times.
959 bool cacheResults
= (requestIsHere
|| hasInput
);
961 bool isRootDeclaration
;
964 // We need to get a factory record to:
965 // - Check whether the caller has permission to access a section.
966 // - Determine if this is the root declaration of a config section,
967 // and thus the termination point for recursion.
968 // - Get a factory that can create a configuration section.
970 // Since most factories will be declared in machine.config and not in
971 // child config files, we do not optimize for checking whether a
972 // factory record is the root declaration, as the calculation at
973 // machine.config is trivial.
975 // It WILL be common in web scenarios for there to be
976 // deep hierarchies of config files, most of which have
977 // sparse input. Therefore we do not want to retreive a
978 // factory record if it is not necessary to do so, as
979 // it would always lead to an order N-squared operation,
980 // where N is the depth of the config hierarchy.
982 // We can skip the reteival of a factory record if:
983 // - This is the recursive call to GetSectionRecursive,
985 // - There is no section input at this level,
987 // - No factory is declared at this level.
989 // In this case, we'll simply continue the recursion to our parent.
993 // Ensure that we have a valid factory record and a valid factory
994 // for creating sections when a request for a section is first
997 factoryRecord
= FindAndEnsureFactoryRecord(configKey
, out isRootDeclaration
);
1000 // If initialization is delayed, complete initialization if:
1001 // - We can't find the requested factory, and it therefore
1002 // may be in the file we haven't yet read,
1004 // - The definition of that factory is allowed at
1005 // levels of the config hierarchy that have not
1006 // been initialized.
1008 // This works for client config scenarios because the default
1009 // for AllowExeDefinition is MachineToApplication. It would not
1010 // be useful for Web scenarios, as most sections can be requested
1013 // Note that configuration errors that may be present in the
1014 // file where intialization is delayed will be ignored, and
1015 // thus the order in which configuration sections are requested
1016 // will affect results. This is considered OK as it is very
1017 // expensive to determine configuration paths to
1018 // client user configuration files, which aren't needed by
1019 // most applications.
1022 && ( factoryRecord
== null
1023 || _initDelayedRoot
.IsDefinitionAllowed(factoryRecord
.AllowDefinition
, factoryRecord
.AllowExeDefinition
))) {
1026 // We are going to remove this record, so get any data we need
1027 // before the reference to 'this' becomes invalid.
1029 string configPath
= this._configPath
;
1030 InternalConfigRoot configRoot
= this._configRoot
;
1032 // Tell the host to no longer permit delayed initialization.
1033 Host
.RequireCompleteInit(_initDelayedRoot
);
1035 // Removed config at the root of where initialization is delayed.
1036 _initDelayedRoot
.Remove();
1038 // Get the config record for this config path
1039 BaseConfigurationRecord newRecord
= (BaseConfigurationRecord
) configRoot
.GetConfigRecord(configPath
);
1041 // Repeat the call to GetSectionRecursive
1042 newRecord
.GetSectionRecursive(
1043 configKey
, getLkg
, checkPermission
,
1044 getRuntimeObject
, requestIsHere
,
1045 out result
, out resultRuntimeObject
);
1047 // Return and make no more references to this record.
1052 // For compatibility with previous versions,
1053 // return null if the section is not found
1056 if (factoryRecord
== null || factoryRecord
.IsGroup
) {
1061 // Use the factory record's copy of the configKey,
1062 // so that we don't store more than one instance
1063 // of the same configKey.
1065 configKey
= factoryRecord
.ConfigKey
;
1067 else if (hasInput
) {
1069 // We'll need a factory to evaluate the input.
1071 factoryRecord
= FindAndEnsureFactoryRecord(configKey
, out isRootDeclaration
);
1072 Debug
.Assert(factoryRecord
!= null, "factoryRecord != null");
1076 // We don't need a factory record unless this is the root declaration.
1077 // We know it is not the root declaration if there is no factory
1078 // declared here. This is important to avoid a walk up the config
1079 // hierachy when there is no input in this record.
1081 factoryRecord
= GetFactoryRecord(configKey
, false);
1082 if (factoryRecord
== null) {
1083 isRootDeclaration
= false;
1086 factoryRecord
= FindAndEnsureFactoryRecord(configKey
, out isRootDeclaration
);
1087 Debug
.Assert(factoryRecord
!= null, "factoryRecord != null");
1091 // We need a factory record to check permission.
1092 Debug
.Assert(!checkPermission
|| factoryRecord
!= null, "!checkPermission || factoryRecord != null");
1095 // If this is the root declaration, then we always want to cache
1096 // the result, in order to prevent the section default from being
1097 // created multiple times.
1099 if (isRootDeclaration
) {
1100 cacheResults
= true;
1104 // We'll need a section record to cache results,
1105 // and maybe to use in creating the section default.
1107 if (sectionRecord
== null && cacheResults
) {
1108 sectionRecord
= EnsureSectionRecord(configKey
, true);
1112 // Retrieve the parent's runtime object if the runtimeObject
1113 // is requested, and we are not going to merge that input
1114 // with input in this section.
1116 bool getParentRuntimeObject
= (getRuntimeObject
&& !hasInput
);
1118 object parentResult
= null;
1119 object parentResultRuntimeObject
= null;
1120 if (isRootDeclaration
) {
1122 // Create the default section.
1124 // Use the existing section record to create it if there is no input,
1125 // so that the cached result is attached to the correct record.
1127 SectionRecord sectionRecordForDefault
= (hasInput
) ? null : sectionRecord
;
1128 CreateSectionDefault(configKey
, getParentRuntimeObject
, factoryRecord
, sectionRecordForDefault
,
1129 out parentResult
, out parentResultRuntimeObject
);
1133 // Get the parent section.
1135 _parent
.GetSectionRecursive(
1136 configKey
, false /* getLkg */, false /* checkPermission */,
1137 getParentRuntimeObject
, false /* requestIsHere */,
1138 out parentResult
, out parentResultRuntimeObject
);
1143 // Evaluate the input.
1145 // If Evaluate() encounters an error, it may not throw an exception
1146 // when getLkg == true.
1148 // The complete success of the evaluation is determined by the return value.
1150 bool success
= Evaluate(factoryRecord
, sectionRecord
, parentResult
, getLkg
, getRuntimeObject
,
1151 out tmpResult
, out tmpResultRuntimeObject
);
1153 Debug
.Assert(success
|| getLkg
, "success || getLkg");
1156 Debug
.Assert(getLkg
== true, "getLkg == true");
1157 // Do not cache partial results if getLkg was specified.
1158 cacheResults
= false;
1163 // If we are going to cache results here, we will need
1164 // to create a copy in the case of MgmtConfigurationRecord -
1165 // otherwise we could inadvertently return the parent to the user,
1166 // which could then be modified.
1168 if (sectionRecord
!= null) {
1169 tmpResult
= UseParentResult(configKey
, parentResult
, sectionRecord
);
1170 if (getRuntimeObject
) {
1172 // If the parent result is the same as the parent runtime object,
1173 // then use the same copy of the parent result for our own runtime object.
1175 if (object.ReferenceEquals(parentResult
, parentResultRuntimeObject
)) {
1176 tmpResultRuntimeObject
= tmpResult
;
1179 tmpResultRuntimeObject
= UseParentResult(configKey
, parentResultRuntimeObject
, sectionRecord
);
1184 Debug
.Assert(!requestIsHere
, "!requestIsHere");
1187 // We don't need to make a copy if we are not storing
1188 // the result, and thus not returning the result to the
1189 // caller of GetSection.
1191 tmpResult
= parentResult
;
1192 tmpResultRuntimeObject
= parentResultRuntimeObject
;
1197 // Determine which permissions are required of the caller.
1199 if (cacheResults
|| checkPermission
) {
1200 requirePermission
= factoryRecord
.RequirePermission
;
1201 isResultTrustedWithoutAptca
= factoryRecord
.IsFactoryTrustedWithoutAptca
;
1204 // Cache the results.
1207 if (sectionRecord
== null) {
1208 sectionRecord
= EnsureSectionRecord(configKey
, true);
1211 sectionRecord
.Result
= tmpResult
;
1212 if (getRuntimeObject
) {
1213 sectionRecord
.ResultRuntimeObject
= tmpResultRuntimeObject
;
1216 sectionRecord
.RequirePermission
= requirePermission
;
1217 sectionRecord
.IsResultTrustedWithoutAptca
= isResultTrustedWithoutAptca
;
1225 // Ignore the error if we are attempting to retreive
1226 // the last known good configuration.
1234 // If we don't have a result, ask our parent for its
1235 // last known good result.
1238 Debug
.Assert(getLkg
== true, "getLkg == true");
1240 _parent
.GetSectionRecursive(
1241 configKey
, true /* getLkg */, checkPermission
,
1242 true /* getRuntimeObject */, true /* requestIsHere */,
1243 out result
, out resultRuntimeObject
);
1250 // Check if permission to access the section is allowed.
1252 if (checkPermission
) {
1253 CheckPermissionAllowed(configKey
, requirePermission
, isResultTrustedWithoutAptca
);
1257 // Return the results.
1260 if (getRuntimeObject
) {
1261 resultRuntimeObject
= tmpResultRuntimeObject
;
1265 protected void CreateSectionDefault(
1266 string configKey
, bool getRuntimeObject
, FactoryRecord factoryRecord
, SectionRecord sectionRecord
,
1267 out object result
, out object resultRuntimeObject
) {
1270 resultRuntimeObject
= null;
1272 SectionRecord sectionRecordForDefault
;
1273 if (sectionRecord
!= null) {
1274 sectionRecordForDefault
= sectionRecord
;
1277 sectionRecordForDefault
= new SectionRecord(configKey
);
1280 object tmpResult
= CallCreateSection(true, factoryRecord
, sectionRecordForDefault
, null, null, null);
1281 object tmpResultRuntimeObject
;
1282 if (getRuntimeObject
) {
1283 tmpResultRuntimeObject
= GetRuntimeObject(tmpResult
);
1286 tmpResultRuntimeObject
= null;
1290 resultRuntimeObject
= tmpResultRuntimeObject
;
1295 // Create a section that is used to manipulate a section that has errors.
1297 private DefaultSection
CreateErrorSection(
1298 string configKey
, FactoryRecord factoryRecord
, SectionRecord sectionRecord
, SectionInput input
, Exception e
) {
1301 // We make assumptions in this implemntation that CreateErrorSection is called
1302 // only for MgmtConfigurationRecord.
1304 Debug
.Assert(this is MgmtConfigurationRecord
, "this is MgmtConfigurationRecord");
1308 string factoryTypeName
;
1310 ConfigurationAllowDefinition allowDefinition
;
1311 ConfigurationAllowExeDefinition allowExeDefinition
;
1312 bool restartOnExternalChanges
;
1313 bool requirePermission
;
1314 bool isFromTrustedConfigRecord
;
1315 string factoryFilename
;
1316 int factoryLineNumber
;
1318 // The original factory record will contain either valid values,
1319 // or the default values in places where there was an error.
1320 if (factoryRecord
!= null) {
1321 configKey
= factoryRecord
.ConfigKey
;
1322 group = factoryRecord
.Group
;
1323 name
= factoryRecord
.Name
;
1324 factoryTypeName
= factoryRecord
.FactoryTypeName
;
1325 allowLocation
= factoryRecord
.AllowLocation
;
1326 allowDefinition
= factoryRecord
.AllowDefinition
;
1327 allowExeDefinition
= factoryRecord
.AllowExeDefinition
;
1328 restartOnExternalChanges
= factoryRecord
.RestartOnExternalChanges
;
1329 requirePermission
= factoryRecord
.RequirePermission
;
1330 isFromTrustedConfigRecord
= factoryRecord
.IsFromTrustedConfigRecord
;
1331 factoryFilename
= factoryRecord
.Filename
;
1332 factoryLineNumber
= factoryRecord
.LineNumber
;
1335 SplitConfigKey(configKey
, out group, out name
);
1336 factoryTypeName
= typeof(DefaultSection
).AssemblyQualifiedName
;
1337 allowLocation
= true;
1338 allowDefinition
= ConfigurationAllowDefinition
.Everywhere
;
1339 allowExeDefinition
= ConfigurationAllowExeDefinition
.MachineToApplication
;
1340 restartOnExternalChanges
= true;
1341 requirePermission
= true;
1342 isFromTrustedConfigRecord
= false;
1343 factoryFilename
= null;
1344 factoryLineNumber
= -1;
1347 // create a new factory record
1348 FactoryRecord factoryRecordForCreate
= new FactoryRecord(
1356 restartOnExternalChanges
,
1358 isFromTrustedConfigRecord
,
1363 // Create the factory needed to create the section.
1365 Debug
.Assert(this is MgmtConfigurationRecord
, "this is MgmtConfigurationRecord");
1366 ConstructorInfo ctor
= TypeUtil
.GetConstructorWithReflectionPermission(typeof(DefaultSection
), typeof(ConfigurationSection
), true);
1367 factoryRecordForCreate
.Factory
= ctor
;
1368 factoryRecordForCreate
.IsFactoryTrustedWithoutAptca
= false;
1371 // Try to get the section content as raw xml.
1373 ConfigXmlReader configXmlReader
= null;
1374 string filename
= null;
1375 int lineNumber
= -1;
1376 if (input
!= null) {
1377 filename
= input
.SectionXmlInfo
.Filename
;
1378 lineNumber
= input
.SectionXmlInfo
.LineNumber
;
1379 string [] keys
= sectionRecord
.ConfigKey
.Split(ConfigPathSeparatorParams
);
1381 configXmlReader
= GetSectionXmlReader(keys
, input
);
1387 DefaultSection section
= (DefaultSection
) CallCreateSection(
1388 isFromTrustedConfigRecord
, factoryRecordForCreate
, sectionRecord
,
1389 null /* parentConfig */, configXmlReader
, filename
, lineNumber
);
1391 section
.ElementInformation
.SetError(configKey
, e
);
1398 // Check if an input should be skipped based on inheritInChildApplications setting.
1400 // If skipInChildApps == true (it means inheritInChildApplications=="false" in the location tag):
1402 // - If _flags[IsAboveApplication]==true, that means the app pointed to by _configPath is above of the
1403 // current running app. In another word, the running app is a child app of the app pointed to by _configPath.
1404 // In this case, we should skip the input.
1406 // - If _flags[IsAboveApplication]==false, that means the app pointed to by _configPath == current running app.
1407 // In this case it's okay to use the input.
1409 private bool ShouldSkipDueToInheritInChildApplications(bool skipInChildApps
) {
1410 return (skipInChildApps
&& _flags
[IsAboveApplication
]);
1413 // configPath - The config path of the config record to which a location tag points to.
1414 private bool ShouldSkipDueToInheritInChildApplications(bool skipInChildApps
, string configPath
) {
1415 return (skipInChildApps
&& Host
.IsAboveApplication(configPath
));
1420 // Evaluate the input.
1422 // If Evaluate() encounters an error, it may not throw an exception
1423 // when getLkg == true.
1425 // The complete success of the evaluation is determined by the return value.
1427 private bool Evaluate(
1428 FactoryRecord factoryRecord
, SectionRecord sectionRecord
, object parentResult
,
1429 bool getLkg
, bool getRuntimeObject
, out object result
, out object resultRuntimeObject
) {
1432 resultRuntimeObject
= null;
1435 // Store results in temporary variables, because we don't want to return
1436 // results if an exception is thrown.
1438 object tmpResult
= null;
1439 object tmpResultRuntimeObject
= null;
1441 // Factory record should be error-free.
1442 Debug
.Assert(!factoryRecord
.HasErrors
, "!factoryRecord.HasErrors");
1444 // We should have some input
1445 Debug
.Assert(sectionRecord
.HasInput
, "sectionRecord.HasInput");
1448 // Grab inputs before checking result,
1449 // as inputs may be cleared once the result is set.
1451 List
<SectionInput
> locationInputs
= sectionRecord
.LocationInputs
;
1452 List
<SectionInput
> indirectLocationInputs
= sectionRecord
.IndirectLocationInputs
;
1453 SectionInput fileInput
= sectionRecord
.FileInput
;
1455 Debug
.Assert(!(IsLocationConfig
&& getLkg
),
1456 "Don't expect getLkg to be true when we're dealing with a location config record");
1459 // Now that we have our inputs, lets check if there
1460 // is a result, because if there is, our inputs might
1461 // have been invalidated.
1463 bool success
= false;
1464 if (sectionRecord
.HasResult
) {
1465 // Results should never be stored if the section has errors.
1466 Debug
.Assert(!sectionRecord
.HasErrors
, "!sectionRecord.HasErrors");
1468 // Create the runtime object if requested and it does not yet exist.
1469 if (getRuntimeObject
&& !sectionRecord
.HasResultRuntimeObject
) {
1471 sectionRecord
.ResultRuntimeObject
= GetRuntimeObject(sectionRecord
.Result
);
1475 // Ignore the error if we are attempting to retreive
1476 // the last known good configuration.
1484 // Get the cached result.
1485 if (!getRuntimeObject
|| sectionRecord
.HasResultRuntimeObject
) {
1486 tmpResult
= sectionRecord
.Result
;
1487 if (getRuntimeObject
) {
1488 tmpResultRuntimeObject
= sectionRecord
.ResultRuntimeObject
;
1496 Exception savedException
= null;
1498 string configKey
= factoryRecord
.ConfigKey
;
1499 string [] keys
= configKey
.Split(ConfigPathSeparatorParams
);
1500 object currentResult
= parentResult
;
1503 // Evaluate indirectLocationInputs. Used only in location config record.
1504 // See the comment for VSWhidbey 540184 in Init() for more details.
1506 if (indirectLocationInputs
!= null) {
1507 Debug
.Assert(IsLocationConfig
, "indirectLocationInputs is non-null only in location config record");
1508 foreach (SectionInput input
in indirectLocationInputs
) {
1509 if (!input
.HasResult
) {
1510 input
.ThrowOnErrors();
1511 bool isTrusted
= Host
.IsTrustedConfigPath(input
.SectionXmlInfo
.DefinitionConfigPath
);
1512 input
.Result
= EvaluateOne(keys
, input
, isTrusted
, factoryRecord
, sectionRecord
, currentResult
);
1515 currentResult
= input
.Result
;
1520 // Evaluate location inputs
1522 if (locationInputs
!= null) {
1523 foreach (SectionInput locationInput
in locationInputs
) {
1524 if (!locationInput
.HasResult
) {
1525 locationInput
.ThrowOnErrors();
1526 bool isTrusted
= Host
.IsTrustedConfigPath(locationInput
.SectionXmlInfo
.DefinitionConfigPath
);
1527 locationInput
.Result
= EvaluateOne(keys
, locationInput
, isTrusted
, factoryRecord
, sectionRecord
, currentResult
);
1530 currentResult
= locationInput
.Result
;
1535 // Evaluate file input
1537 if (fileInput
!= null) {
1538 if (!fileInput
.HasResult
) {
1539 fileInput
.ThrowOnErrors();
1540 bool isTrusted
= _flags
[IsTrusted
];
1541 fileInput
.Result
= EvaluateOne(keys
, fileInput
, isTrusted
, factoryRecord
, sectionRecord
, currentResult
);
1544 currentResult
= fileInput
.Result
;
1548 // The section needs its own copy of the result that is distinct
1549 // from its location parent result.
1551 // Please note that this is needed only in design-time because any
1552 // change to the section shouldn't go to the locationInput.Result.
1554 // In runtime, UseParentResult does nothing.
1556 Debug
.Assert(locationInputs
!= null || indirectLocationInputs
!= null,
1557 "locationInputs != null || indirectLocationInputs != null");
1558 currentResult
= UseParentResult(configKey
, currentResult
, sectionRecord
);
1561 if (getRuntimeObject
) {
1562 tmpResultRuntimeObject
= GetRuntimeObject(currentResult
);
1565 tmpResult
= currentResult
;
1568 catch (Exception e
) {
1570 // Catch the exception if LKG is requested and we have
1571 // location input to fall back on.
1573 if (getLkg
&& locationInputs
!= null) {
1583 // If getLkg, then return a result from the last valid location input.
1586 Debug
.Assert(getLkg
== true, "getLkg == true");
1588 int i
= locationInputs
.Count
;
1590 SectionInput locationInput
= locationInputs
[i
];
1591 if (locationInput
.HasResult
) {
1592 if (getRuntimeObject
&& !locationInput
.HasResultRuntimeObject
) {
1594 locationInput
.ResultRuntimeObject
= GetRuntimeObject(locationInput
.Result
);
1600 if (!getRuntimeObject
|| locationInput
.HasResultRuntimeObject
) {
1601 tmpResult
= locationInput
.Result
;
1602 if (getRuntimeObject
) {
1603 tmpResultRuntimeObject
= locationInput
.ResultRuntimeObject
;
1612 throw savedException
;
1618 // If evaluation was successful, we can remove any saved rawXml.
1620 if (success
&& !_flags
[SupportsKeepInputs
]) {
1621 sectionRecord
.ClearRawXml();
1625 if (getRuntimeObject
) {
1626 resultRuntimeObject
= tmpResultRuntimeObject
;
1632 private object EvaluateOne(
1633 string[] keys
, SectionInput input
, bool isTrusted
,
1634 FactoryRecord factoryRecord
, SectionRecord sectionRecord
, object parentResult
) {
1638 ConfigXmlReader reader
= GetSectionXmlReader(keys
, input
);
1639 if (reader
== null) {
1641 // If section is not found in a file, use the parent result
1643 result
= UseParentResult(factoryRecord
.ConfigKey
, parentResult
, sectionRecord
);
1646 result
= CallCreateSection(isTrusted
, factoryRecord
, sectionRecord
, input
, parentResult
, reader
);
1649 catch (Exception e
) {
1650 throw ExceptionUtil
.WrapAsConfigException(
1651 SR
.GetString(SR
.Config_exception_creating_section
, factoryRecord
.ConfigKey
),
1652 e
, input
.SectionXmlInfo
);
1659 // Create a single cached instance of UnrestrictedConfigPermission.
1661 private static ConfigurationPermission UnrestrictedConfigPermission
{
1663 if (s_unrestrictedConfigPermission
== null) {
1664 s_unrestrictedConfigPermission
= new ConfigurationPermission(PermissionState
.Unrestricted
);
1667 return s_unrestrictedConfigPermission
;
1672 // Check whether permission to the section is allowed to the caller.
1674 private void CheckPermissionAllowed(string configKey
, bool requirePermission
, bool isTrustedWithoutAptca
) {
1676 // Demand unrestricted ConfigurationPermission if the section requires it
1678 if (requirePermission
) {
1681 UnrestrictedConfigPermission
.Demand();
1683 catch (SecurityException e
) {
1685 // Add a nice error message that includes the sectionName and explains
1686 // how to use the requirePermission attribute.
1688 throw new SecurityException(
1689 SR
.GetString(SR
.ConfigurationPermission_Denied
, configKey
),
1695 // Ensure that the recepient isn't receiving an object they otherwise
1696 // wouldn't be able to create due to Aptca.
1698 if (isTrustedWithoutAptca
&& !Host
.IsFullTrustSectionWithoutAptcaAllowed(this)) {
1699 throw new ConfigurationErrorsException(SR
.GetString(SR
.Section_from_untrusted_assembly
, configKey
));
1704 private ConfigXmlReader
FindSection(string [] keys
, SectionXmlInfo sectionXmlInfo
, out int lineNumber
) {
1706 ConfigXmlReader section
= null;
1708 using (Impersonate()) {
1709 using (Stream stream
= Host
.OpenStreamForRead(sectionXmlInfo
.Filename
)) {
1710 if ( !_flags
[SupportsRefresh
]
1711 && (stream
== null || HasStreamChanged(sectionXmlInfo
.Filename
, sectionXmlInfo
.StreamVersion
))) {
1713 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_file_has_changed
), sectionXmlInfo
.Filename
, 0);
1716 if (stream
!= null) {
1718 using (XmlUtil xmlUtil
= new XmlUtil(stream
, sectionXmlInfo
.Filename
, true)) {
1719 if (sectionXmlInfo
.SubPath
== null) {
1720 section
= FindSectionRecursive(keys
, 0, xmlUtil
, ref lineNumber
);
1723 // search children of <configuration> for <location>
1724 xmlUtil
.ReadToNextElement();
1725 while (xmlUtil
.Reader
.Depth
> 0) {
1726 if (xmlUtil
.Reader
.Name
== KEYWORD_LOCATION
) {
1727 bool locationValid
= false;
1728 string locationSubPathAttribute
= xmlUtil
.Reader
.GetAttribute(KEYWORD_LOCATION_PATH
);
1731 locationSubPathAttribute
= NormalizeLocationSubPath(locationSubPathAttribute
, xmlUtil
);
1732 locationValid
= true;
1734 catch (ConfigurationException ce
) {
1735 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.NonSpecific
);
1738 if (locationValid
&&
1739 StringUtil
.EqualsIgnoreCase(sectionXmlInfo
.SubPath
, locationSubPathAttribute
)) {
1741 section
= FindSectionRecursive(keys
, 0, xmlUtil
, ref lineNumber
);
1742 if (section
!= null)
1747 xmlUtil
.SkipToNextElement();
1751 // Throw accumulated errors
1752 ThrowIfParseErrors(xmlUtil
.SchemaErrors
);
1758 // Don't allow frames up the stack to run exception filters while impersonated.
1766 private ConfigXmlReader
FindSectionRecursive(string [] keys
, int iKey
, XmlUtil xmlUtil
, ref int lineNumber
) {
1767 string name
= keys
[iKey
];
1768 ConfigXmlReader section
= null;
1770 int depth
= xmlUtil
.Reader
.Depth
;
1771 xmlUtil
.ReadToNextElement();
1773 while (xmlUtil
.Reader
.Depth
> depth
) {
1774 if (xmlUtil
.Reader
.Name
== name
) {
1775 if (iKey
< keys
.Length
- 1) {
1777 // We haven't reached the section yet, so keep evaluating
1779 section
= FindSectionRecursive(keys
, iKey
+ 1, xmlUtil
, ref lineNumber
);
1780 if (section
!= null) {
1784 continue; // don't call "Skip" -- FindSectionRecursive forwards the reader
1788 // We've reached the section. Load the section into a string.
1790 string filename
= ((IConfigErrorInfo
)xmlUtil
).Filename
;
1791 int lineOffset
= xmlUtil
.Reader
.LineNumber
;
1792 string rawXml
= xmlUtil
.CopySection();
1793 section
= new ConfigXmlReader(rawXml
, filename
, lineOffset
);
1797 else if (iKey
== 0 && xmlUtil
.Reader
.Name
== KEYWORD_LOCATION
) {
1798 string locationSubPath
= xmlUtil
.Reader
.GetAttribute(KEYWORD_LOCATION_PATH
);
1799 bool isValid
= false;
1801 locationSubPath
= NormalizeLocationSubPath(locationSubPath
, xmlUtil
);
1804 catch (ConfigurationException ce
) {
1805 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.NonSpecific
);
1808 if (isValid
&& locationSubPath
== null) {
1810 // Location sections that don't have a subpath are treated
1811 // as ordinary sections.
1813 section
= FindSectionRecursive(keys
, iKey
, xmlUtil
, ref lineNumber
);
1814 if (section
!= null) {
1818 continue; // don't call "Skip" -- FindSectionRecursive forwards the reader
1822 xmlUtil
.SkipToNextElement();
1828 private ConfigXmlReader
LoadConfigSource(string name
, SectionXmlInfo sectionXmlInfo
) {
1829 string configSourceStreamName
= sectionXmlInfo
.ConfigSourceStreamName
;
1832 using (Impersonate()) {
1833 using (Stream stream
= Host
.OpenStreamForRead(configSourceStreamName
)) {
1834 if (stream
== null) {
1835 throw new ConfigurationErrorsException(
1836 SR
.GetString(SR
.Config_cannot_open_config_source
, sectionXmlInfo
.ConfigSource
),
1840 using (XmlUtil xmlUtil
= new XmlUtil(stream
, configSourceStreamName
, true)) {
1841 if (xmlUtil
.Reader
.Name
!= name
) {
1842 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_file_format
), xmlUtil
);
1845 // Check for protectionProvider
1846 string protectionProviderAttribute
= xmlUtil
.Reader
.GetAttribute(KEYWORD_PROTECTION_PROVIDER
);
1847 if (protectionProviderAttribute
!= null) {
1848 if (xmlUtil
.Reader
.AttributeCount
!= 1) {
1849 // Error: elements with protectionProvider should not have other attributes
1850 throw new ConfigurationErrorsException(SR
.GetString(SR
.Protection_provider_syntax_error
), xmlUtil
);
1853 sectionXmlInfo
.ProtectionProviderName
= ValidateProtectionProviderAttribute(protectionProviderAttribute
, xmlUtil
);
1856 // Check for configBuilder
1857 string configBuilderAttribute
= xmlUtil
.Reader
.GetAttribute(KEYWORD_CONFIG_BUILDER
);
1858 if (configBuilderAttribute
!= null) {
1859 sectionXmlInfo
.ConfigBuilderName
= ValidateConfigBuilderAttribute(configBuilderAttribute
, xmlUtil
);
1862 int lineOffset
= xmlUtil
.Reader
.LineNumber
;
1863 string rawXml
= xmlUtil
.CopySection();
1865 // Detect if there is any XML left over after the section
1866 while (!xmlUtil
.Reader
.EOF
) {
1867 XmlNodeType t
= xmlUtil
.Reader
.NodeType
;
1868 if (t
!= XmlNodeType
.Comment
) {
1869 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_file_format
), xmlUtil
);
1872 xmlUtil
.Reader
.Read();
1875 ConfigXmlReader section
= new ConfigXmlReader(rawXml
, configSourceStreamName
, lineOffset
);
1882 // Don't allow frames up the stack to run exception filters while impersonated.
1887 protected ConfigXmlReader
GetSectionXmlReader(string[] keys
, SectionInput input
) {
1888 ConfigXmlReader reader
= null;
1889 string filename
= input
.SectionXmlInfo
.Filename
;
1890 int lineNumber
= input
.SectionXmlInfo
.LineNumber
;
1893 string name
= keys
[keys
.Length
-1];
1894 string rawXml
= input
.SectionXmlInfo
.RawXml
;
1895 if (rawXml
!= null) {
1896 // Use the stored raw xml to provide the content of the section.
1897 reader
= new ConfigXmlReader(rawXml
, input
.SectionXmlInfo
.Filename
, input
.SectionXmlInfo
.LineNumber
);
1899 else if (!String
.IsNullOrEmpty(input
.SectionXmlInfo
.ConfigSource
)) {
1900 // Load the config source to provide the content of the section.
1901 filename
= input
.SectionXmlInfo
.ConfigSourceStreamName
;
1903 reader
= LoadConfigSource(name
, input
.SectionXmlInfo
);
1906 // Find the content of the section in the config file.
1908 reader
= FindSection(keys
, input
.SectionXmlInfo
, out lineNumber
);
1911 // Decrypt protected sections
1912 if (reader
!= null) {
1913 if (!input
.IsProtectionProviderDetermined
) {
1914 input
.ProtectionProvider
= GetProtectionProviderFromName(input
.SectionXmlInfo
.ProtectionProviderName
, false);
1917 if (input
.ProtectionProvider
!= null) {
1918 reader
= DecryptConfigSection(reader
, input
.ProtectionProvider
);
1922 // Allow configBuilder a chance to modify
1923 if (reader
!= null) {
1924 if (!input
.IsConfigBuilderDetermined
&& !String
.IsNullOrWhiteSpace(input
.SectionXmlInfo
.ConfigBuilderName
)) {
1925 input
.ConfigBuilder
= GetConfigBuilderFromName(input
.SectionXmlInfo
.ConfigBuilderName
);
1928 if (input
.IsConfigBuilderDetermined
) {
1929 // Decrypt removes the invalid "configProtectionProvider" attribute simply by returning an xml reader for the
1930 // decrypted inner xml... which persumably does not have the attribute. ConfigBuilders OTOH, will presumably
1931 // return an xml reader based on this whole section as is. And the "configBuilders" attribute will be an
1932 // "unrecognized attribute" further down the road. So let's remove it here.
1933 XmlDocument doc
= new XmlDocument();
1934 doc
.PreserveWhitespace
= true;
1935 doc
.LoadXml(reader
.RawXml
);
1936 doc
.DocumentElement
.RemoveAttribute(KEYWORD_CONFIG_BUILDER
);
1937 reader
= new ConfigXmlReader(doc
.DocumentElement
.OuterXml
, filename
, lineNumber
);
1940 if (input
.ConfigBuilder
!= null) {
1941 reader
= ProcessRawXml(reader
, input
.ConfigBuilder
);
1947 // Guarantee that exceptions contain the name of the stream and an approximate line number.
1949 catch (Exception e
) {
1950 throw ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_error_loading_XML_file
), e
, filename
, lineNumber
);
1956 internal string DefaultProviderName
{
1958 return ProtectedConfig
.DefaultProvider
;
1962 internal ProtectedConfigurationProvider
GetProtectionProviderFromName(string providerName
, bool throwIfNotFound
) {
1963 ProtectedConfigurationProvider provider
= null;
1965 if (String
.IsNullOrEmpty(providerName
)) {
1966 if (throwIfNotFound
) {
1967 throw new ConfigurationErrorsException(SR
.GetString(SR
.ProtectedConfigurationProvider_not_found
, providerName
));
1974 provider
= ProtectedConfig
.GetProviderFromName(providerName
);
1979 private ProtectedConfigurationSection ProtectedConfig
{
1981 if (!_flags
[ProtectedDataInitialized
]) {
1982 InitProtectedConfigurationSection();
1985 return _protectedConfig
;
1989 internal void InitProtectedConfigurationSection() {
1990 if (!_flags
[ProtectedDataInitialized
]) {
1991 _protectedConfig
= GetSection(BaseConfigurationRecord
.RESERVED_SECTION_PROTECTED_CONFIGURATION
, false, false) as ProtectedConfigurationSection
;
1993 Debug
.Assert(_protectedConfig
!= null, "<configProtectedData> section should always be available because it's a built-in section");
1995 _flags
[ProtectedDataInitialized
] = true;
1999 internal ConfigurationBuilder
GetConfigBuilderFromName(string builderName
) {
2000 if (String
.IsNullOrEmpty(builderName
) || ConfigBuilders
== null) {
2001 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_builder_not_found
, builderName
));
2004 return ConfigBuilders
.GetBuilderFromName(builderName
);
2007 private ConfigurationBuildersSection ConfigBuilders
{
2009 if (!_flags
[ConfigBuildersInitialized
]) {
2010 InitConfigBuildersSection();
2013 return _configBuilders
;
2017 internal void InitConfigBuildersSection() {
2018 if (!_flags
[ConfigBuildersInitialized
]) {
2019 _configBuilders
= GetSection(BaseConfigurationRecord
.RESERVED_SECTION_CONFIGURATION_BUILDERS
, false, false) as ConfigurationBuildersSection
;
2021 _flags
[ConfigBuildersInitialized
] = true;
2025 protected object CallCreateSection(bool inputIsTrusted
, FactoryRecord factoryRecord
, SectionRecord sectionRecord
, SectionInput sectionInput
, object parentConfig
, ConfigXmlReader reader
) {
2027 string filename
= null;
2030 if (sectionInput
!= null && sectionInput
.SectionXmlInfo
!= null) {
2031 filename
= sectionInput
.SectionXmlInfo
.Filename
;
2032 line
= sectionInput
.SectionXmlInfo
.LineNumber
;
2035 // Call into config section while impersonating process or UNC identity
2036 // so that the section could read files from disk if needed
2038 using (Impersonate()) {
2039 config
= CreateSection(inputIsTrusted
, factoryRecord
, sectionRecord
, sectionInput
, parentConfig
, reader
);
2040 if (config
== null && parentConfig
!= null) {
2041 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_object_is_null
), filename
, line
);
2045 catch (ThreadAbortException
) {
2048 catch (Exception e
) {
2049 throw ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_exception_creating_section_handler
, factoryRecord
.ConfigKey
), e
, filename
, line
);
2055 // IsRootDeclaration
2057 // Is this the Root Record of where this configKey is Declared
2059 // If parent is null, or there is not a factory record above
2060 // this one, then it is the root of where it can be declared
2062 // Optionally consider whether implicit sections are to be considered rooted.
2064 internal bool IsRootDeclaration(string configKey
, bool implicitIsRooted
) {
2065 if (!implicitIsRooted
&& IsImplicitSection(configKey
)) {
2069 return (_parent
.IsRootConfig
|| _parent
.FindFactoryRecord(configKey
, true) == null);
2072 // Search the config hierarchy for a FactoryRecord.
2073 // Note that callers should check whether the returned factory has errors.
2074 internal FactoryRecord
FindFactoryRecord(string configKey
, bool permitErrors
, out BaseConfigurationRecord configRecord
) {
2075 configRecord
= null;
2076 BaseConfigurationRecord tConfigRecord
= this;
2078 while (!tConfigRecord
.IsRootConfig
) {
2079 FactoryRecord factoryRecord
= tConfigRecord
.GetFactoryRecord(configKey
, permitErrors
);
2080 if (factoryRecord
!= null) {
2082 if (IsImplicitSection(configKey
) && !factoryRecord
.HasErrors
) {
2083 Debug
.Assert(tConfigRecord
._parent
.IsRootConfig
,
2084 "Implicit section should be found only at the record beneath the root (e.g. machine.config)");
2088 configRecord
= tConfigRecord
;
2089 return factoryRecord
;
2092 tConfigRecord
= tConfigRecord
._parent
;
2099 internal FactoryRecord
FindFactoryRecord(string configKey
, bool permitErrors
) {
2100 BaseConfigurationRecord dummy
;
2101 return FindFactoryRecord(configKey
, permitErrors
, out dummy
);
2105 // FindAndEnsureFactoryRecord:
2107 // - Find the nearest factory record
2108 // - Determine if it is the root
2109 // - Create the factory in the root if it doesn't exist.
2110 // - Determine if the factory type is from a global assembly without APTCA
2111 // - Copy the factory and IsFactoryTrustedWithoutAptca bit into the child record
2113 private FactoryRecord
FindAndEnsureFactoryRecord(string configKey
, out bool isRootDeclaredHere
) {
2114 isRootDeclaredHere
= false;
2116 BaseConfigurationRecord configRecord
;
2117 FactoryRecord factoryRecord
= FindFactoryRecord(configKey
, false, out configRecord
);
2118 if (factoryRecord
!= null && !factoryRecord
.IsGroup
) {
2120 // Find the root declaration
2122 FactoryRecord rootFactoryRecord
= factoryRecord
;
2123 BaseConfigurationRecord rootConfigRecord
= configRecord
;
2125 BaseConfigurationRecord currentConfigRecord
= configRecord
._parent
;
2126 while (!currentConfigRecord
.IsRootConfig
) {
2127 BaseConfigurationRecord tempConfigRecord
;
2128 FactoryRecord tempFactoryRecord
= currentConfigRecord
.FindFactoryRecord(configKey
, false, out tempConfigRecord
);
2129 if (tempFactoryRecord
== null)
2132 rootFactoryRecord
= tempFactoryRecord
;
2133 rootConfigRecord
= tempConfigRecord
;
2135 // continue the search from the parent of the configRecord we found
2136 currentConfigRecord
= tempConfigRecord
.Parent
;
2140 // A child factory record must be equivalent to its parent,
2141 // so if the child has no errors, the parent must also have no errors.
2143 Debug
.Assert(!rootFactoryRecord
.HasErrors
, "!rootFactoryRecord.HasErrors");
2144 if (rootFactoryRecord
.Factory
== null) {
2147 // Create the factory from the type string, and cache it
2149 object factory
= rootConfigRecord
.CreateSectionFactory(rootFactoryRecord
);
2150 bool isFactoryTrustedWithoutAptca
= TypeUtil
.IsTypeFromTrustedAssemblyWithoutAptca(factory
.GetType());
2151 rootFactoryRecord
.Factory
= factory
;
2152 rootFactoryRecord
.IsFactoryTrustedWithoutAptca
= isFactoryTrustedWithoutAptca
;
2154 catch (Exception e
) {
2155 throw ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_exception_creating_section_handler
, factoryRecord
.ConfigKey
), e
, factoryRecord
);
2159 if (factoryRecord
.Factory
== null) {
2160 factoryRecord
.Factory
= rootFactoryRecord
.Factory
;
2161 factoryRecord
.IsFactoryTrustedWithoutAptca
= rootFactoryRecord
.IsFactoryTrustedWithoutAptca
;
2164 isRootDeclaredHere
= Object
.ReferenceEquals(this, rootConfigRecord
);
2167 return factoryRecord
;
2170 private Hashtable
ScanFactories(XmlUtil xmlUtil
) {
2171 Hashtable factoryList
;
2173 factoryList
= new Hashtable();
2175 if (xmlUtil
.Reader
.NodeType
!= XmlNodeType
.Element
|| xmlUtil
.Reader
.Name
!= KEYWORD_CONFIGURATION
) {
2177 string safeFilename
= ConfigurationErrorsException
.AlwaysSafeFilename(((IConfigErrorInfo
)xmlUtil
).Filename
);
2179 throw new ConfigurationErrorsException(
2180 SR
.GetString(SR
.Config_file_doesnt_have_root_configuration
, safeFilename
),
2184 // Ignore xmlns attribute
2185 while (xmlUtil
.Reader
.MoveToNextAttribute()) {
2186 switch (xmlUtil
.Reader
.Name
) {
2188 if (xmlUtil
.Reader
.Value
== KEYWORD_CONFIGURATION_NAMESPACE
) {
2189 _flags
[NamespacePresentInFile
] = true;
2190 _flags
[NamespacePresentCurrent
] = true;
2192 ConfigurationErrorsException ce
;
2194 ce
= new ConfigurationErrorsException(
2195 SR
.GetString(SR
.Config_namespace_invalid
, xmlUtil
.Reader
.Value
, KEYWORD_CONFIGURATION_NAMESPACE
),
2198 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Global
);
2204 xmlUtil
.AddErrorUnrecognizedAttribute(ExceptionAction
.NonSpecific
);
2209 // move to first child of <configuration>
2210 xmlUtil
.StrictReadToNextElement(ExceptionAction
.NonSpecific
);
2211 if (xmlUtil
.Reader
.Depth
== 1 && xmlUtil
.Reader
.Name
== KEYWORD_CONFIGSECTIONS
) {
2212 xmlUtil
.VerifyNoUnrecognizedAttributes(ExceptionAction
.NonSpecific
);
2213 ScanFactoriesRecursive(xmlUtil
, string.Empty
, factoryList
);
2220 // Scans the <configSections> section of a configuration file. The function is recursive
2221 // to traverse arbitrarily nested config groups.
2223 // <sectionGroup name="foo">
2224 // <sectionGroup name="bar">
2225 // <section name="----Section" type="..." />
2228 // Note: This function valiates that the factory record has not been
2229 // declared before in a parent record. (it does not check
2230 // current record, which allows you to update list)
2232 private void ScanFactoriesRecursive(XmlUtil xmlUtil
, string parentConfigKey
, Hashtable factoryList
) {
2234 // discard any accumulated local errors
2235 xmlUtil
.SchemaErrors
.ResetLocalErrors();
2237 int depth
= xmlUtil
.Reader
.Depth
;
2238 xmlUtil
.StrictReadToNextElement(ExceptionAction
.NonSpecific
);
2240 while (xmlUtil
.Reader
.Depth
== depth
+ 1) {
2241 bool positionedAtNextElement
= false;
2243 switch (xmlUtil
.Reader
.Name
) {
2245 // Handle <sectionGroup name="groupName" [type="typename"] />
2247 case KEYWORD_SECTIONGROUP
: {
2248 string tagName
= null;
2249 string typeName
= null;
2251 int lineNumber
= xmlUtil
.Reader
.LineNumber
;
2252 while (xmlUtil
.Reader
.MoveToNextAttribute()) {
2253 switch (xmlUtil
.Reader
.Name
) {
2254 case KEYWORD_SECTIONGROUP_NAME
:
2255 tagName
= xmlUtil
.Reader
.Value
;
2256 VerifySectionName(tagName
, xmlUtil
, ExceptionAction
.Local
, false, false);
2259 case KEYWORD_SECTIONGROUP_TYPE
:
2260 xmlUtil
.VerifyAndGetNonEmptyStringAttribute(ExceptionAction
.Local
, out typeName
);
2264 xmlUtil
.AddErrorUnrecognizedAttribute(ExceptionAction
.Local
);
2268 xmlUtil
.Reader
.MoveToElement(); // if on an attribute move back to the element
2270 if (!xmlUtil
.VerifyRequiredAttribute(
2272 KEYWORD_SECTIONGROUP_NAME
,
2273 ExceptionAction
.NonSpecific
)) {
2276 // Without a name, we cannot continue parsing the sections and groups within.
2277 // Skip the entire section.
2279 xmlUtil
.SchemaErrors
.RetrieveAndResetLocalErrors(true);
2280 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
2283 string configKey
= CombineConfigKey(parentConfigKey
, tagName
);
2285 FactoryRecord factoryRecord
= (FactoryRecord
) factoryList
[configKey
];
2286 if (factoryRecord
!= null) {
2287 // Error: duplicate sectionGroup declaration
2288 xmlUtil
.SchemaErrors
.AddError(
2289 new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_already_defined_at_this_level
, tagName
), xmlUtil
),
2290 ExceptionAction
.Local
);
2293 FactoryRecord parentFactoryRecord
= _parent
.FindFactoryRecord(configKey
, true);
2294 if (parentFactoryRecord
!= null) {
2295 configKey
= parentFactoryRecord
.ConfigKey
;
2297 // make sure that an ancestor has not defined a section with the same name as the group
2298 if ( parentFactoryRecord
!= null &&
2299 (!parentFactoryRecord
.IsGroup
||
2300 !parentFactoryRecord
.IsEquivalentSectionGroupFactory(Host
, typeName
))) {
2302 xmlUtil
.SchemaErrors
.AddError(
2303 new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_already_defined
, tagName
), xmlUtil
),
2304 ExceptionAction
.Local
);
2306 parentFactoryRecord
= null;
2310 if (parentFactoryRecord
!= null) {
2311 factoryRecord
= parentFactoryRecord
.CloneSectionGroup(typeName
, xmlUtil
.Filename
, lineNumber
);
2314 factoryRecord
= new FactoryRecord(configKey
, parentConfigKey
, tagName
, typeName
, xmlUtil
.Filename
, lineNumber
);
2317 factoryList
[configKey
] = factoryRecord
;
2320 // Add any errors we may have encountered
2321 factoryRecord
.AddErrors(xmlUtil
.SchemaErrors
.RetrieveAndResetLocalErrors(true));
2323 // continue recursive scan
2324 ScanFactoriesRecursive(xmlUtil
, configKey
, factoryList
);
2330 case KEYWORD_SECTION
: {
2331 string tagName
= null;
2332 string typeName
= null;
2333 ConfigurationAllowDefinition allowDefinition
= ConfigurationAllowDefinition
.Everywhere
;
2334 ConfigurationAllowExeDefinition allowExeDefinition
= ConfigurationAllowExeDefinition
.MachineToApplication
;
2335 OverrideModeSetting overrideModeDefault
= OverrideModeSetting
.SectionDefault
;
2336 bool allowLocation
= true;
2337 bool restartOnExternalChanges
= true;
2338 bool requirePermission
= true;
2339 bool gotType
= false;
2341 // parse section attributes
2342 int lineNumber
= xmlUtil
.Reader
.LineNumber
;
2343 while (xmlUtil
.Reader
.MoveToNextAttribute()) {
2344 switch (xmlUtil
.Reader
.Name
) {
2345 case KEYWORD_SECTION_NAME
:
2346 tagName
= xmlUtil
.Reader
.Value
;
2347 VerifySectionName(tagName
, xmlUtil
, ExceptionAction
.Local
, false, true);
2350 case KEYWORD_SECTION_TYPE
:
2351 xmlUtil
.VerifyAndGetNonEmptyStringAttribute(ExceptionAction
.Local
, out typeName
);
2355 case KEYWORD_SECTION_ALLOWLOCATION
:
2356 xmlUtil
.VerifyAndGetBooleanAttribute(
2357 ExceptionAction
.Local
, true, out allowLocation
);
2360 case KEYWORD_SECTION_ALLOWEXEDEFINITION
:
2362 allowExeDefinition
= AllowExeDefinitionToEnum(xmlUtil
.Reader
.Value
, xmlUtil
);
2364 catch (ConfigurationException ce
) {
2365 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Local
);
2370 case KEYWORD_SECTION_ALLOWDEFINITION
:
2372 allowDefinition
= AllowDefinitionToEnum(xmlUtil
.Reader
.Value
, xmlUtil
);
2374 catch (ConfigurationException ce
) {
2375 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Local
);
2380 case KEYWORD_SECTION_RESTARTONEXTERNALCHANGES
:
2381 xmlUtil
.VerifyAndGetBooleanAttribute(
2382 ExceptionAction
.Local
, true, out restartOnExternalChanges
);
2386 case KEYWORD_SECTION_REQUIREPERMISSION
:
2387 xmlUtil
.VerifyAndGetBooleanAttribute(
2388 ExceptionAction
.Local
, true, out requirePermission
);
2392 case KEYWORD_SECTION_OVERRIDEMODEDEFAULT
:
2394 overrideModeDefault
= OverrideModeSetting
.CreateFromXmlReadValue(
2395 OverrideModeSetting
.ParseOverrideModeXmlValue(xmlUtil
.Reader
.Value
, xmlUtil
));
2397 // Inherit means Allow when comming from the default value
2398 if (overrideModeDefault
.OverrideMode
== OverrideMode
.Inherit
) {
2399 overrideModeDefault
.ChangeModeInternal(OverrideMode
.Allow
);
2403 catch (ConfigurationException ce
) {
2404 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Local
);
2411 xmlUtil
.AddErrorUnrecognizedAttribute(ExceptionAction
.Local
);
2416 xmlUtil
.Reader
.MoveToElement(); // if on an attribute move back to the element
2418 if (!xmlUtil
.VerifyRequiredAttribute(
2419 tagName
, KEYWORD_SECTION_NAME
, ExceptionAction
.NonSpecific
)) {
2422 // Without a name, we cannot continue to create a factoryRecord.
2424 xmlUtil
.SchemaErrors
.RetrieveAndResetLocalErrors(true);
2427 // Verify that the Type attribute was present.
2428 // Note that 'typeName' will be null if the attribute was present
2429 // but specified as an empty string.
2431 xmlUtil
.AddErrorRequiredAttribute(KEYWORD_SECTION_TYPE
, ExceptionAction
.Local
);
2434 // Disallow names starting with "config" unless it is the special configBuilders section.
2435 if (StringUtil
.StartsWith(tagName
, "config")) {
2436 Type sectionType
= Type
.GetType(typeName
);
2437 if (!StringUtil
.Equals(tagName
, RESERVED_SECTION_CONFIGURATION_BUILDERS
) || sectionType
!= ConfigurationBuildersSectionType
) {
2438 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_cannot_begin_with_config
), xmlUtil
);
2442 string configKey
= CombineConfigKey(parentConfigKey
, tagName
);
2444 FactoryRecord factoryRecord
= (FactoryRecord
) factoryList
[configKey
];
2445 if (factoryRecord
!= null) {
2446 // Error: duplicate section declaration
2447 xmlUtil
.SchemaErrors
.AddError(
2448 new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_already_defined_at_this_level
, tagName
), xmlUtil
),
2449 ExceptionAction
.Local
);
2451 FactoryRecord parentFactoryRecord
= _parent
.FindFactoryRecord(configKey
, true);
2452 if (parentFactoryRecord
!= null) {
2453 configKey
= parentFactoryRecord
.ConfigKey
;
2455 // make sure that an ancestor has not defined a section with the same name as the group
2456 if (parentFactoryRecord
.IsGroup
) {
2457 xmlUtil
.SchemaErrors
.AddError(
2458 new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_already_defined
, tagName
), xmlUtil
),
2459 ExceptionAction
.Local
);
2461 parentFactoryRecord
= null;
2463 else if (!parentFactoryRecord
.IsEquivalentSectionFactory(Host
, typeName
, allowLocation
, allowDefinition
, allowExeDefinition
, restartOnExternalChanges
, requirePermission
)) {
2464 xmlUtil
.SchemaErrors
.AddError(
2465 new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_already_defined
, tagName
), xmlUtil
),
2466 ExceptionAction
.Local
);
2468 parentFactoryRecord
= null;
2472 if (parentFactoryRecord
!= null) {
2473 // Note - Clone will propagate the IsFromTrustedConfigRecord bit,
2474 // which is what we want - if this record is a duplicate of an ancestor,
2475 // the ancestor may be from a trusted config record.
2476 factoryRecord
= parentFactoryRecord
.CloneSection(xmlUtil
.Filename
, lineNumber
);
2479 factoryRecord
= new FactoryRecord(
2487 overrideModeDefault
,
2488 restartOnExternalChanges
,
2491 false, // isUndeclared
2496 factoryList
[configKey
] = factoryRecord
;
2499 // Add any errors we may have encountered
2500 factoryRecord
.AddErrors(xmlUtil
.SchemaErrors
.RetrieveAndResetLocalErrors(true));
2505 case KEYWORD_REMOVE
: {
2507 int lineNumber
= -1;
2510 while (xmlUtil
.Reader
.MoveToNextAttribute()) {
2511 if (xmlUtil
.Reader
.Name
!= KEYWORD_SECTION_NAME
) {
2512 xmlUtil
.AddErrorUnrecognizedAttribute(ExceptionAction
.NonSpecific
);
2515 name
= xmlUtil
.Reader
.Value
;
2516 lineNumber
= xmlUtil
.Reader
.LineNumber
;
2518 xmlUtil
.Reader
.MoveToElement();
2520 if (xmlUtil
.VerifyRequiredAttribute(
2521 name
, KEYWORD_SECTION_NAME
, ExceptionAction
.NonSpecific
)) {
2523 VerifySectionName(name
, xmlUtil
, ExceptionAction
.NonSpecific
, false, true);
2528 case KEYWORD_CLEAR
: {
2529 xmlUtil
.VerifyNoUnrecognizedAttributes(ExceptionAction
.NonSpecific
);
2534 xmlUtil
.AddErrorUnrecognizedElement(ExceptionAction
.NonSpecific
);
2535 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
2536 positionedAtNextElement
= true;
2540 if (!positionedAtNextElement
) {
2541 // Need to read to next element, and check if an unrecognized child
2542 // element is found.
2543 xmlUtil
.StrictReadToNextElement(ExceptionAction
.NonSpecific
);
2545 // unrecognized children are not allowed in <configSections>
2546 if (xmlUtil
.Reader
.Depth
> depth
+ 1) {
2547 xmlUtil
.AddErrorUnrecognizedElement(ExceptionAction
.NonSpecific
);
2549 // Lets try to backup to where we are suppose to be
2550 while (xmlUtil
.Reader
.Depth
> (depth
+ 1)) {
2551 xmlUtil
.ReadToNextElement();
2558 // ExeDefinitionToEnum
2560 // Translate an ExeDefinition string from the Declaration in a file
2561 // to the appropriate enumeration
2564 // allowExeDefinition - string representation of value
2565 // xmlUtil [optional] - can provide better error
2567 static internal ConfigurationAllowExeDefinition
2568 AllowExeDefinitionToEnum(string allowExeDefinition
, XmlUtil xmlUtil
)
2570 switch (allowExeDefinition
)
2572 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY
:
2573 return ConfigurationAllowExeDefinition
.MachineOnly
;
2575 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION
:
2576 return ConfigurationAllowExeDefinition
.MachineToApplication
;
2578 case KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOROAMING
:
2579 return ConfigurationAllowExeDefinition
.MachineToRoamingUser
;
2581 case KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOLOCAL
:
2582 return ConfigurationAllowExeDefinition
.MachineToLocalUser
;
2585 throw new ConfigurationErrorsException(
2586 SR
.GetString(SR
.Config_section_allow_exe_definition_attribute_invalid
),
2591 static internal ConfigurationAllowDefinition
2592 AllowDefinitionToEnum(string allowDefinition
, XmlUtil xmlUtil
) {
2593 switch (xmlUtil
.Reader
.Value
) {
2594 case KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE
:
2595 return ConfigurationAllowDefinition
.Everywhere
;
2597 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY
:
2598 return ConfigurationAllowDefinition
.MachineOnly
;
2600 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION
:
2601 return ConfigurationAllowDefinition
.MachineToApplication
;
2603 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT
:
2604 return ConfigurationAllowDefinition
.MachineToWebRoot
;
2607 throw new ConfigurationErrorsException(
2608 SR
.GetString(SR
.Config_section_allow_definition_attribute_invalid
),
2613 static internal string CombineConfigKey(string parentConfigKey
, string tagName
) {
2614 if (String
.IsNullOrEmpty(parentConfigKey
)) {
2618 if (String
.IsNullOrEmpty(tagName
)) {
2619 return parentConfigKey
;
2622 return parentConfigKey
+ "/" + tagName
;
2625 static internal void SplitConfigKey(string configKey
, out string group, out string name
) {
2626 int lastSlash
= configKey
.LastIndexOf('/');
2627 if (lastSlash
== -1) {
2628 group = string.Empty
;
2632 group = configKey
.Substring(0, lastSlash
);
2633 name
= configKey
.Substring(lastSlash
+ 1);
2637 [System
.Diagnostics
.Conditional("DBG")]
2638 private void DebugValidateIndirectInputs(SectionRecord sectionRecord
) {
2639 if (_parent
.IsRootConfig
) {
2643 // Verify that for each indirect input, its target config path is a child path of _parent.
2644 // That's the definition of indirect input.
2645 for(int i
= sectionRecord
.IndirectLocationInputs
.Count
-1; i
>= 0; i
--) {
2646 SectionInput input
= sectionRecord
.IndirectLocationInputs
[i
];
2648 // Get the override mode starting from the closest input.
2649 Debug
.Assert(UrlPath
.IsSubpath(_parent
.ConfigPath
, input
.SectionXmlInfo
.TargetConfigPath
));
2653 // Return the lock mode for a section as comming from parent config levels
2654 private OverrideMode
ResolveOverrideModeFromParent(string configKey
, out OverrideMode childLockMode
) {
2656 // When the current record is a location config level we are a direct child of the config level of the actual
2657 // config file inside which the location tag is. For example we have a file d:\inetpub\wwwroot\web.config which
2658 // containts <location path="Sub"> then "this" will be the config level inside the location tag and this.Parent
2659 // is the config level of d:\inetpub\wwwroot\web.config.
2661 // What we will do to come up with the result is:
2662 // 1) Try to find an existing section record somewhere above us.
2663 // If we find an existing section record then it will have the effective value of the lock mode
2664 // that applies to us in it's LockChidlren. We dont need to go further up once we find a section record
2665 // as it has the lock mode of all it's parents accumulated
2667 // There is one huge trick though - Location config records are different ( see begining of the func for what a location config record is )
2668 // A location config record is not locked if the config level of the web.config file in which it lives is not locked.
2669 // I.e. when we are looking for the effective value for a location config we have two cases
2670 // a) There is a section record in our immediate parent ( remember our immediate parent is the config file in which we /as a location tag/ are defined )
2671 // In this case our lock mode is not the LockChildren of this section record because this lock mode applies to child config levels in child config files
2672 // The real lock mode for us is the Locked mode of the section record in self.
2673 // b) There is no section record in our immediate parent - in this case the locking is the same as for normal config - LockChildren value of any section
2674 // record we may find above us.
2676 // 2) If we can't find an existing section record we have two cases again:
2677 // a) We are at the section declaration level - at this level a section is always unlocked by definition
2678 // If this wasnt so there would be no way to unlock a section that is locked by default
2679 // A Location config is a bit wierd again in a sence that a location config is unlocked if its in the config file where the section is declared
2680 // I.e. if "this" is a location record then a section is unconditionally unlocked if "this.Parent" is the section declaration level
2681 // b) We are not at section declaration level - in this case the result is whatever the default lock mode for the section is ( remember
2682 // that we fall back to the default since we couldnt find a section record with explicit lock mode nowhere above us)
2684 // I sure hope that made some sense!
2687 OverrideMode mode
= OverrideMode
.Inherit
;
2688 BaseConfigurationRecord parent
= Parent
;
2689 BaseConfigurationRecord immediateParent
= Parent
;
2691 childLockMode
= OverrideMode
.Inherit
;
2693 // Walk the hierarchy until we find an explicit setting for lock state at a config level or we reach to root
2694 while (!parent
.IsRootConfig
&& (mode
== OverrideMode
.Inherit
)) {
2695 SectionRecord sectionRecord
= parent
.GetSectionRecord(configKey
, true);
2697 if (sectionRecord
!= null) {
2700 if (IsLocationConfig
&& object.ReferenceEquals(immediateParent
, parent
)) {
2702 mode
= sectionRecord
.Locked
? OverrideMode
.Deny
: OverrideMode
.Allow
;
2704 // In this specific case the lock mode for our children is whatever the children of our parent should inherit
2705 // For example imagine a web.config which has a <location path="." overrideMode="Deny"> and we open "locationSubPath" from this web.config
2706 // The lock for the section is not Deny and will be allow ( see the code line above ). However the chidlren of this location tag
2707 // inherit the lock that applies to the children of the web.config file itself
2708 childLockMode
= sectionRecord
.LockChildren
? OverrideMode
.Deny
: OverrideMode
.Allow
;
2711 mode
= sectionRecord
.LockChildren
? OverrideMode
.Deny
: OverrideMode
.Allow
;
2713 // When the lock mode is comming from a parent level the
2714 // lock mode that applies to children of "this" is the same as what applies to "this"
2715 childLockMode
= mode
;
2719 parent
= parent
._parent
;
2723 if (mode
== OverrideMode
.Inherit
) {
2725 Debug
.Assert(FindFactoryRecord(configKey
, true) != null);
2727 bool atDeclarationLevel
= false;
2728 OverrideMode defaultMode
= FindFactoryRecord(configKey
, true).OverrideModeDefault
.OverrideMode
;
2730 if (IsLocationConfig
) {
2731 atDeclarationLevel
= this.Parent
.GetFactoryRecord(configKey
, true) != null;
2734 atDeclarationLevel
= this.GetFactoryRecord(configKey
, true) != null;
2737 if (!atDeclarationLevel
) {
2742 // Lock mode for children and self is the same since the default value is comming
2743 // from a parent level and hence - applies to both
2744 childLockMode
= mode
= defaultMode
;
2746 Debug
.Assert(mode
!= OverrideMode
.Inherit
); // Remember that the default is never Inherit
2753 // Self is always allow at section declaration level
2754 // Child lock mode is the default value ( remember we are here because no explici mode was set anywhere above us )
2756 mode
= OverrideMode
.Allow
;
2757 childLockMode
= defaultMode
;
2761 // This function must return Allow or Deny
2762 Debug
.Assert(mode
!= OverrideMode
.Inherit
);
2767 protected OverrideMode
GetSectionLockedMode(string configKey
) {
2768 OverrideMode dummy
= OverrideMode
.Inherit
;
2770 return GetSectionLockedMode(configKey
, out dummy
);
2773 // Return the current lock mode for a section
2774 protected OverrideMode
GetSectionLockedMode(string configKey
, out OverrideMode childLockMode
) {
2776 OverrideMode result
= OverrideMode
.Inherit
;
2778 SectionRecord sectionRecord
= GetSectionRecord(configKey
, true);
2780 // If there is a section record it has the effective locking settings resolved
2781 // There is no need to do ResolveOverrideModeFromParent because it was done in:
2782 // 1) In EnsureSectionRecord when the section record was creteted
2783 // 2) Right after the SectionRecord was created without initialization of the lock settings
2784 // in this:ScanSectionsRecursive().
2785 // As long as nobody uses EnsureSectionRecordUnsafe this method will be returning the correct
2786 // lock value only by looking at the section record
2788 if (sectionRecord
!= null) {
2789 result
= sectionRecord
.Locked
? OverrideMode
.Deny
: OverrideMode
.Allow
;
2790 childLockMode
= sectionRecord
.LockChildren
? OverrideMode
.Deny
: OverrideMode
.Allow
;
2793 result
= ResolveOverrideModeFromParent(configKey
, out childLockMode
);
2799 private void ScanSections(XmlUtil xmlUtil
) {
2800 ScanSectionsRecursive(xmlUtil
, string.Empty
, false, null, OverrideModeSetting
.LocationDefault
, false);
2803 private void ScanSectionsRecursive(
2805 string parentConfigKey
,
2807 string locationSubPath
,
2808 OverrideModeSetting overrideMode
,
2809 bool skipInChildApps
) {
2811 // discard any accumulated local errors
2812 xmlUtil
.SchemaErrors
.ResetLocalErrors();
2816 // only move to child nodes when not on first level (we've already passed the first <configsections>)
2817 if (parentConfigKey
.Length
== 0 && !inLocation
) {
2821 depth
= xmlUtil
.Reader
.Depth
;
2822 xmlUtil
.StrictReadToNextElement(ExceptionAction
.NonSpecific
);
2825 while (xmlUtil
.Reader
.Depth
== depth
+ 1) {
2827 string tagName
= xmlUtil
.Reader
.Name
;
2830 // Check for reserved elements before looking up the factory,
2831 // which may have the same name if it is in error.
2833 if (tagName
== KEYWORD_CONFIGSECTIONS
) {
2834 // Error: duplicate <configSections> tag, or <configSections> not the first tag under <configuration>
2835 xmlUtil
.SchemaErrors
.AddError(
2836 new ConfigurationErrorsException(SR
.GetString(SR
.Config_client_config_too_many_configsections_elements
, tagName
), xmlUtil
),
2837 ExceptionAction
.NonSpecific
);
2839 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
2843 if (tagName
== KEYWORD_LOCATION
) {
2844 if (parentConfigKey
.Length
> 0 || inLocation
) {
2845 // Error: <location> section not at top level
2846 xmlUtil
.SchemaErrors
.AddError(
2847 new ConfigurationErrorsException(SR
.GetString(SR
.Config_location_location_not_allowed
), xmlUtil
),
2848 ExceptionAction
.Global
);
2850 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
2853 // Recurse into the location section
2854 ScanLocationSection(xmlUtil
);
2860 string configKey
= CombineConfigKey(parentConfigKey
, tagName
);
2861 FactoryRecord factoryRecord
= FindFactoryRecord(configKey
, true);
2863 if (factoryRecord
== null) {
2865 // Unregistered configuration section
2867 // At runtime, it is a local error to have an unrecognized section.
2868 // By treating it as local we avoid throwing an error if the
2869 // section is encountered within a location section, just as we treat
2870 // other section errors in a location tag.
2872 // At designtime, we do not consider it an error, so that programs
2873 // that worked on version N config files can continue to work with
2874 // version N+1 config files that may introduce new sections.
2876 if (!ClassFlags
[ClassIgnoreLocalErrors
]) {
2877 xmlUtil
.SchemaErrors
.AddError(
2878 new ConfigurationErrorsException(SR
.GetString(SR
.Config_unrecognized_configuration_section
, configKey
), xmlUtil
),
2879 ExceptionAction
.Local
);
2882 VerifySectionName(tagName
, xmlUtil
, ExceptionAction
.Local
, false);
2884 factoryRecord
= new FactoryRecord(
2888 typeof(DefaultSection
).AssemblyQualifiedName
,
2889 true, // allowLocation
2890 ConfigurationAllowDefinition
.Everywhere
,
2891 ConfigurationAllowExeDefinition
.MachineToRoamingUser
,
2892 OverrideModeSetting
.SectionDefault
,
2893 true, // restartOnExternalChanges
2894 true, // requirePermission
2896 true, // isUndeclared
2900 // Add any errors we may have encountered to the factory record,
2901 // so that child config that also refer to this unrecognized section
2903 factoryRecord
.AddErrors(xmlUtil
.SchemaErrors
.RetrieveAndResetLocalErrors(true));
2905 // Add the factory to the list of factories
2906 EnsureFactories()[configKey
] = factoryRecord
;
2909 if (factoryRecord
.IsGroup
) {
2913 if (factoryRecord
.HasErrors
) {
2914 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
2917 if (xmlUtil
.Reader
.AttributeCount
> 0) {
2918 // We allow unrecognized attributes for backward compatibility (VSWhidbey 516534)
2919 // However, we will still throw if the unrecognized attribute is reserved.
2920 while (xmlUtil
.Reader
.MoveToNextAttribute()) {
2921 if (IsReservedAttributeName(xmlUtil
.Reader
.Name
)) {
2922 xmlUtil
.AddErrorReservedAttribute(ExceptionAction
.NonSpecific
);
2926 xmlUtil
.Reader
.MoveToElement(); // if on an attribute move back to the element
2929 // Recurse into group definition
2930 ScanSectionsRecursive(xmlUtil
, configKey
, inLocation
, locationSubPath
, overrideMode
, skipInChildApps
);
2937 configKey
= factoryRecord
.ConfigKey
;
2938 string fileName
= xmlUtil
.Filename
;
2939 int lineNumber
= xmlUtil
.LineNumber
;
2940 string rawXml
= null;
2941 string configSource
= null;
2942 string configSourceStreamName
= null;
2943 object configSourceStreamVersion
= null;
2944 string configBuilderName
= null;
2945 string protectionProviderName
= null;
2946 OverrideMode sectionLockMode
= OverrideMode
.Inherit
;
2947 OverrideMode sectionChildLockMode
= OverrideMode
.Inherit
;
2948 bool positionedAtNextElement
= false;
2949 bool isFileInput
= (locationSubPath
== null);
2951 if (!factoryRecord
.HasErrors
) {
2952 // We have a valid factoryRecord for a section
2953 if (inLocation
&& factoryRecord
.AllowLocation
== false) {
2954 xmlUtil
.SchemaErrors
.AddError(
2955 new ConfigurationErrorsException(SR
.GetString(SR
.Config_section_cannot_be_used_in_location
), xmlUtil
),
2956 ExceptionAction
.Local
);
2959 // Verify correctness for file inputs.
2961 // Verify that the section is unique
2962 SectionRecord sectionRecord
= GetSectionRecord(configKey
, true);
2963 if (sectionRecord
!= null && sectionRecord
.HasFileInput
) {
2964 if (!(factoryRecord
.IsIgnorable())) {
2965 xmlUtil
.SchemaErrors
.AddError(
2966 new ConfigurationErrorsException(SR
.GetString(SR
.Config_sections_must_be_unique
), xmlUtil
),
2967 ExceptionAction
.Local
);
2972 // Verify that the definition is allowed.
2974 VerifyDefinitionAllowed(factoryRecord
, _configPath
, xmlUtil
);
2976 catch (ConfigurationException ce
) {
2977 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Local
);
2981 // Verify that section is unlocked, both for file and location inputs.
2982 sectionLockMode
= GetSectionLockedMode(configKey
, out sectionChildLockMode
);
2984 if (sectionLockMode
== OverrideMode
.Deny
) {
2985 xmlUtil
.SchemaErrors
.AddError( new ConfigurationErrorsException(SR
.GetString(SR
.Config_section_locked
), xmlUtil
),
2986 ExceptionAction
.Local
);
2989 // check for configSource or protectionProvider
2990 if (xmlUtil
.Reader
.AttributeCount
>= 1) {
2991 // First do all the attributes reading without advancing the reader.
2993 string configSourceAttribute
= xmlUtil
.Reader
.GetAttribute(KEYWORD_CONFIGSOURCE
);
2994 if (configSourceAttribute
!= null) {
2996 configSource
= NormalizeConfigSource(configSourceAttribute
, xmlUtil
);
2998 catch (ConfigurationException ce
) {
2999 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Local
);
3002 if (xmlUtil
.Reader
.AttributeCount
!= 1) {
3003 // Error: elements with configSource should not have other attributes
3004 xmlUtil
.SchemaErrors
.AddError(
3005 new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_syntax_error
), xmlUtil
),
3006 ExceptionAction
.Local
);
3010 string protectionProviderAttribute
= xmlUtil
.Reader
.GetAttribute(KEYWORD_PROTECTION_PROVIDER
);
3011 if (protectionProviderAttribute
!= null) {
3013 protectionProviderName
= ValidateProtectionProviderAttribute(protectionProviderAttribute
, xmlUtil
);
3015 catch (ConfigurationException ce
) {
3016 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Local
);
3019 if (xmlUtil
.Reader
.AttributeCount
!= 1) {
3020 // Error: elements with protectionProvider should not have other attributes
3021 xmlUtil
.SchemaErrors
.AddError(
3022 new ConfigurationErrorsException(SR
.GetString(SR
.Protection_provider_syntax_error
), xmlUtil
),
3023 ExceptionAction
.Local
);
3027 string configBuilderAttribute
= xmlUtil
.Reader
.GetAttribute(KEYWORD_CONFIG_BUILDER
);
3028 if (configBuilderAttribute
!= null) {
3030 configBuilderName
= ValidateConfigBuilderAttribute(configBuilderAttribute
, xmlUtil
);
3032 catch (ConfigurationException ce
) {
3033 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Local
);
3037 // The 2nd part of the configSource check requires advancing the reader.
3038 // Please note that this part should be done only AFTER all other attributes
3039 // checking are done.
3040 if (configSourceAttribute
!= null) {
3041 if (!xmlUtil
.Reader
.IsEmptyElement
) {
3042 while (xmlUtil
.Reader
.Read()) {
3043 XmlNodeType t
= xmlUtil
.Reader
.NodeType
;
3044 if (t
== XmlNodeType
.EndElement
)
3047 if (t
!= XmlNodeType
.Comment
) {
3048 // Error: elements with configSource should not subelements other than comments
3049 xmlUtil
.SchemaErrors
.AddError(
3050 new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_syntax_error
), xmlUtil
),
3051 ExceptionAction
.Local
);
3053 if (t
== XmlNodeType
.Element
) {
3054 xmlUtil
.StrictSkipToOurParentsEndElement(ExceptionAction
.NonSpecific
);
3057 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
3060 positionedAtNextElement
= true;
3068 if (configSource
!= null) {
3071 configSourceStreamName
= Host
.GetStreamNameForConfigSource(ConfigStreamInfo
.StreamName
, configSource
);
3073 catch (Exception e
) {
3074 throw ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_source_invalid
), e
, xmlUtil
);
3076 ValidateUniqueConfigSource(configKey
, configSourceStreamName
, configSource
, xmlUtil
);
3077 configSourceStreamVersion
= MonitorStream(configKey
, configSource
, configSourceStreamName
);
3079 catch (ConfigurationException ex
) {
3080 xmlUtil
.SchemaErrors
.AddError(ex
, ExceptionAction
.Local
);
3085 // prefetch the raw xml
3087 if (!xmlUtil
.SchemaErrors
.HasLocalErrors
) {
3088 if (configSource
== null && ShouldPrefetchRawXml(factoryRecord
)) {
3089 Debug
.Assert(!positionedAtNextElement
, "!positionedAtNextElement");
3091 rawXml
= xmlUtil
.CopySection();
3092 if (xmlUtil
.Reader
.NodeType
!= XmlNodeType
.Element
) {
3093 xmlUtil
.VerifyIgnorableNodeType(ExceptionAction
.NonSpecific
);
3094 xmlUtil
.StrictReadToNextElement(ExceptionAction
.NonSpecific
);
3097 positionedAtNextElement
= true;
3102 // Get the list of errors before advancing the reader
3103 List
<ConfigurationException
> localErrors
= xmlUtil
.SchemaErrors
.RetrieveAndResetLocalErrors(isFileInput
);
3105 // advance the reader to the next element
3106 if (!positionedAtNextElement
) {
3107 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
3110 // Add the input either to:
3111 // 1. The file input at the current config level, or
3112 // 2. LocationSections, where it will be used in sub paths
3113 bool addInput
= true;
3116 // If isFileInput==true, Input added will be used against this config level.
3117 // Need to check if we need to skip it due to inheritInChildApplications.
3119 if (ShouldSkipDueToInheritInChildApplications(skipInChildApps
)) {
3124 if (!_flags
[SupportsLocation
]) {
3125 // Skip if we have a location input but we don't support location tag.
3132 string targetConfigPath
= (locationSubPath
== null) ? _configPath
: null;
3134 SectionXmlInfo sectionXmlInfo
= new SectionXmlInfo(
3135 configKey
, _configPath
, targetConfigPath
, locationSubPath
,
3136 fileName
, lineNumber
, ConfigStreamInfo
.StreamVersion
, rawXml
,
3137 configSource
, configSourceStreamName
, configSourceStreamVersion
,
3138 configBuilderName
, protectionProviderName
, overrideMode
, skipInChildApps
);
3140 if (locationSubPath
== null) {
3142 // Add this file input to the section record
3145 // We've already checked for locked above, so use skip the second check
3146 // and set the locked bit.
3147 SectionRecord sectionRecord
= EnsureSectionRecordUnsafe(configKey
, true);
3149 // Since we called EnsureSectionRecordUnsafe the section record does not have its lock mode resolved
3150 // but we have it in sectionLockMode and childLockMode. Apply it now
3151 sectionRecord
.ChangeLockSettings(sectionLockMode
, sectionChildLockMode
);
3153 // Note that we first apply the lock mode comming from parent levels ( the line above ) and then
3154 // add the file input since the file input takes precedence over whats comming from parent
3155 SectionInput fileInput
= new SectionInput(sectionXmlInfo
, localErrors
);
3156 sectionRecord
.AddFileInput(fileInput
);
3162 // Add this location input to this list of location sections
3164 LocationSectionRecord locationSectionRecord
= new LocationSectionRecord(sectionXmlInfo
, localErrors
);
3165 EnsureLocationSections().Add(locationSectionRecord
);
3172 private void ScanLocationSection(XmlUtil xmlUtil
) {
3173 string locationSubPath
= null;
3174 bool inheritInChildApp
= true;
3175 int errorCountBeforeScan
= xmlUtil
.SchemaErrors
.GlobalErrorCount
;
3176 OverrideModeSetting overrideMode
= OverrideModeSetting
.LocationDefault
;
3177 bool overrideModeInit
= false;
3179 // Get the location section attributes
3180 while (xmlUtil
.Reader
.MoveToNextAttribute()) {
3181 switch (xmlUtil
.Reader
.Name
) {
3182 case KEYWORD_LOCATION_PATH
:
3183 locationSubPath
= xmlUtil
.Reader
.Value
;
3186 case KEYWORD_LOCATION_ALLOWOVERRIDE
:
3187 // Check that allowOverride and OverrideMode werent specified at the same time
3188 if (overrideModeInit
== true){
3189 xmlUtil
.SchemaErrors
.AddError(new ConfigurationErrorsException(SR
.GetString(SR
.Invalid_override_mode_declaration
), xmlUtil
), ExceptionAction
.Global
);
3195 xmlUtil
.VerifyAndGetBooleanAttribute(
3196 ExceptionAction
.Global
, true, out value);
3198 overrideMode
= OverrideModeSetting
.CreateFromXmlReadValue(value);
3199 overrideModeInit
= true;
3204 case KEYWORD_LOCATION_OVERRIDEMODE
:
3205 if (overrideModeInit
== true){
3206 xmlUtil
.SchemaErrors
.AddError(new ConfigurationErrorsException(SR
.GetString(SR
.Invalid_override_mode_declaration
), xmlUtil
), ExceptionAction
.Global
);
3209 overrideMode
= OverrideModeSetting
.CreateFromXmlReadValue(
3210 OverrideModeSetting
.ParseOverrideModeXmlValue(xmlUtil
.Reader
.Value
, xmlUtil
));
3211 overrideModeInit
= true;
3216 case KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS
:
3217 xmlUtil
.VerifyAndGetBooleanAttribute(
3218 ExceptionAction
.Global
, true, out inheritInChildApp
);
3223 xmlUtil
.AddErrorUnrecognizedAttribute(ExceptionAction
.Global
);
3228 xmlUtil
.Reader
.MoveToElement(); // if on an attribute move back to the element
3231 locationSubPath
= NormalizeLocationSubPath(locationSubPath
, xmlUtil
);
3234 // See attached email in the bug. Basically, we decided to throw if we see one of these
3235 // in machine.config or root web.config:
3236 // <location path="." inheritInChildApplications="false" >
3237 // <location inheritInChildApplications="false" >
3239 // To detect whetherewe're machine.config or root web.config, the current fix is to use
3240 // Host.IsDefinitionAllowed. Instead of this we should invent a new method in
3241 // IInternalConfigHost to return whether a configPath can be part of an app or not.
3242 // But since it's Whidbey RC "Ask Mode" I chose not to do it due to bigger code churn.
3245 if (locationSubPath
== null &&
3246 !inheritInChildApp
&&
3247 Host
.IsDefinitionAllowed(_configPath
, ConfigurationAllowDefinition
.MachineToWebRoot
, ConfigurationAllowExeDefinition
.MachineOnly
)) {
3248 throw new ConfigurationErrorsException(SR
.GetString(SR
.Location_invalid_inheritInChildApplications_in_machine_or_root_web_config
), xmlUtil
);
3251 catch (ConfigurationErrorsException ce
) {
3252 xmlUtil
.SchemaErrors
.AddError(ce
, ExceptionAction
.Global
);
3255 // Skip over this location section if there are errors
3256 if (xmlUtil
.SchemaErrors
.GlobalErrorCount
> errorCountBeforeScan
) {
3257 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
3261 // Scan elements of the location section if the path is the current path.
3262 // We do not add <location path="." /> to the _locationSections list.
3263 if (locationSubPath
== null) {
3264 ScanSectionsRecursive(xmlUtil
, string.Empty
, true, null, overrideMode
, !inheritInChildApp
);
3268 // Skip over location sections for client config
3269 if (!_flags
[SupportsLocation
]) {
3270 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
3274 // WOS 1955773: (Perf) 4,000 location sections in web.config file degrades working set
3275 // Skip over location sections that don't apply to this (application) host
3276 // WOS 1983387: do this for the runtime record only. It's a valid scenario for
3277 // mgt config record
3278 IInternalConfigHost host
= Host
;
3279 if ((this is RuntimeConfigurationRecord
) && host
!= null && locationSubPath
.Length
!= 0 && locationSubPath
[0] != '.') {
3281 // The application's config path is global to the AppDomain
3282 if (s_appConfigPath
== null) {
3283 object ctx
= ConfigContext
;
3285 string appConfigPath
= ctx
.ToString();
3286 Interlocked
.CompareExchange(ref s_appConfigPath
, appConfigPath
, null);
3290 // If targetConfigPath is not upstream or downstream of the application's config path,
3291 // skip this location section.
3293 // Example #1: <location path="Site1"> has a targetConfigPath of "machine/webroot/1". This applies
3294 // to Site1, whose application config path is "machine/webroot/1", but it does not apply
3295 // to Site2, whose application config path is "machine/webroot/2"
3297 // Example #2: <location path="subdir"> has a targetConfigPath of "machine/webroot/1/root/subdir".
3298 // This applies to an application with an application config path of "machine/webroot/1/root/subdir/app".
3300 string targetConfigPath
= host
.GetConfigPathFromLocationSubPath(_configPath
, locationSubPath
);
3301 if (!StringUtil
.StartsWithIgnoreCase(s_appConfigPath
, targetConfigPath
)
3302 && !StringUtil
.StartsWithIgnoreCase(targetConfigPath
, s_appConfigPath
)) {
3303 xmlUtil
.StrictSkipToNextElement(ExceptionAction
.NonSpecific
);
3308 AddLocation(locationSubPath
);
3309 ScanSectionsRecursive(xmlUtil
, string.Empty
, true, locationSubPath
, overrideMode
, !inheritInChildApp
);
3315 // If you wish to keep track of the Location Fields, then use this
3317 protected virtual void AddLocation(string LocationSubPath
) {}
3320 // Resolve information about a location section at the time that the location section
3321 // is being used by child configuration records. This allows us to:
3322 // * Delay determining the configuration path for the location record until the sites section is available.
3323 // * Delay reporting bad location paths until the location record has to be used.
3325 private void ResolveLocationSections() {
3326 if (!_flags
[IsLocationListResolved
]) {
3327 // Resolve outside of any lock
3328 if (!_parent
.IsRootConfig
) {
3329 _parent
.ResolveLocationSections();
3333 if (!_flags
[IsLocationListResolved
]) {
3334 if (_locationSections
!= null) {
3336 // Create dictionary that maps configPaths to (dictionary that maps sectionNames to locationSectionRecords)
3338 HybridDictionary locationConfigPaths
= new HybridDictionary(true);
3339 foreach (LocationSectionRecord locationSectionRecord
in _locationSections
) {
3341 // Resolve the target config path
3343 string targetConfigPath
= Host
.GetConfigPathFromLocationSubPath(_configPath
, locationSectionRecord
.SectionXmlInfo
.SubPath
);
3344 locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
= targetConfigPath
;
3349 HybridDictionary locationSectionRecordDictionary
= (HybridDictionary
) locationConfigPaths
[targetConfigPath
];
3350 if (locationSectionRecordDictionary
== null) {
3351 locationSectionRecordDictionary
= new HybridDictionary(false);
3352 locationConfigPaths
.Add(targetConfigPath
, locationSectionRecordDictionary
);
3355 LocationSectionRecord duplicateRecord
= (LocationSectionRecord
) locationSectionRecordDictionary
[locationSectionRecord
.ConfigKey
];
3356 FactoryRecord factoryRecord
= null;
3357 if (duplicateRecord
== null) {
3358 locationSectionRecordDictionary
.Add(locationSectionRecord
.ConfigKey
, locationSectionRecord
);
3361 factoryRecord
= FindFactoryRecord(locationSectionRecord
.ConfigKey
, true);
3362 if (factoryRecord
== null || !(factoryRecord
.IsIgnorable())) {
3363 if (!duplicateRecord
.HasErrors
) {
3364 duplicateRecord
.AddError(
3365 new ConfigurationErrorsException(
3366 SR
.GetString(SR
.Config_sections_must_be_unique
),
3367 duplicateRecord
.SectionXmlInfo
));
3370 locationSectionRecord
.AddError(
3371 new ConfigurationErrorsException(
3372 SR
.GetString(SR
.Config_sections_must_be_unique
),
3373 locationSectionRecord
.SectionXmlInfo
));
3378 // Check if the definition is allowed
3380 if (factoryRecord
== null)
3381 factoryRecord
= FindFactoryRecord(locationSectionRecord
.ConfigKey
, true);
3382 if (!factoryRecord
.HasErrors
) {
3384 VerifyDefinitionAllowed(factoryRecord
, targetConfigPath
, locationSectionRecord
.SectionXmlInfo
);
3386 catch (ConfigurationException e
) {
3387 locationSectionRecord
.AddError(e
);
3394 // Check location section for being locked.
3396 BaseConfigurationRecord parent
= _parent
;
3397 while (!parent
.IsRootConfig
) {
3398 foreach (LocationSectionRecord locationSectionRecord
in this._locationSections
) {
3400 bool locked
= false;
3403 // It is an error if a parent section with the same configKey is locked.
3405 SectionRecord sectionRecord
= parent
.GetSectionRecord(locationSectionRecord
.ConfigKey
, true);
3406 if ( sectionRecord
!= null &&
3407 (sectionRecord
.LockChildren
|| sectionRecord
.Locked
)) {
3413 // It is an error if a parent configuration file locks a section for the
3414 // locationConfigPath or any sub-path of the locationConfigPath.
3416 if (parent
._locationSections
!= null) {
3417 string targetConfigPath
= locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
;
3419 foreach (LocationSectionRecord parentLocationSectionRecord
in parent
._locationSections
) {
3420 string parentTargetConfigPath
= parentLocationSectionRecord
.SectionXmlInfo
.TargetConfigPath
;
3422 if ( parentLocationSectionRecord
.SectionXmlInfo
.OverrideModeSetting
.IsLocked
&&
3423 locationSectionRecord
.ConfigKey
== parentLocationSectionRecord
.ConfigKey
&&
3424 UrlPath
.IsEqualOrSubpath(targetConfigPath
, parentTargetConfigPath
)) {
3434 locationSectionRecord
.AddError(new ConfigurationErrorsException(
3435 SR
.GetString(SR
.Config_section_locked
),
3436 locationSectionRecord
.SectionXmlInfo
));
3440 parent
= parent
._parent
;
3446 _flags
[IsLocationListResolved
] = true;
3453 // VerifyDefinitionAllowed
3455 // Verify that the Definition is allowed at this
3458 // For example, if this config record is an application then
3459 // make sure the section say's it can be defined in an
3462 private void VerifyDefinitionAllowed(FactoryRecord factoryRecord
, string configPath
, IConfigErrorInfo errorInfo
) {
3463 Host
.VerifyDefinitionAllowed(configPath
, factoryRecord
.AllowDefinition
, factoryRecord
.AllowExeDefinition
, errorInfo
);
3466 internal bool IsDefinitionAllowed(ConfigurationAllowDefinition allowDefinition
, ConfigurationAllowExeDefinition allowExeDefinition
) {
3467 return Host
.IsDefinitionAllowed(_configPath
, allowDefinition
, allowExeDefinition
);
3470 static protected void VerifySectionName(string name
, XmlUtil xmlUtil
, ExceptionAction action
, bool allowImplicit
, bool allowConfigNames
= false) {
3472 VerifySectionName(name
, (IConfigErrorInfo
) xmlUtil
, allowImplicit
, allowConfigNames
);
3474 catch (ConfigurationErrorsException ce
) {
3475 xmlUtil
.SchemaErrors
.AddError(ce
, action
);
3479 // Check if the section name contains reserved words from the config system,
3480 // and is a valid name for an XML Element.
3481 static protected void VerifySectionName(string name
, IConfigErrorInfo errorInfo
, bool allowImplicit
, bool allowConfigNames
= false) {
3482 if (String
.IsNullOrEmpty(name
)) {
3483 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_invalid
), errorInfo
);
3486 // must be a valid name in xml, so that it can be used as an element
3487 // n.b. - it also excludes forward slash '/'
3489 XmlConvert
.VerifyName(name
);
3491 // Do not let the exception propagate as an XML exception,
3492 // for we want errors in the section name to be treated as local errors,
3494 catch (Exception e
) {
3495 throw ExceptionUtil
.WrapAsConfigException(SR
.GetString(SR
.Config_tag_name_invalid
), e
, errorInfo
);
3498 if (IsImplicitSection(name
)) {
3499 if (allowImplicit
) {
3500 // avoid test below for strings starting with "config"
3504 throw new ConfigurationErrorsException(SR
.GetString(SR
.Cannot_declare_or_remove_implicit_section
, name
), errorInfo
);
3508 if (!allowConfigNames
&& StringUtil
.StartsWith(name
, "config")) {
3509 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_cannot_begin_with_config
), errorInfo
);
3512 if (name
== KEYWORD_LOCATION
) {
3513 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_tag_name_cannot_be_location
), errorInfo
);
3518 From http://www.w3.org/Addressing/
3520 reserved = ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ','
3524 reserved = '\' | '/' | '|' | ':' | '"' | '<' | '>'
3528 // NOTE: If you change these strings, you must change the associated error message
3529 const string invalidFirstSubPathCharacters
= @"\./";
3530 const string invalidLastSubPathCharacters
= @"\./";
3531 const string invalidSubPathCharactersString
= @"\?:*""<>|";
3532 static char[] s_invalidSubPathCharactersArray
= invalidSubPathCharactersString
.ToCharArray();
3534 // Return null if the subPath represents the current directory, for example:
3539 internal static string NormalizeLocationSubPath(string subPath
, IConfigErrorInfo errorInfo
) {
3540 // if subPath is null or empty, it is the current dir
3541 if (String
.IsNullOrEmpty(subPath
))
3544 // if subPath=".", it is the current dir
3548 // do not allow whitespace in front of subPath, as the OS
3549 // handles beginning and trailing whitespace inconsistently
3550 string trimmedSubPath
= subPath
.TrimStart();
3551 if (trimmedSubPath
.Length
!= subPath
.Length
) {
3552 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_location_path_invalid_first_character
), errorInfo
);
3555 // do not allow problematic starting characters
3556 if (invalidFirstSubPathCharacters
.IndexOf(subPath
[0]) != -1) {
3557 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_location_path_invalid_first_character
), errorInfo
);
3560 // do not allow whitespace at end of subPath, as the OS
3561 // handles beginning and trailing whitespace inconsistently
3562 trimmedSubPath
= subPath
.TrimEnd();
3563 if (trimmedSubPath
.Length
!= subPath
.Length
) {
3564 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_location_path_invalid_last_character
), errorInfo
);
3567 // the file system ignores trailing '.', '\', or '/', so do not allow it in a location subpath specification
3568 if (invalidLastSubPathCharacters
.IndexOf(subPath
[subPath
.Length
-1]) != -1) {
3569 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_location_path_invalid_last_character
), errorInfo
);
3572 // combination of URI reserved characters and OS invalid filename characters, minus / (allowed reserved character)
3573 if (subPath
.IndexOfAny(s_invalidSubPathCharactersArray
) != -1) {
3574 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_location_path_invalid_character
), errorInfo
);
3582 // Return the SectionRecord for a section.
3583 // If the record does not exist, return null.
3584 // Throw cached errors if the section is in error and permitErrors == false.
3586 protected SectionRecord
GetSectionRecord(string configKey
, bool permitErrors
) {
3587 SectionRecord sectionRecord
;
3589 if (_sectionRecords
!= null) {
3590 sectionRecord
= (SectionRecord
) _sectionRecords
[configKey
];
3593 sectionRecord
= null;
3596 if (sectionRecord
!= null && !permitErrors
) {
3597 sectionRecord
.ThrowOnErrors();
3600 return sectionRecord
;
3603 // Return an existing SectionRecord, or create one if one does not exist.
3604 // Propagate the Locked bit from parent
3605 protected SectionRecord
EnsureSectionRecord(string configKey
, bool permitErrors
) {
3606 return EnsureSectionRecordImpl(configKey
, permitErrors
, true);
3609 // Return an existing SectionRecord, or create one if one does not exist.
3610 // Do not propagate the Locked bit from parent, because caller will check
3612 protected SectionRecord
EnsureSectionRecordUnsafe(string configKey
, bool permitErrors
) {
3613 return EnsureSectionRecordImpl(configKey
, permitErrors
, false);
3616 // Return an existing SectionRecord, or create one if one does not exist.
3617 // If desired, set the lock settings based on parent configs.
3618 private SectionRecord
EnsureSectionRecordImpl(string configKey
, bool permitErrors
, bool setLockSettings
) {
3619 SectionRecord sectionRecord
= GetSectionRecord(configKey
, permitErrors
);
3620 if (sectionRecord
== null) {
3622 if (_sectionRecords
== null) {
3623 _sectionRecords
= new Hashtable();
3626 sectionRecord
= GetSectionRecord(configKey
, permitErrors
);
3629 if (sectionRecord
== null) {
3631 sectionRecord
= new SectionRecord(configKey
);
3633 _sectionRecords
.Add(configKey
, sectionRecord
);
3637 if (setLockSettings
) {
3638 // Get the lock mode from parent configs
3639 OverrideMode parentMode
= OverrideMode
.Inherit
;
3640 OverrideMode childLockMode
= OverrideMode
.Inherit
;
3642 parentMode
= ResolveOverrideModeFromParent(configKey
, out childLockMode
);
3644 sectionRecord
.ChangeLockSettings(parentMode
, childLockMode
);
3648 return sectionRecord
;
3651 private bool HasFactoryRecords
{
3653 return _factoryRecords
!= null;
3657 internal FactoryRecord
GetFactoryRecord(string configKey
, bool permitErrors
) {
3658 FactoryRecord factoryRecord
;
3659 if (_factoryRecords
== null) {
3663 factoryRecord
= (FactoryRecord
) _factoryRecords
[configKey
];
3664 if (factoryRecord
!= null && !permitErrors
) {
3665 factoryRecord
.ThrowOnErrors();
3668 return factoryRecord
;
3671 // Only create a _factories hashtable when necessary.
3672 // Most config records won't have factories, so we can save 120 bytes
3673 // per record by creating the table on demand.
3674 protected Hashtable
EnsureFactories() {
3675 if (_factoryRecords
== null) {
3676 _factoryRecords
= new Hashtable();
3679 return _factoryRecords
;
3682 private ArrayList
EnsureLocationSections() {
3683 if (_locationSections
== null) {
3684 _locationSections
= new ArrayList();
3687 return _locationSections
;
3690 // Return true if there is no unique configuration information in this record.
3691 internal bool IsEmpty
{
3695 && !_initErrors
.HasErrors(false)
3696 && (_sectionRecords
== null || _sectionRecords
.Count
== 0)
3697 && (_factoryRecords
== null || _factoryRecords
.Count
== 0)
3698 && (_locationSections
== null || _locationSections
.Count
== 0);
3702 static internal string NormalizeConfigSource(string configSource
, IConfigErrorInfo errorInfo
) {
3703 if (String
.IsNullOrEmpty(configSource
)) {
3704 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_invalid_format
), errorInfo
);
3707 string trimmedConfigSource
= configSource
.Trim();
3708 if (trimmedConfigSource
.Length
!= configSource
.Length
) {
3709 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_invalid_format
), errorInfo
);
3712 if (configSource
.IndexOf('/') != -1) {
3713 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_invalid_chars
), errorInfo
);
3716 if (String
.IsNullOrEmpty(configSource
) || System
.IO
.Path
.IsPathRooted(configSource
)) {
3717 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_invalid_format
), errorInfo
);
3720 return configSource
;
3723 protected object MonitorStream(string configKey
, string configSource
, string streamname
) {
3725 if (_flags
[Closed
]) {
3729 StreamInfo streamInfo
= (StreamInfo
) ConfigStreamInfo
.StreamInfos
[streamname
];
3730 if (streamInfo
!= null) {
3731 if (streamInfo
.SectionName
!= configKey
) {
3732 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_source_cannot_be_shared
, streamname
));
3735 if (streamInfo
.IsMonitored
) {
3736 return streamInfo
.Version
;
3740 streamInfo
= new StreamInfo(configKey
, configSource
, streamname
);
3741 ConfigStreamInfo
.StreamInfos
.Add(streamname
, streamInfo
);
3746 // Call the host outside the lock to avoid deadlock.
3748 object version
= Host
.GetStreamVersion(streamname
);
3750 StreamChangeCallback callbackDelegate
= null;
3753 if (_flags
[Closed
]) {
3757 StreamInfo streamInfo
= (StreamInfo
) ConfigStreamInfo
.StreamInfos
[streamname
];
3758 if (streamInfo
.IsMonitored
) {
3759 return streamInfo
.Version
;
3762 streamInfo
.IsMonitored
= true;
3763 streamInfo
.Version
= version
;
3765 if (_flags
[SupportsChangeNotifications
]) {
3766 if (ConfigStreamInfo
.CallbackDelegate
== null) {
3767 ConfigStreamInfo
.CallbackDelegate
= new StreamChangeCallback(this.OnStreamChanged
);
3770 callbackDelegate
= ConfigStreamInfo
.CallbackDelegate
;
3774 if (_flags
[SupportsChangeNotifications
]) {
3775 Host
.StartMonitoringStreamForChanges(streamname
, callbackDelegate
);
3781 private void OnStreamChanged(string streamname
) {
3783 StreamInfo streamInfo
;
3790 streamInfo
= (StreamInfo
) ConfigStreamInfo
.StreamInfos
[streamname
];
3791 if (streamInfo
== null || !streamInfo
.IsMonitored
)
3794 sectionName
= streamInfo
.SectionName
;
3797 if (sectionName
== null) {
3798 notifyChanged
= true;
3801 FactoryRecord factoryRecord
= FindFactoryRecord(sectionName
, false);
3802 notifyChanged
= factoryRecord
.RestartOnExternalChanges
;
3805 if (notifyChanged
) {
3806 _configRoot
.FireConfigChanged(_configPath
);
3809 _configRoot
.ClearResult(this, sectionName
, false);
3813 // ValidateUniqueConfigSource
3815 // Validate that the configSource is unique for this particular
3816 // configKey. This looks up at the parents and makes sure it is
3817 // unique. It if is in a child, then it's check will find this
3818 // one. If it is in a peer, then we don't care as much, since it
3819 // will not affect Merge and UnMerge
3821 // See VSWhidbey 460219 for details.
3823 private void ValidateUniqueConfigSource(
3824 string configKey
, string configSourceStreamName
, string configSourceArg
, IConfigErrorInfo errorInfo
) {
3827 // Detect if another section in this file is using the same configSource
3828 // with has a different section name.
3831 if (ConfigStreamInfo
.HasStreamInfos
) {
3832 StreamInfo streamInfo
= (StreamInfo
) ConfigStreamInfo
.StreamInfos
[configSourceStreamName
];
3833 if (streamInfo
!= null && streamInfo
.SectionName
!= configKey
) {
3834 throw new ConfigurationErrorsException(
3835 SR
.GetString(SR
.Config_source_cannot_be_shared
, configSourceArg
),
3841 ValidateUniqueChildConfigSource(configKey
, configSourceStreamName
, configSourceArg
, errorInfo
);
3844 protected void ValidateUniqueChildConfigSource(
3845 string configKey
, string configSourceStreamName
, string configSourceArg
, IConfigErrorInfo errorInfo
) {
3848 // Detect if a parent config file is using the same config source stream.
3850 BaseConfigurationRecord current
;
3851 if (IsLocationConfig
) {
3852 current
= _parent
._parent
;
3858 while (!current
.IsRootConfig
) {
3860 if (current
.ConfigStreamInfo
.HasStreamInfos
) {
3861 StreamInfo streamInfo
= (StreamInfo
) current
.ConfigStreamInfo
.StreamInfos
[configSourceStreamName
];
3862 if (streamInfo
!= null) {
3863 throw new ConfigurationErrorsException(
3864 SR
.GetString(SR
.Config_source_parent_conflict
, configSourceArg
),
3870 current
= current
.Parent
;
3874 // Recursively clear the result.
3875 // If forceEvaluation == true, force a rescan of the config file to find
3877 // Requires the hierarchy lock to be acquired (hl)
3878 internal void hlClearResultRecursive(string configKey
, bool forceEvaluatation
) {
3879 SectionRecord sectionRecord
;
3881 // Refresh it's factory Record
3882 RefreshFactoryRecord(configKey
);
3884 // Clear any stored result in the section
3885 sectionRecord
= GetSectionRecord(configKey
, false);
3886 if (sectionRecord
!= null) {
3887 sectionRecord
.ClearResult();
3889 // VSWhidbey 535724: Need to clear all RawXml so that when GetSectionXmlReader
3890 // is called later it will reload the file.
3891 sectionRecord
.ClearRawXml();
3895 // If we need to reevaluate, add a dummy file input so
3896 // that we open the file on the next evaluation
3898 if (forceEvaluatation
&& !IsInitDelayed
&& !String
.IsNullOrEmpty(ConfigStreamInfo
.StreamName
)) {
3899 if (_flags
[SupportsPath
]) {
3900 throw ExceptionUtil
.UnexpectedError("BaseConfigurationRecord::hlClearResultRecursive");
3903 FactoryRecord factoryRecord
= FindFactoryRecord(configKey
, false);
3904 if (factoryRecord
!= null && !factoryRecord
.IsGroup
) {
3905 configKey
= factoryRecord
.ConfigKey
;
3906 sectionRecord
= EnsureSectionRecord(configKey
, false);
3907 if (!sectionRecord
.HasFileInput
) {
3908 SectionXmlInfo sectionXmlInfo
= new SectionXmlInfo(
3909 configKey
, _configPath
, _configPath
, null,
3910 ConfigStreamInfo
.StreamName
, 0, null, null,
3911 null, null, null, null,
3912 null, OverrideModeSetting
.LocationDefault
, false);
3914 SectionInput fileInput
= new SectionInput(sectionXmlInfo
, null);
3915 sectionRecord
.AddFileInput(fileInput
);
3921 if (_children
!= null) {
3922 IEnumerable children
= _children
.Values
;
3923 foreach (BaseConfigurationRecord child
in children
) {
3924 child
.hlClearResultRecursive(configKey
, forceEvaluatation
);
3929 // Returns a child record.
3930 // Requires the hierarchy lock to be acquired (hl)
3931 internal BaseConfigurationRecord
hlGetChild(string configName
) {
3932 if (_children
== null)
3935 return (BaseConfigurationRecord
) _children
[configName
];
3938 // Adds a child record.
3939 // Requires the hierarchy lock to be acquired (hl)
3940 internal void hlAddChild(string configName
, BaseConfigurationRecord child
) {
3941 if (_children
== null) {
3942 _children
= new Hashtable(StringComparer
.OrdinalIgnoreCase
);
3945 _children
.Add(configName
, child
);
3948 // Removes a child record.
3949 // Requires the hierarchy lock to be acquired (hl)
3950 internal void hlRemoveChild(string configName
) {
3951 if (_children
!= null) {
3952 _children
.Remove(configName
);
3956 // Removes true if a child record is needed for a
3957 // child config path.
3958 // Requires the hierarchy lock to be acquired (hl)
3959 internal bool hlNeedsChildFor(string configName
) {
3960 // Always return true for root config record
3964 // Never create a child record when the parent has an exception.
3965 if (HasInitErrors
) {
3969 string childConfigPath
= ConfigPathUtility
.Combine(_configPath
, configName
);
3972 using (Impersonate()) {
3973 // check host if required
3974 if (Host
.IsConfigRecordRequired(childConfigPath
)) {
3980 // Don't allow frames up the stack to run exception filters while impersonated.
3984 // see if there's a location
3985 if (_flags
[SupportsLocation
]) {
3986 BaseConfigurationRecord configRecord
= this;
3988 while (!configRecord
.IsRootConfig
) {
3989 if (configRecord
._locationSections
!= null) {
3990 configRecord
.ResolveLocationSections();
3991 foreach (LocationSectionRecord locationSectionRecord
in configRecord
._locationSections
) {
3992 if (UrlPath
.IsEqualOrSubpath(childConfigPath
, locationSectionRecord
.SectionXmlInfo
.TargetConfigPath
)) {
3998 configRecord
= configRecord
._parent
;
4005 // Close the record. An explicit close is needed
4006 // in order to stop monitoring streams used by
4007 // this record. Stream monitors cause this record
4008 // to be rooted in the GC heap.
4010 // Note that we purposely do not cleanup the child/parent
4011 // hierarchy. This is so that a config system which has
4012 // a pointer to this record can still call GetSection on
4013 // it while another thread closes it.
4014 internal void CloseRecursive() {
4015 if (!_flags
[Closed
]) {
4016 bool doClose
= false;
4017 HybridDictionary streamInfos
= null;
4018 StreamChangeCallback callbackDelegate
= null;
4021 if (!_flags
[Closed
]) {
4022 _flags
[Closed
] = true;
4025 if (!IsLocationConfig
&& ConfigStreamInfo
.HasStreamInfos
) {
4026 callbackDelegate
= ConfigStreamInfo
.CallbackDelegate
;
4027 streamInfos
= ConfigStreamInfo
.StreamInfos
;
4029 ConfigStreamInfo
.CallbackDelegate
= null;
4030 ConfigStreamInfo
.ClearStreamInfos();
4036 // no hierarchy lock is needed to access _children here,
4037 // as it has already been detached from the hierarchy tree
4038 if (_children
!= null) {
4039 foreach (BaseConfigurationRecord child
in _children
.Values
) {
4040 child
.CloseRecursive();
4044 if (streamInfos
!= null) {
4045 foreach (StreamInfo streamInfo
in streamInfos
.Values
) {
4046 if (streamInfo
.IsMonitored
) {
4047 Host
.StopMonitoringStreamForChanges(streamInfo
.StreamName
, callbackDelegate
);
4048 streamInfo
.IsMonitored
= false;
4056 internal string FindChangedConfigurationStream() {
4057 BaseConfigurationRecord configRecord
= this;
4058 while (!configRecord
.IsRootConfig
) {
4059 lock (configRecord
) {
4060 if (configRecord
.ConfigStreamInfo
.HasStreamInfos
) {
4061 foreach (StreamInfo streamInfo
in configRecord
.ConfigStreamInfo
.StreamInfos
.Values
) {
4062 if (streamInfo
.IsMonitored
&& HasStreamChanged(streamInfo
.StreamName
, streamInfo
.Version
)) {
4063 return streamInfo
.StreamName
;
4069 configRecord
= configRecord
._parent
;
4075 private bool HasStreamChanged(string streamname
, object lastVersion
) {
4076 object currentVersion
= Host
.GetStreamVersion(streamname
);
4078 if (lastVersion
!= null) {
4079 return (currentVersion
== null || !lastVersion
.Equals(currentVersion
));
4082 return currentVersion
!= null;
4086 // RuntimeConfigurationRecord will override it in order to Assert Fulltrust before calling the provider.
4087 // See VSWhidbey 429996.
4088 protected virtual string CallHostDecryptSection(string encryptedXml
, ProtectedConfigurationProvider protectionProvider
, ProtectedConfigurationSection protectedConfig
) {
4089 return Host
.DecryptSection(encryptedXml
, protectionProvider
, protectedConfig
);
4092 protected virtual XmlNode
CallHostProcessRawXml(XmlNode rawXml
, ConfigurationBuilder configBuilder
) {
4093 if (ConfigBuilderHost
!= null) {
4094 return ConfigBuilderHost
.ProcessRawXml(rawXml
, configBuilder
);
4100 protected virtual ConfigurationSection
CallHostProcessConfigurationSection(ConfigurationSection configSection
, ConfigurationBuilder configBuilder
) {
4101 if (ConfigBuilderHost
!= null) {
4102 return ConfigBuilderHost
.ProcessConfigurationSection(configSection
, configBuilder
);
4105 return configSection
;
4108 static internal string ValidateConfigBuilderAttribute(string configBuilder
, IConfigErrorInfo errorInfo
) {
4109 if (String
.IsNullOrEmpty(configBuilder
)) {
4110 throw new ConfigurationErrorsException(SR
.GetString(SR
.Config_builder_invalid_format
), errorInfo
);
4113 return configBuilder
;
4116 static internal string ValidateProtectionProviderAttribute(string protectionProvider
, IConfigErrorInfo errorInfo
) {
4117 if (String
.IsNullOrEmpty(protectionProvider
)) {
4118 throw new ConfigurationErrorsException(SR
.GetString(SR
.Protection_provider_invalid_format
), errorInfo
);
4121 return protectionProvider
;
4124 private ConfigXmlReader
DecryptConfigSection(ConfigXmlReader reader
, ProtectedConfigurationProvider protectionProvider
) {
4125 ConfigXmlReader clone
= reader
.Clone();
4126 IConfigErrorInfo err
= (IConfigErrorInfo
)clone
;
4127 string encryptedXml
= null;
4128 string clearTextXml
= null;
4129 XmlNodeType nodeType
;
4133 // Save the file and line at the top of the section
4135 string filename
= err
.Filename
;
4136 int lineNumber
= err
.LineNumber
;
4137 int sectionLineNumber
= lineNumber
;
4139 if (clone
.IsEmptyElement
) {
4140 throw new ConfigurationErrorsException(SR
.GetString(SR
.EncryptedNode_not_found
), filename
, lineNumber
);
4143 //////////////////////////////////////////////////////////
4144 // Find the <EncryptedData> node
4146 clone
.Read(); // Keep reading till we find a relavant node
4148 nodeType
= clone
.NodeType
;
4150 if (nodeType
== XmlNodeType
.Element
&& clone
.Name
== "EncryptedData") { // Found it!
4154 if (nodeType
== XmlNodeType
.EndElement
) {
4155 throw new ConfigurationErrorsException(SR
.GetString(SR
.EncryptedNode_not_found
), filename
, lineNumber
);
4157 else if (nodeType
!= XmlNodeType
.Comment
&& nodeType
!= XmlNodeType
.Whitespace
) {
4158 // some other unexpected content
4159 throw new ConfigurationErrorsException(SR
.GetString(SR
.EncryptedNode_is_in_invalid_format
), filename
, lineNumber
);
4163 //////////////////////////////////////////////////////////
4164 // Do the decryption
4166 // Save the line at the top of the <EncryptedData> node
4167 lineNumber
= err
.LineNumber
;
4169 encryptedXml
= clone
.ReadOuterXml();
4171 clearTextXml
= CallHostDecryptSection(encryptedXml
, protectionProvider
, ProtectedConfig
);
4172 } catch (Exception e
) {
4173 throw new ConfigurationErrorsException(SR
.GetString(SR
.Decryption_failed
, protectionProvider
.Name
, e
.Message
), e
, filename
, lineNumber
);
4176 // Detect if there is any XML left over after <EncryptedData>
4178 nodeType
= clone
.NodeType
;
4180 if (nodeType
== XmlNodeType
.EndElement
) {
4183 else if (nodeType
!= XmlNodeType
.Comment
&& nodeType
!= XmlNodeType
.Whitespace
) {
4184 // Got other unexpected content
4185 throw new ConfigurationErrorsException(SR
.GetString(SR
.EncryptedNode_is_in_invalid_format
), filename
, lineNumber
);
4187 } while (clone
.Read());
4189 // Create a new reader, using the position of the original reader
4190 return new ConfigXmlReader(clearTextXml
, filename
, sectionLineNumber
, true);
4193 private ConfigXmlReader
ProcessRawXml(ConfigXmlReader reader
, ConfigurationBuilder configBuilder
) {
4194 IConfigErrorInfo err
= (IConfigErrorInfo
)reader
;
4195 XmlNode processedXml
= null;
4197 string filename
= err
.Filename
;
4198 int lineNumber
= err
.LineNumber
;
4201 XmlDocument doc
= new XmlDocument();
4202 doc
.PreserveWhitespace
= true;
4203 doc
.LoadXml(reader
.RawXml
);
4204 processedXml
= CallHostProcessRawXml(doc
.DocumentElement
, configBuilder
);
4206 catch (Exception e
) {
4207 throw new ConfigurationErrorsException(SR
.GetString(SR
.ConfigBuilder_processXml_error
, configBuilder
.Name
, e
.Message
), e
, filename
, lineNumber
);
4210 return new ConfigXmlReader(processedXml
.OuterXml
, filename
, lineNumber
, true);
4215 // Retrieve the context for the config
4217 internal object ConfigContext
4221 if (!_flags
[ContextEvaluated
]) {
4222 // Retrieve context for Path
4223 _configContext
= Host
.CreateConfigurationContext(ConfigPath
, LocationSubPath
);
4224 _flags
[ContextEvaluated
] = true;
4227 return _configContext
;
4231 // ThrowIfParseErrors
4233 // Throw if there were parse errors detected
4235 private void ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors
) {
4236 schemaErrors
.ThrowIfErrors(ClassFlags
[ClassIgnoreLocalErrors
]);
4240 // RecordSupportsLocation
4242 // Does it make sense to put use location tags in this file?
4243 // In the web case this is true at any level. In the exe case
4244 // this is only true for machine.config (since machine.config
4245 // can really be used for any scenario)
4247 internal bool RecordSupportsLocation
{
4249 return (_flags
[SupportsLocation
] || IsMachineConfig
);
4254 // Note: Some of the per-attribute encryption stuff is moved to the end of the file to minimize
4255 // FI merging conflicts
4257 const string ConfigurationBuildersSectionTypeName
= "System.Configuration.ConfigurationBuildersSection, " + AssemblyRef
.SystemConfiguration
;
4258 internal const string RESERVED_SECTION_CONFIGURATION_BUILDERS
= "configBuilders";
4259 Type ConfigurationBuildersSectionType
= Type
.GetType(ConfigurationBuildersSectionTypeName
);
4260 const string ProtectedConfigurationSectionTypeName
= "System.Configuration.ProtectedConfigurationSection, " + AssemblyRef
.SystemConfiguration
;
4261 internal const string RESERVED_SECTION_PROTECTED_CONFIGURATION
= "configProtectedData";
4262 internal const string Microsoft_CONFIGURATION_SECTION
= ConfigurationStringConstants
.WinformsApplicationConfigurationSectionName
;
4263 const string SystemConfigurationSectionTypeName
= "System.Configuration.AppSettingsSection, " + AssemblyRef
.SystemConfiguration
;
4265 internal static bool IsImplicitSection(string configKey
) {
4266 if (string.Equals(configKey
, RESERVED_SECTION_PROTECTED_CONFIGURATION
, StringComparison
.Ordinal
) ||
4267 //string.Equals(configKey, RESERVED_SECTION_CONFIGURATION_BUILDERS, StringComparison.Ordinal) ||
4268 string.Equals(configKey
, Microsoft_CONFIGURATION_SECTION
, StringComparison
.Ordinal
)) {
4277 // Add implicit sections to the factory list.
4278 // If factoryList == null, then add to the config record's factory list.
4280 private void AddImplicitSections(Hashtable factoryList
) {
4281 // Add implicit sections to the factoryList if we're under the root
4282 // (e.g. if we're in machine.config)
4283 if (_parent
.IsRootConfig
) {
4285 if (factoryList
== null) {
4286 factoryList
= EnsureFactories();
4289 FactoryRecord factoryRecord
= (FactoryRecord
)factoryList
[RESERVED_SECTION_PROTECTED_CONFIGURATION
];
4291 // If the user has mistakenly declared an implicit section, we should leave the factoryRecord
4292 // alone because it contains the error and the error will be thrown later.
4293 if (factoryRecord
!= null) {
4294 Debug
.Assert(factoryRecord
.HasErrors
, "If the user has mistakenly declared an implicit section, we should have recorded an error.");
4297 factoryList
[RESERVED_SECTION_PROTECTED_CONFIGURATION
] =
4299 RESERVED_SECTION_PROTECTED_CONFIGURATION
, // configKey
4300 string.Empty
, // group
4301 RESERVED_SECTION_PROTECTED_CONFIGURATION
, // name
4302 ProtectedConfigurationSectionTypeName
, // factoryTypeName
4303 true, // allowLocation
4304 ConfigurationAllowDefinition
.Everywhere
, // allowDefinition
4305 ConfigurationAllowExeDefinition
.MachineToApplication
, // allowExeDefinition
4306 OverrideModeSetting
.SectionDefault
, // overrideModeDefault
4307 true, // restartOnExternalChanges
4308 true, // requirePermission
4309 true, // isFromTrustedConfig
4310 true, // isUndeclared
4315 factoryRecord
= (FactoryRecord
)factoryList
[Microsoft_CONFIGURATION_SECTION
];
4317 // If the user has mistakenly declared an implicit section, we should leave the factoryRecord
4318 // alone because it contains the error and the error will be thrown later.
4319 if (factoryRecord
!= null)
4321 Debug
.Assert(factoryRecord
.HasErrors
, "If the user has mistakenly declared an implicit section, we should have recorded an error.");
4325 factoryList
[Microsoft_CONFIGURATION_SECTION
] =
4327 Microsoft_CONFIGURATION_SECTION
, // configKey
4328 string.Empty
, // group
4329 Microsoft_CONFIGURATION_SECTION
, // name
4330 SystemConfigurationSectionTypeName
, // factoryTypeName
4331 true, // allowLocation
4332 ConfigurationAllowDefinition
.Everywhere
, // allowDefinition
4333 ConfigurationAllowExeDefinition
.MachineToApplication
, // allowExeDefinition
4334 OverrideModeSetting
.SectionDefault
, // overrideModeDefault
4335 true, // restartOnExternalChanges
4336 true, // requirePermission
4337 true, // isFromTrustedConfig
4338 true, // isUndeclared
4345 // We reserve all attribute names starting with config or lock
4346 internal static bool IsReservedAttributeName(string name
) {
4347 if (StringUtil
.StartsWith(name
, "config") ||
4348 StringUtil
.StartsWith(name
, "lock")) {
4356 protected class ConfigRecordStreamInfo
{
4357 private bool _hasStream
; // does the stream exist?
4358 private string _streamname
; // name of the stream of this record
4359 private object _streamVersion
; // version of the stream
4360 private Encoding _encoding
; // encoding of the stream
4361 private StreamChangeCallback _callbackDelegate
; // host delegate to callback to when stream has changed
4362 private HybridDictionary _streamInfos
; // streamname -> StreamInfo. It'll also contain the main stream pointed to by _streamname
4364 internal ConfigRecordStreamInfo() {
4366 _encoding
= Encoding
.UTF8
;
4369 internal bool HasStream
{
4370 get { return _hasStream; }
4371 set { _hasStream = value; }
4374 internal string StreamName
{
4375 get { return _streamname; }
4376 set { _streamname = value; }
4379 internal object StreamVersion
{
4380 get { return _streamVersion; }
4381 set { _streamVersion = value; }
4384 internal Encoding StreamEncoding
{
4385 get { return _encoding; }
4386 set { _encoding = value; }
4389 internal StreamChangeCallback CallbackDelegate
{
4390 get { return _callbackDelegate; }
4391 set { _callbackDelegate = value; }
4394 internal HybridDictionary StreamInfos
{
4396 if (_streamInfos
== null) {
4397 _streamInfos
= new HybridDictionary(true);
4400 return _streamInfos
;
4404 internal bool HasStreamInfos
{
4405 get { return _streamInfos != null; }
4408 internal void ClearStreamInfos() {
4409 _streamInfos
= null;
4414 // For Debugging only
4415 internal string[] Keys
{
4417 string[] keys
= new string[StreamInfos
.Count
];
4418 StreamInfos
.Keys
.CopyTo(keys
, 0);
4425 private class IndirectLocationInputComparer
: IComparer
<SectionInput
> {
4426 public int Compare(SectionInput x
, SectionInput y
) {
4427 // We have to sort the indirect inputs
4428 // 1. First by the location tag's target config path, and if they're the same,
4429 // 2. Then by the location tag's definition config path.
4431 // In the final sorted list, a child will be smaller than a parent.
4433 Debug
.Assert(x
.SectionXmlInfo
.ConfigKey
== y
.SectionXmlInfo
.ConfigKey
);
4435 if (Object
.ReferenceEquals(x
, y
)) {
4436 // Check if they're the same object.
4440 string xTargetConfigPath
= x
.SectionXmlInfo
.TargetConfigPath
;
4441 string yTargetConfigPath
= y
.SectionXmlInfo
.TargetConfigPath
;
4443 // First compare using location tag's target config path:
4444 if (UrlPath
.IsSubpath(xTargetConfigPath
, yTargetConfigPath
)) {
4445 // yTargetConfigPath is a child path of xTargetConfigPath, so y < x
4448 else if (UrlPath
.IsSubpath(yTargetConfigPath
, xTargetConfigPath
)) {
4449 // xTargetConfigPath is a child path of yTargetConfigPath, so x < y
4453 // Because all indirect inputs must be pointing to nodes along a
4454 // single branch of config hierarchy, so if the above two cases
4455 // aren't true, then the two target config path must be equal;
4456 // in another word, they should not be siblings.
4457 Debug
.Assert(StringUtil
.EqualsIgnoreCase(yTargetConfigPath
, xTargetConfigPath
));
4459 string xDefinitionConfigPath
= x
.SectionXmlInfo
.DefinitionConfigPath
;
4460 string yDefinitionConfigPath
= y
.SectionXmlInfo
.DefinitionConfigPath
;
4462 // Then compare using where the location tag is defined.
4463 if (UrlPath
.IsSubpath(xDefinitionConfigPath
, yDefinitionConfigPath
)) {
4464 // yDefinitionConfigPath is a child path of xDefinitionConfigPath, so y < x
4467 else if (UrlPath
.IsSubpath(yDefinitionConfigPath
, xDefinitionConfigPath
)) {
4468 // xDefinitionConfigPath is a child path of yDefinitionConfigPath, so x < y
4473 "It's not possible for two location input to come from the same config file and point to the same target");
4480 internal Configuration CurrentConfiguration
{
4482 return _configRoot
.CurrentConfiguration
;
4486 internal bool TypeStringTransformerIsSet
{
4488 return (CurrentConfiguration
== null) ? false : CurrentConfiguration
.TypeStringTransformerIsSet
;
4492 internal bool AssemblyStringTransformerIsSet
{
4494 return (CurrentConfiguration
== null) ? false : CurrentConfiguration
.AssemblyStringTransformerIsSet
;
4498 internal System
.Func
<string, string> TypeStringTransformer
{
4500 return (CurrentConfiguration
== null) ? null : CurrentConfiguration
.TypeStringTransformer
;
4504 internal System
.Func
<string, string> AssemblyStringTransformer
{
4506 return (CurrentConfiguration
== null) ? null : CurrentConfiguration
.AssemblyStringTransformer
;
4510 internal FrameworkName TargetFramework
{
4512 return (CurrentConfiguration
== null) ? null : CurrentConfiguration
.TargetFramework
;
4516 internal Stack SectionsStack
{
4518 return (CurrentConfiguration
== null) ? (new Stack()) : CurrentConfiguration
.SectionsStack
;