// 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();
}
}
}
}
}