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); } } } } }
private void ScanLocationSection(XmlUtil xmlUtil) { string locationSubPath = null; bool allowOverride = true; bool inheritInChildApp = true; int errorCountBeforeScan = xmlUtil.SchemaErrors.GlobalErrorCount; // Get the location section attributes while (xmlUtil.Reader.MoveToNextAttribute()) { switch (xmlUtil.Reader.Name) { case KEYWORD_LOCATION_PATH: locationSubPath = xmlUtil.Reader.Value; break; case KEYWORD_LOCATION_ALLOWOVERRIDE: xmlUtil.VerifyAndGetBooleanAttribute( ExceptionAction.Global, true, out allowOverride); break; case KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS: xmlUtil.VerifyAndGetBooleanAttribute( ExceptionAction.Global, true, out inheritInChildApp); break; default: xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Global); break; } } xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element try { locationSubPath = NormalizeLocationSubPath(locationSubPath, xmlUtil); // // if (locationSubPath == null && !inheritInChildApp && Host.IsDefinitionAllowed(_configPath, ConfigurationAllowDefinition.MachineToWebRoot, ConfigurationAllowExeDefinition.MachineOnly)) { throw new ConfigurationErrorsException(SR.GetString(SR.Location_invalid_inheritInChildApplications_in_machine_or_root_web_config), xmlUtil); } } catch (ConfigurationErrorsException ce) { xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Global); } // Skip over this location section if there are errors if (xmlUtil.SchemaErrors.GlobalErrorCount > errorCountBeforeScan) { xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific); return; } // Scan elements of the location section if the path is the current path. // We do not add <location path="." /> to the _locationSections list. if (locationSubPath == null) { ScanSectionsRecursive(xmlUtil, string.Empty, true, null, !allowOverride, !inheritInChildApp); return; } // Skip over location sections for client config if (!_flags[SupportsLocation]) { xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific); return; } AddLocation(locationSubPath); ScanSectionsRecursive(xmlUtil, string.Empty, true, locationSubPath, !allowOverride, !inheritInChildApp); }
// Scans the <configSections> section of a configuration file. The function is recursive // to traverse arbitrarily nested config groups. // // <sectionGroup name="foo"> // <sectionGroup name="bar"> // <section name="fooBarSection" type="..." /> // ... // // Note: This function valiates that the factory record has not been // declared before in a parent record. (it does not check // current record, which allows you to update list) // private void ScanFactoriesRecursive(XmlUtil xmlUtil, string parentConfigKey, Hashtable factoryList) { // discard any accumulated local errors xmlUtil.SchemaErrors.ResetLocalErrors(); int depth = xmlUtil.Reader.Depth; xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific); while (xmlUtil.Reader.Depth == depth + 1) { bool positionedAtNextElement = false; switch (xmlUtil.Reader.Name) { // // Handle <sectionGroup name="groupName" [type="typename"] /> // case KEYWORD_SECTIONGROUP: { string tagName = null; string typeName = null; int lineNumber = xmlUtil.Reader.LineNumber; while (xmlUtil.Reader.MoveToNextAttribute()) { switch (xmlUtil.Reader.Name) { case KEYWORD_SECTIONGROUP_NAME: tagName = xmlUtil.Reader.Value; VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false); break; case KEYWORD_SECTIONGROUP_TYPE: xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName); break; default: xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local); break; } } xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element if (!xmlUtil.VerifyRequiredAttribute( tagName, KEYWORD_SECTIONGROUP_NAME, ExceptionAction.NonSpecific)) { // // Without a name, we cannot continue parsing the sections and groups within. // Skip the entire section. // xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true); xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific); } else { string configKey = CombineConfigKey(parentConfigKey, tagName); FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey]; if (factoryRecord != null) { // Error: duplicate sectionGroup declaration xmlUtil.SchemaErrors.AddError( new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil), ExceptionAction.Local); } else { FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true); if (parentFactoryRecord != null) { configKey = parentFactoryRecord.ConfigKey; // make sure that an ancestor has not defined a section with the same name as the group if ( !parentFactoryRecord.IsGroup || !parentFactoryRecord.IsEquivalentSectionGroupFactory(Host, typeName)) { xmlUtil.SchemaErrors.AddError( new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil), ExceptionAction.Local); parentFactoryRecord = null; } } if (parentFactoryRecord != null) { factoryRecord = parentFactoryRecord.CloneSectionGroup(typeName, xmlUtil.Filename, lineNumber); } else { factoryRecord = new FactoryRecord(configKey, parentConfigKey, tagName, typeName, xmlUtil.Filename, lineNumber); } factoryList[configKey] = factoryRecord; } // Add any errors we may have encountered factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true)); // continue recursive scan ScanFactoriesRecursive(xmlUtil, configKey, factoryList); } continue; } case KEYWORD_SECTION: { string tagName = null; string typeName = null; ConfigurationAllowDefinition allowDefinition = ConfigurationAllowDefinition.Everywhere; ConfigurationAllowExeDefinition allowExeDefinition = ConfigurationAllowExeDefinition.MachineToApplication; bool allowLocation = true; bool restartOnExternalChanges = true; bool requirePermission = true; bool gotType = false; // parse section attributes int lineNumber = xmlUtil.Reader.LineNumber; while (xmlUtil.Reader.MoveToNextAttribute()) { switch (xmlUtil.Reader.Name) { case KEYWORD_SECTION_NAME: tagName = xmlUtil.Reader.Value; VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false); break; case KEYWORD_SECTION_TYPE: xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName); gotType = true; break; case KEYWORD_SECTION_ALLOWLOCATION: xmlUtil.VerifyAndGetBooleanAttribute( ExceptionAction.Local, true, out allowLocation); break; case KEYWORD_SECTION_ALLOWEXEDEFINITION: try { allowExeDefinition = AllowExeDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil); } catch (ConfigurationException ce) { xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local); } break; case KEYWORD_SECTION_ALLOWDEFINITION: try { allowDefinition = AllowDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil); } catch (ConfigurationException ce) { xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local); } break; case KEYWORD_SECTION_RESTARTONEXTERNALCHANGES: xmlUtil.VerifyAndGetBooleanAttribute( ExceptionAction.Local, true, out restartOnExternalChanges); break; case KEYWORD_SECTION_REQUIREPERMISSION: xmlUtil.VerifyAndGetBooleanAttribute( ExceptionAction.Local, true, out requirePermission); break; default: xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local); break; } } xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element if (!xmlUtil.VerifyRequiredAttribute( tagName, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) { // // Without a name, we cannot continue to create a factoryRecord. // xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true); } else { // Verify that the Type attribute was present. // Note that 'typeName' will be null if the attribute was present // but specified as an empty string. if (!gotType) { xmlUtil.AddErrorRequiredAttribute(KEYWORD_SECTION_TYPE, ExceptionAction.Local); } string configKey = CombineConfigKey(parentConfigKey, tagName); FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey]; if (factoryRecord != null) { // Error: duplicate section declaration xmlUtil.SchemaErrors.AddError( new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil), ExceptionAction.Local); } else { FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true); if (parentFactoryRecord != null) { configKey = parentFactoryRecord.ConfigKey; // make sure that an ancestor has not defined a section with the same name as the group if (parentFactoryRecord.IsGroup) { xmlUtil.SchemaErrors.AddError( new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil), ExceptionAction.Local); parentFactoryRecord = null; } else if (!parentFactoryRecord.IsEquivalentSectionFactory(Host, typeName, allowLocation, allowDefinition, allowExeDefinition, restartOnExternalChanges, requirePermission)) { xmlUtil.SchemaErrors.AddError( new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil), ExceptionAction.Local); parentFactoryRecord = null; } } if (parentFactoryRecord != null) { // Note - Clone will propagate the IsFromTrustedConfigRecord bit, // which is what we want - if this record is a duplicate of an ancestor, // the ancestor may be from a trusted config record. factoryRecord = parentFactoryRecord.CloneSection(xmlUtil.Filename, lineNumber); } else { factoryRecord = new FactoryRecord( configKey, parentConfigKey, tagName, typeName, allowLocation, allowDefinition, allowExeDefinition, restartOnExternalChanges, requirePermission, _flags[IsTrusted], false, // isUndeclared xmlUtil.Filename, lineNumber); } factoryList[configKey] = factoryRecord; } // Add any errors we may have encountered factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true)); } } break; case KEYWORD_REMOVE: { string name = null; int lineNumber = -1; // parse attributes while (xmlUtil.Reader.MoveToNextAttribute()) { if (xmlUtil.Reader.Name != KEYWORD_SECTION_NAME) { xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.NonSpecific); } name = xmlUtil.Reader.Value; lineNumber = xmlUtil.Reader.LineNumber; } xmlUtil.Reader.MoveToElement(); if (xmlUtil.VerifyRequiredAttribute( name, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) { VerifySectionName(name, xmlUtil, ExceptionAction.NonSpecific, false); } } break; case KEYWORD_CLEAR: { xmlUtil.VerifyNoUnrecognizedAttributes(ExceptionAction.NonSpecific); } break; default: xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific); xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific); positionedAtNextElement = true; break; } if (!positionedAtNextElement) { // Need to read to next element, and check if an unrecognized child // element is found. xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific); // unrecognized children are not allowed in <configSections> if (xmlUtil.Reader.Depth > depth + 1) { xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific); // Lets try to backup to where we are suppose to be while (xmlUtil.Reader.Depth > (depth + 1)) { xmlUtil.ReadToNextElement(); } } } } }