private void ScanSectionsRecursive(
XmlUtil xmlUtil, string parentConfigKey, bool inLocation, string locationSubPath,
bool lockChildren, bool skipInChildApps) {
// discard any accumulated local errors
xmlUtil.SchemaErrors.ResetLocalErrors();
int depth;
// only move to child nodes when not on first level (we've already passed the first <configsections>)
if (parentConfigKey.Length == 0 && !inLocation) {
depth = 0;
}
else {
depth = xmlUtil.Reader.Depth;
xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
}
while (xmlUtil.Reader.Depth == depth + 1) {
string tagName = xmlUtil.Reader.Name;
//
// Check for reserved elements before looking up the factory,
// which may have the same name if it is in error.
//
if (tagName == KEYWORD_CONFIGSECTIONS) {
// Error: duplicate <configSections> tag, or <configSections> not the first tag under <configuration>
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Config_client_config_too_many_configsections_elements, tagName), xmlUtil),
ExceptionAction.NonSpecific);
xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
continue;
}
if (tagName == KEYWORD_LOCATION) {
if (parentConfigKey.Length > 0 || inLocation) {
// Error: <location> section not at top level
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Config_location_location_not_allowed), xmlUtil),
ExceptionAction.Global);
xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
}
else {
// Recurse into the location section
ScanLocationSection(xmlUtil);
}
continue;
}
string configKey = CombineConfigKey(parentConfigKey, tagName);
FactoryRecord factoryRecord = FindFactoryRecord(configKey, true);
if (factoryRecord == null) {
//
//
if (!ClassFlags[ClassIgnoreLocalErrors]) {
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Config_unrecognized_configuration_section, configKey), xmlUtil),
ExceptionAction.Local);
}
VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false);
factoryRecord = new FactoryRecord(
configKey,
parentConfigKey,
tagName,
typeof(DefaultSection).AssemblyQualifiedName,
true, // allowLocation
ConfigurationAllowDefinition.Everywhere,
ConfigurationAllowExeDefinition.MachineToRoamingUser,
true, // restartOnExternalChanges
true, // requirePermission
_flags[IsTrusted],
true, // isUndeclared
null,
-1);
// Add any errors we may have encountered to the factory record,
// so that child config that also refer to this unrecognized section
// get the error.
factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));
// Add the factory to the list of factories
EnsureFactories()[configKey] = factoryRecord;
}
if (factoryRecord.IsGroup) {
//
// Section Group
//
if (factoryRecord.HasErrors) {
xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
}
else {
if (xmlUtil.Reader.AttributeCount > 0) {
while (xmlUtil.Reader.MoveToNextAttribute()) {
if (IsReservedAttributeName(xmlUtil.Reader.Name)) {
xmlUtil.AddErrorReservedAttribute(ExceptionAction.NonSpecific);
}
}
xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element
}
// Recurse into group definition
ScanSectionsRecursive(xmlUtil, configKey, inLocation, locationSubPath, lockChildren, skipInChildApps);
}
}
else {
//
// Section
//
configKey = factoryRecord.ConfigKey;
string fileName = xmlUtil.Filename;
int lineNumber = xmlUtil.LineNumber;
string rawXml = null;
string configSource = null;
string configSourceStreamName = null;
object configSourceStreamVersion = null;
string protectionProviderName = null;
bool isSectionLocked = false;
bool positionedAtNextElement = false;
bool isFileInput = (locationSubPath == null);
if (!factoryRecord.HasErrors) {
// We have a valid factoryRecord for a section
if (inLocation && factoryRecord.AllowLocation == false) {
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Config_section_cannot_be_used_in_location), xmlUtil),
ExceptionAction.Local);
}
// Verify correctness for file inputs.
if (isFileInput) {
// Verify that the section is unique
SectionRecord sectionRecord = GetSectionRecord(configKey, true);
if (sectionRecord != null && sectionRecord.HasFileInput) {
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Config_sections_must_be_unique), xmlUtil),
ExceptionAction.Local);
}
// Verify that the definition is allowed.
try {
VerifyDefinitionAllowed(factoryRecord, _configPath, xmlUtil);
}
catch (ConfigurationException ce) {
xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
}
}
// Verify that section is unlocked, both for file and location inputs.
try {
VerifySectionUnlocked(configKey, xmlUtil);
}
catch (ConfigurationException ce) {
isSectionLocked = true;
xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
}
// check for configSource or protectionProvider
if (xmlUtil.Reader.AttributeCount >= 1) {
// First do all the attributes reading without advancing the reader.
string configSourceAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_CONFIGSOURCE);
if (configSourceAttribute != null) {
try {
configSource = NormalizeConfigSource(configSourceAttribute, xmlUtil);
}
catch (ConfigurationException ce) {
xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
}
if (xmlUtil.Reader.AttributeCount != 1) {
// Error: elements with configSource should not have other attributes
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Config_source_syntax_error), xmlUtil),
ExceptionAction.Local);
}
}
string protectionProviderAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_PROTECTION_PROVIDER);
if (protectionProviderAttribute != null) {
try {
protectionProviderName = ValidateProtectionProviderAttribute(protectionProviderAttribute, xmlUtil);
}
catch (ConfigurationException ce) {
xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
}
if (xmlUtil.Reader.AttributeCount != 1) {
// Error: elements with protectionProvider should not have other attributes
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Protection_provider_syntax_error), xmlUtil),
ExceptionAction.Local);
}
}
// The 2nd part of the configSource check requires advancing the reader.
// Please note that this part should be done only AFTER all other attributes
// checking are done.
if (configSourceAttribute != null) {
if (!xmlUtil.Reader.IsEmptyElement) {
while (xmlUtil.Reader.Read()) {
XmlNodeType t = xmlUtil.Reader.NodeType;
if (t == XmlNodeType.EndElement)
break;
if (t != XmlNodeType.Comment) {
// Error: elements with configSource should not subelements other than comments
xmlUtil.SchemaErrors.AddError(
new ConfigurationErrorsException(SR.GetString(SR.Config_source_syntax_error), xmlUtil),
ExceptionAction.Local);
if (t == XmlNodeType.Element) {
xmlUtil.StrictSkipToOurParentsEndElement(ExceptionAction.NonSpecific);
}
else {
xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
}
positionedAtNextElement = true;
break;
}
}
}
}
}
if (configSource != null) {
try {
try {
configSourceStreamName = Host.GetStreamNameForConfigSource(ConfigStreamInfo.StreamName, configSource);
}
catch (Exception e) {
throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_source_invalid), e, xmlUtil);
}
ValidateUniqueConfigSource(configKey, configSourceStreamName, configSource, xmlUtil);
configSourceStreamVersion = MonitorStream(configKey, configSource, configSourceStreamName);
}
catch (ConfigurationException ex) {
xmlUtil.SchemaErrors.AddError(ex, ExceptionAction.Local);
}
}
//
// prefetch the raw xml
//
if (!xmlUtil.SchemaErrors.HasLocalErrors) {
if (configSource == null && ShouldPrefetchRawXml(factoryRecord)) {
Debug.Assert(!positionedAtNextElement, "!positionedAtNextElement");
rawXml = xmlUtil.CopySection();
if (xmlUtil.Reader.NodeType != XmlNodeType.Element) {
xmlUtil.VerifyIgnorableNodeType(ExceptionAction.NonSpecific);
xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
}
positionedAtNextElement = true;
}
}
}
// Get the list of errors before advancing the reader
List<ConfigurationException> localErrors = xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(isFileInput);
// advance the reader to the next element
if (!positionedAtNextElement) {
xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
}
// Add the input either to:
// 1. The file input at the current config level, or
// 2. LocationSections, where it will be used in sub paths
bool addInput = true;
if (isFileInput) {
// If isFileInput==true, Input added will be used against this config level.
// Need to check if we need to skip it due to inheritInChildApplications.
if (ShouldSkipDueToInheritInChildApplications(skipInChildApps)) {
addInput = false;
}
}
else {
if (!_flags[SupportsLocation]) {
// Skip if we have a location input but we don't support location tag.
addInput = false;
}
}
if (addInput) {
string targetConfigPath = (locationSubPath == null) ? _configPath : null;
SectionXmlInfo sectionXmlInfo = new SectionXmlInfo(
configKey, _configPath, targetConfigPath, locationSubPath,
fileName, lineNumber, ConfigStreamInfo.StreamVersion, rawXml,
configSource, configSourceStreamName, configSourceStreamVersion,
protectionProviderName, lockChildren, skipInChildApps);
if (locationSubPath == null) {
//
// Add this file input to the section record
//
// We've already checked for locked above, so use skip the second check
// and set the locked bit.
SectionRecord sectionRecord = EnsureSectionRecordUnsafe(configKey, true);
Debug.Assert(!sectionRecord.Locked || isSectionLocked,
"!sectionRecord.Locked || isSectionLocked");
if (isSectionLocked) {
sectionRecord.Locked = true;
}
SectionInput fileInput = new SectionInput(sectionXmlInfo, localErrors);
sectionRecord.AddFileInput(fileInput);
}
else {
//
// Add this location input to this list of location sections
//
LocationSectionRecord locationSectionRecord = new LocationSectionRecord(sectionXmlInfo, localErrors);
EnsureLocationSections().Add(locationSectionRecord);
}
}
}
}
}