// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
namespace WixToolset.Util
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using WixToolset.Data;
using WixToolset.Data.WindowsInstaller;
using WixToolset.Extensibility;
using WixToolset.Util.Symbols;
///
/// The decompiler for the WiX Toolset Utility Extension.
///
internal sealed class UtilDecompiler : BaseWindowsInstallerDecompilerExtension
{
public override IReadOnlyCollection TableDefinitions => UtilTableDefinitions.All;
private static readonly Dictionary CustomActionMapping = new Dictionary()
{
{ "Wix4BroadcastEnvironmentChange_X86", UtilConstants.BroadcastEnvironmentChange },
{ "Wix4BroadcastEnvironmentChange_X64", UtilConstants.BroadcastEnvironmentChange },
{ "Wix4BroadcastEnvironmentChange_ARM64", UtilConstants.BroadcastEnvironmentChange },
{ "Wix4BroadcastSettingChange_X86", UtilConstants.BroadcastSettingChange },
{ "Wix4BroadcastSettingChange_X64", UtilConstants.BroadcastSettingChange },
{ "Wix4BroadcastSettingChange_ARM64", UtilConstants.BroadcastSettingChange },
{ "Wix4CheckRebootRequired_X86", UtilConstants.CheckRebootRequired },
{ "Wix4CheckRebootRequired_X64", UtilConstants.CheckRebootRequired },
{ "Wix4CheckRebootRequired_ARM64", UtilConstants.CheckRebootRequired },
{ "Wix4QueryNativeMachine_X86", UtilConstants.QueryNativeMachine },
{ "Wix4QueryNativeMachine_X64", UtilConstants.QueryNativeMachine },
{ "Wix4QueryNativeMachine_ARM64", UtilConstants.QueryNativeMachine },
{ "Wix4QueryOsDriverInfo_X86", UtilConstants.QueryWindowsDriverInfo },
{ "Wix4QueryOsDriverInfo_X64", UtilConstants.QueryWindowsDriverInfo },
{ "Wix4QueryOsDriverInfo_ARM64", UtilConstants.QueryWindowsDriverInfo },
{ "Wix4QueryOsInfo_X86", UtilConstants.QueryWindowsSuiteInfo },
{ "Wix4QueryOsInfo_X64", UtilConstants.QueryWindowsSuiteInfo },
{ "Wix4QueryOsInfo_ARM64", UtilConstants.QueryWindowsSuiteInfo },
};
private IReadOnlyCollection customActionNames;
///
/// Called at the beginning of the decompilation of a database.
///
/// The collection of all tables.
public override void PreDecompileTables(TableIndexedCollection tables)
{
this.RememberCustomActionNames(tables);
this.CleanupSecureCustomProperties(tables);
this.CleanupInternetShortcutRemoveFileTables(tables);
}
private void RememberCustomActionNames(TableIndexedCollection tables)
{
var customActionTable = tables["CustomAction"];
this.customActionNames = customActionTable?.Rows.Select(r => r.GetPrimaryKey()).Distinct().ToList() ?? (IReadOnlyCollection)Array.Empty();
}
///
/// Decompile the SecureCustomProperties field to PropertyRefs for known extension properties.
///
///
/// If we've referenced any of the suite or directory properties, add
/// a PropertyRef to refer to the Property (and associated custom action)
/// from the extension's library. Then remove the property from
/// SecureCustomExtensions property so later decompilation won't create
/// new Property elements.
///
/// The collection of all tables.
private void CleanupSecureCustomProperties(TableIndexedCollection tables)
{
var propertyTable = tables["Property"];
if (null != propertyTable)
{
foreach (var row in propertyTable.Rows)
{
if ("SecureCustomProperties" == row[0].ToString())
{
var remainingProperties = new StringBuilder();
var secureCustomProperties = row[1].ToString().Split(';');
foreach (var property in secureCustomProperties)
{
if (property.StartsWith("WIX_SUITE_", StringComparison.Ordinal) || property.StartsWith("WIX_DIR_", StringComparison.Ordinal)
|| property.StartsWith("WIX_ACCOUNT_", StringComparison.Ordinal))
{
this.DecompilerHelper.AddElementToRoot("PropertyRef", new XAttribute("Id", property));
}
else
{
if (0 < remainingProperties.Length)
{
remainingProperties.Append(";");
}
remainingProperties.Append(property);
}
}
row[1] = remainingProperties.ToString();
break;
}
}
}
}
///
/// Remove RemoveFile rows that the InternetShortcut compiler extension adds for us.
///
/// The collection of all tables.
private void CleanupInternetShortcutRemoveFileTables(TableIndexedCollection tables)
{
// index the WixInternetShortcut table
var wixInternetShortcutTable = tables["WixInternetShortcut"];
var wixInternetShortcuts = new Hashtable();
if (null != wixInternetShortcutTable)
{
foreach (var row in wixInternetShortcutTable.Rows)
{
wixInternetShortcuts.Add(row.GetPrimaryKey(), row);
}
}
// remove the RemoveFile rows with primary keys that match the WixInternetShortcut table's
var removeFileTable = tables["RemoveFile"];
if (null != removeFileTable)
{
for (var i = removeFileTable.Rows.Count - 1; 0 <= i; i--)
{
if (null != wixInternetShortcuts[removeFileTable.Rows[i][0]])
{
removeFileTable.Rows.RemoveAt(i);
}
}
}
}
///
/// Decompiles an extension table.
///
/// The table to decompile.
public override bool TryDecompileTable(Table table)
{
switch (table.Name)
{
case "WixCloseApplication":
case "Wix4CloseApplication":
this.DecompileWixCloseApplicationTable(table);
break;
case "WixRemoveFolderEx":
case "Wix4RemoveFolderEx":
this.DecompileWixRemoveFolderExTable(table);
break;
case "WixRestartResource":
case "Wix4RestartResource":
this.DecompileWixRestartResourceTable(table);
break;
case "FileShare":
case "Wix4FileShare":
this.DecompileFileShareTable(table);
break;
case "FileSharePermissions":
case "Wix4FileSharePermissions":
this.DecompileFileSharePermissionsTable(table);
break;
case "WixInternetShortcut":
case "Wix4InternetShortcut":
this.DecompileWixInternetShortcutTable(table);
break;
case "Group":
case "Wix4Group":
this.DecompileGroupTable(table);
break;
case "Perfmon":
case "Wix4Perfmon":
this.DecompilePerfmonTable(table);
break;
case "PerfmonManifest":
case "Wix4PerfmonManifest":
this.DecompilePerfmonManifestTable(table);
break;
case "EventManifest":
case "Wix4EventManifest":
this.DecompileEventManifestTable(table);
break;
case "SecureObjects":
case "Wix4SecureObjects":
this.DecompileSecureObjectsTable(table);
break;
case "ServiceConfig":
case "Wix4ServiceConfig":
this.DecompileServiceConfigTable(table);
break;
case "User":
case "Wix4User":
this.DecompileUserTable(table);
break;
case "UserGroup":
case "Wix4UserGroup":
this.DecompileUserGroupTable(table);
break;
case "XmlConfig":
case "Wix4XmlConfig":
this.DecompileXmlConfigTable(table);
break;
case "XmlFile":
case "Wix4XmlFile":
// XmlFile decompilation has been moved to FinalizeXmlFileTable function
break;
default:
return false;
}
return true;
}
///
/// Finalize decompilation.
///
/// The collection of all tables.
public override void PostDecompileTables(TableIndexedCollection tables)
{
this.FinalizeCustomActions();
this.FinalizePerfmonTable(tables);
this.FinalizePerfmonManifestTable(tables);
this.FinalizeSecureObjectsTable(tables);
this.FinalizeServiceConfigTable(tables);
this.FinalizeXmlConfigTable(tables);
this.FinalizeXmlFileTable(tables);
this.FinalizeEventManifestTable(tables);
}
///
/// Decompile the WixCloseApplication table.
///
/// The table to decompile.
private void DecompileWixCloseApplicationTable(Table table)
{
foreach (var row in table.Rows)
{
var attribute = row.FieldAsNullableInteger(4) ?? 0x2;
this.DecompilerHelper.AddElementToRoot(UtilConstants.CloseApplicationName,
new XAttribute("Id", row.FieldAsString(0)),
new XAttribute("Target", row.FieldAsString(1)),
AttributeIfNotNull("Description", row, 2),
AttributeIfNotNull("Content", row, 3),
AttributeIfNotNull("CloseMessage", 0x1 == (attribute & 0x1)),
AttributeIfNotNull("RebootPrompt", 0x2 == (attribute & 0x2)),
AttributeIfNotNull("ElevatedCloseMessage", 0x4 == (attribute & 0x4)),
NumericAttributeIfNotNull("Sequence", row, 5),
AttributeIfNotNull("Property", row, 6)
);
}
}
///
/// Decompile the WixRemoveFolderEx table.
///
/// The table to decompile.
private void DecompileWixRemoveFolderExTable(Table table)
{
foreach (var row in table.Rows)
{
var on = String.Empty;
var installMode = row.FieldAsInteger(3);
switch (installMode)
{
case (int)WixRemoveFolderExInstallMode.Install:
on = "install";
break;
case (int)WixRemoveFolderExInstallMode.Uninstall:
on = "uninstall";
break;
case (int)WixRemoveFolderExInstallMode.Both:
on = "both";
break;
default:
this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "InstallMode", installMode));
break;
}
var removeFolder = new XElement(UtilConstants.RemoveFolderExName,
AttributeIfNotNull("Id", row, 0),
AttributeIfNotNull("Property", row, 2),
AttributeIfNotNull("On", on)
);
// Add to the appropriate Component or section element.
var componentId = row.FieldAsString(1);
if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
{
component.Add(removeFolder);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
}
}
}
///
/// Decompile the WixRestartResource table.
///
/// The table to decompile.
private void DecompileWixRestartResourceTable(Table table)
{
foreach (var row in table.Rows)
{
var restartResource = new XElement(UtilConstants.RestartResourceName,
new XAttribute("Id", row.FieldAsString(0)));
// Determine the resource type and set accordingly.
var resource = row.FieldAsString(2);
var attributes = row.FieldAsInteger(3);
var type = (WixRestartResourceAttributes)attributes;
switch (type)
{
case WixRestartResourceAttributes.Filename:
restartResource.Add(new XAttribute("Path", resource));
break;
case WixRestartResourceAttributes.ProcessName:
restartResource.Add(new XAttribute("ProcessName", resource));
break;
case WixRestartResourceAttributes.ServiceName:
restartResource.Add(new XAttribute("ServiceName", resource));
break;
default:
this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Attributes", attributes));
break;
}
// Add to the appropriate Component or section element.
var componentId = row.FieldAsString(1);
if (!String.IsNullOrEmpty(componentId))
{
if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
{
component.Add(restartResource);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
}
}
else
{
this.DecompilerHelper.AddElementToRoot(restartResource);
}
}
}
///
/// Decompile the FileShare table.
///
/// The table to decompile.
private void DecompileFileShareTable(Table table)
{
foreach (var row in table.Rows)
{
var fileShare = new XElement(UtilConstants.FileShareName,
new XAttribute("Id", row.FieldAsString(0)),
new XAttribute("Name", row.FieldAsString(1)),
AttributeIfNotNull("Description", row, 3)
);
// the Directory_ column is set by the parent Component
// the User_ and Permissions columns are deprecated
if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(2), out var component))
{
component.Add(fileShare);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", (string)row[2], "Component"));
}
this.DecompilerHelper.IndexElement(row, fileShare);
}
}
///
/// Decompile the FileSharePermissions table.
///
/// The table to decompile.
private void DecompileFileSharePermissionsTable(Table table)
{
foreach (var row in table.Rows)
{
var fileSharePermission = new XElement(UtilConstants.FileSharePermissionName,
new XAttribute("User", row.FieldAsString(1)));
this.AddPermissionAttributes(fileSharePermission, row, 2, UtilConstants.FolderPermissions);
if (this.DecompilerHelper.TryGetIndexedElement("Wix4FileShare", row.FieldAsString(0), out var fileShare) ||
this.DecompilerHelper.TryGetIndexedElement("FileShare", row.FieldAsString(0), out fileShare))
{
fileShare.Add(fileSharePermission);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "FileShare_", (string)row[0], "Wix4FileShare"));
}
}
}
///
/// Decompile the Group table.
///
/// The table to decompile.
private void DecompileGroupTable(Table table)
{
foreach (var row in table.Rows)
{
if (null != row[1])
{
this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Component_", (string)row[1]));
}
this.DecompilerHelper.AddElementToRoot(UtilConstants.GroupName,
new XAttribute("Id", row.FieldAsString(0)),
new XAttribute("Name", row.FieldAsString(1)),
AttributeIfNotNull("Domain", row, 3)
);
}
}
///
/// Decompile the WixInternetShortcut table.
///
/// The table to decompile.
private void DecompileWixInternetShortcutTable(Table table)
{
foreach (var row in table.Rows)
{
var type = String.Empty;
var shortcutType = (UtilCompiler.InternetShortcutType)row.FieldAsInteger(5);
switch (shortcutType)
{
case UtilCompiler.InternetShortcutType.Link:
type = "link";
break;
case UtilCompiler.InternetShortcutType.Url:
type = "url";
break;
}
var internetShortcut = new XElement(UtilConstants.InternetShortcutName,
new XAttribute("Id", row.FieldAsString(0)),
new XAttribute("Directory", row.FieldAsString(2)),
new XAttribute("Name", Path.GetFileNameWithoutExtension(row.FieldAsString(3))), // remove .lnk/.url extension because compiler extension adds it back for us
new XAttribute("Type", type),
new XAttribute("Target", row.FieldAsString(4)),
new XAttribute("IconFile", row.FieldAsString(6)),
NumericAttributeIfNotNull("IconIndex", row, 7)
);
var componentId = row.FieldAsString(1);
if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
{
component.Add(internetShortcut);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
}
this.DecompilerHelper.IndexElement(row, internetShortcut);
}
}
///
/// Decompile the Perfmon table.
///
/// The table to decompile.
private void DecompilePerfmonTable(Table table)
{
foreach (var row in table.Rows)
{
this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterName, new XAttribute("Name", row.FieldAsString(2))));
}
}
///
/// Decompile the PerfmonManifest table.
///
/// The table to decompile.
private void DecompilePerfmonManifestTable(Table table)
{
foreach (var row in table.Rows)
{
this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterManifestName, new XAttribute("ResourceFileDirectory", row.FieldAsString(2))));
}
}
///
/// Decompile the EventManifest table.
///
/// The table to decompile.
private void DecompileEventManifestTable(Table table)
{
foreach (var row in table.Rows)
{
this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.EventManifestName));
}
}
///
/// Decompile the SecureObjects table.
///
/// The table to decompile.
private void DecompileSecureObjectsTable(Table table)
{
foreach (var row in table.Rows)
{
var permissionEx = new XElement(UtilConstants.PermissionExName,
AttributeIfNotNull("Domain", row, 2),
AttributeIfNotNull("User", row, 3)
);
string[] specialPermissions;
switch ((string)row[1])
{
case "CreateFolder":
specialPermissions = UtilConstants.FolderPermissions;
break;
case "File":
specialPermissions = UtilConstants.FilePermissions;
break;
case "Registry":
specialPermissions = UtilConstants.RegistryPermissions;
break;
case "ServiceInstall":
specialPermissions = UtilConstants.ServicePermissions;
break;
default:
this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, row.Table.Name, row.Fields[1].Column.Name, row[1]));
return;
}
this.AddPermissionAttributes(permissionEx, row, 4, specialPermissions);
this.DecompilerHelper.IndexElement(row, permissionEx);
}
}
///
/// Decompile the ServiceConfig table.
///
/// The table to decompile.
private void DecompileServiceConfigTable(Table table)
{
foreach (var row in table.Rows)
{
var serviceConfig = new XElement(UtilConstants.ServiceConfigName,
new XAttribute("ServiceName", row.FieldAsString(0)),
AttributeIfNotNull("FirstFailureActionType", row, 3),
AttributeIfNotNull("SecondFailureActionType", row, 4),
AttributeIfNotNull("ThirdFailureActionType", row, 5),
NumericAttributeIfNotNull("ResetPeriodInDays", row, 6),
NumericAttributeIfNotNull("RestartServiceDelayInSeconds", row, 7),
AttributeIfNotNull("ProgramCommandLine", row, 8),
AttributeIfNotNull("RebootMessage", row, 9)
);
this.DecompilerHelper.IndexElement(row, serviceConfig);
}
}
///
/// Decompile the User table.
///
/// The table to decompile.
private void DecompileUserTable(Table table)
{
foreach (var row in table.Rows)
{
var attributes = row.FieldAsNullableInteger(6) ?? 0;
var user = new XElement(UtilConstants.UserName,
new XAttribute("Id", row.FieldAsString(0)),
new XAttribute("Name", row.FieldAsString(2)),
AttributeIfNotNull("Domain", row, 3),
AttributeIfNotNull("Password", row, 4),
AttributeIfNotNull("Comment", row, 5),
AttributeIfTrue("PasswordNeverExpires", UtilCompiler.UserDontExpirePasswrd == (attributes & UtilCompiler.UserDontExpirePasswrd)),
AttributeIfTrue("CanNotChangePassword", UtilCompiler.UserPasswdCantChange == (attributes & UtilCompiler.UserPasswdCantChange)),
AttributeIfTrue("PasswordExpired", UtilCompiler.UserPasswdChangeReqdOnLogin == (attributes & UtilCompiler.UserPasswdChangeReqdOnLogin)),
AttributeIfTrue("Disabled", UtilCompiler.UserDisableAccount == (attributes & UtilCompiler.UserDisableAccount)),
AttributeIfTrue("FailIfExists", UtilCompiler.UserFailIfExists == (attributes & UtilCompiler.UserFailIfExists)),
AttributeIfTrue("UpdateIfExists", UtilCompiler.UserUpdateIfExists == (attributes & UtilCompiler.UserUpdateIfExists)),
AttributeIfTrue("LogonAsService", UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService)),
AttributeIfTrue("LogonAsBatchJob", UtilCompiler.UserLogonAsBatchJob == (attributes & UtilCompiler.UserLogonAsBatchJob)),
AttributeIfTrue("RemoveComment", UtilCompiler.UserRemoveComment == (attributes & UtilCompiler.UserRemoveComment))
);
if (UtilCompiler.UserDontRemoveOnUninstall == (attributes & UtilCompiler.UserDontRemoveOnUninstall))
{
user.Add(new XAttribute("RemoveOnUninstall", "no"));
}
if (UtilCompiler.UserDontCreateUser == (attributes & UtilCompiler.UserDontCreateUser))
{
user.Add(new XAttribute("CreateUser", "no"));
}
if (UtilCompiler.UserNonVital == (attributes & UtilCompiler.UserNonVital))
{
user.Add(new XAttribute("Vital", "no"));
}
var componentId = row.FieldAsString(1);
if (!String.IsNullOrEmpty(componentId))
{
if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
{
component.Add(user);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
}
}
else
{
this.DecompilerHelper.AddElementToRoot(user);
}
this.DecompilerHelper.IndexElement(row, user);
}
}
///
/// Decompile the UserGroup table.
///
/// The table to decompile.
private void DecompileUserGroupTable(Table table)
{
foreach (var row in table.Rows)
{
var userId = row.FieldAsString(0);
if (this.DecompilerHelper.TryGetIndexedElement("User", userId, out var user))
{
user.Add(new XElement(UtilConstants.GroupRefName, new XAttribute("Id", row.FieldAsString(1))));
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Group_", userId, "Group"));
}
}
}
///
/// Decompile the XmlConfig table.
///
/// The table to decompile.
private void DecompileXmlConfigTable(Table table)
{
foreach (var row in table.Rows)
{
var flags = row.FieldAsNullableInteger(6) ?? 0;
string node = null;
string action = null;
string on = null;
if (0x1 == (flags & 0x1))
{
node = "element";
}
else if (0x2 == (flags & 0x2))
{
node = "value";
}
else if (0x4 == (flags & 0x4))
{
node = "document";
}
if (0x10 == (flags & 0x10))
{
action = "create";
}
else if (0x20 == (flags & 0x20))
{
action = "delete";
}
if (0x100 == (flags & 0x100))
{
on = "install";
}
else if (0x200 == (flags & 0x200))
{
on = "uninstall";
}
var xmlConfig = new XElement(UtilConstants.XmlConfigName,
new XAttribute("Id", row.FieldAsString(0)),
new XAttribute("File", row.FieldAsString(1)),
new XAttribute("ElementPath", row.FieldAsString(2)),
AttributeIfNotNull("VerifyPath", row, 3),
AttributeIfNotNull("Name", row, 4),
AttributeIfNotNull("Node", node),
AttributeIfNotNull("Action", action),
AttributeIfNotNull("On", on),
AttributeIfTrue("PreserveModifiedDate", 0x00001000 == (flags & 0x00001000)),
NumericAttributeIfNotNull("Sequence", row, 8)
);
this.DecompilerHelper.IndexElement(row, xmlConfig);
}
}
private void FinalizeCustomActions()
{
foreach (var customActionName in this.customActionNames)
{
if (CustomActionMapping.TryGetValue(customActionName, out var elementName))
{
this.DecompilerHelper.AddElementToRoot(elementName);
}
}
}
///
/// Finalize the Perfmon table.
///
/// The collection of all tables.
///
/// Since the PerfCounter element nests under a File element, but
/// the Perfmon table does not have a foreign key relationship with
/// the File table (instead it has a formatted string that usually
/// refers to a file row - but doesn't have to), the nesting must
/// be inferred during finalization.
///
private void FinalizePerfmonTable(TableIndexedCollection tables)
{
if (tables.TryGetTable("Perfmon", out var perfmonTable))
{
foreach (var row in perfmonTable.Rows)
{
var formattedFile = row.FieldAsString(1);
// try to "de-format" the File column's value to determine the proper parent File element
if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
&& formattedFile.EndsWith("]", StringComparison.Ordinal))
{
var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
{
var perfCounter = this.DecompilerHelper.GetIndexedElement(row);
file.Add(perfCounter);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, perfmonTable.Name, row.GetPrimaryKey(), "File", formattedFile, "File"));
}
}
else
{
this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon"));
}
}
}
}
///
/// Finalize the PerfmonManifest table.
///
/// The collection of all tables.
private void FinalizePerfmonManifestTable(TableIndexedCollection tables)
{
if (tables.TryGetTable("PerfmonManifest", out var perfmonManifestTable))
{
foreach (var row in perfmonManifestTable.Rows)
{
var formattedFile = row.FieldAsString(1);
// try to "de-format" the File column's value to determine the proper parent File element
if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
&& formattedFile.EndsWith("]", StringComparison.Ordinal))
{
var perfCounterManifest = this.DecompilerHelper.GetIndexedElement(row);
var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
{
file.Add(perfCounterManifest);
}
else
{
var resourceFileDirectory = perfCounterManifest.Attribute("ResourceFileDirectory")?.Value;
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, resourceFileDirectory, row.GetPrimaryKey(), "File", formattedFile, "File"));
}
}
else
{
this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest"));
}
}
}
}
///
/// Finalize the SecureObjects table.
///
/// The collection of all tables.
///
/// Nests the PermissionEx elements below their parent elements. There are no declared foreign
/// keys for the parents of the SecureObjects table.
///
private void FinalizeSecureObjectsTable(TableIndexedCollection tables)
{
var createFolderElementsByDirectoryId = new Dictionary>();
// index the CreateFolder table because the foreign key to this table from the
// LockPermissions table is only part of the primary key of this table
if (tables.TryGetTable("CreateFolder", out var createFolderTable))
{
foreach (var row in createFolderTable.Rows)
{
var directoryId = row.FieldAsString(0);
if (!createFolderElementsByDirectoryId.TryGetValue(directoryId, out var createFolderElements))
{
createFolderElements = new List();
createFolderElementsByDirectoryId.Add(directoryId, createFolderElements);
}
var createFolder = this.DecompilerHelper.GetIndexedElement(row);
createFolderElements.Add(createFolder);
}
}
if (tables.TryGetTable("SecureObjects", out var secureObjectsTable))
{
foreach (var row in secureObjectsTable.Rows)
{
var id = row.FieldAsString(0);
var table = row.FieldAsString(1);
var permissionEx = this.DecompilerHelper.GetIndexedElement(row);
if (table == "CreateFolder")
{
if (createFolderElementsByDirectoryId.TryGetValue(id, out var createFolderElements))
{
foreach (var createFolder in createFolderElements)
{
createFolder.Add(permissionEx);
}
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(), "LockObject", id, table));
}
}
else
{
var parentElement = this.DecompilerHelper.GetIndexedElement(table, id);
if (parentElement != null)
{
parentElement.Add(permissionEx);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(), "LockObject", id, table));
}
}
}
}
}
///
/// Finalize the ServiceConfig table.
///
/// The collection of all tables.
///
/// Since there is no foreign key from the ServiceName column to the
/// ServiceInstall table, this relationship must be handled late.
///
private void FinalizeServiceConfigTable(TableIndexedCollection tables)
{
//var serviceInstalls = new Hashtable();
var serviceInstallElementsByName = new Dictionary>();
// index the ServiceInstall table because the foreign key used by the ServiceConfig
// table is actually the ServiceInstall.Name, not the ServiceInstall.ServiceInstall
// this is unfortunate because the service Name is not guaranteed to be unique, so
// decompiler must assume there could be multiple matches and add the ServiceConfig to each
// TODO: the Component column information should be taken into acount to accurately identify
// the correct column to use
if (tables.TryGetTable("ServiceInstall", out var serviceInstallTable))
{
foreach (var row in serviceInstallTable.Rows)
{
var name = row.FieldAsString(1);
if (!serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements))
{
serviceInstallElements = new List();
serviceInstallElementsByName.Add(name, serviceInstallElements);
}
var serviceInstall = this.DecompilerHelper.GetIndexedElement(row);
serviceInstallElements.Add(serviceInstall);
}
}
if (tables.TryGetTable("ServiceConfig", out var serviceConfigTable))
{
foreach (var row in serviceConfigTable.Rows)
{
var serviceConfig = this.DecompilerHelper.GetIndexedElement(row);
if (row.FieldAsInteger(2) == 0)
{
var componentId = row.FieldAsString(1);
if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
{
component.Add(serviceConfig);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
}
}
else
{
var name = row.FieldAsString(0);
if (serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements))
{
foreach (var serviceInstall in serviceInstallElements)
{
serviceInstall.Add(serviceConfig);
}
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(), "ServiceName", name, "ServiceInstall"));
}
}
}
}
}
///
/// Finalize the XmlConfig table.
///
/// Collection of all tables.
private void FinalizeXmlConfigTable(TableIndexedCollection tables)
{
if (tables.TryGetTable("XmlConfig", out var xmlConfigTable))
{
foreach (var row in xmlConfigTable.Rows)
{
var xmlConfig = this.DecompilerHelper.GetIndexedElement(row);
if (null == row[6] || 0 == (int)row[6])
{
var id = row.FieldAsString(2);
if (this.DecompilerHelper.TryGetIndexedElement("XmlConfig", id, out var parentXmlConfig))
{
parentXmlConfig.Add(xmlConfig);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(), "ElementPath", (string)row[2], "XmlConfig"));
}
}
else
{
var componentId = row.FieldAsString(7);
if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
{
component.Add(xmlConfig);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
}
}
}
}
}
///
/// Finalize the XmlFile table.
///
/// The collection of all tables.
///
/// Some of the XmlFile table rows are compiler generated from util:EventManifest node
/// These rows should not be appended to component.
///
private void FinalizeXmlFileTable(TableIndexedCollection tables)
{
if (tables.TryGetTable("XmlFile", out var xmlFileTable))
{
var eventManifestTable = tables["EventManifest"];
foreach (var row in xmlFileTable.Rows)
{
var manifestGenerated = false;
var xmlFileConfigId = (string)row[0];
if (null != eventManifestTable)
{
foreach (var emrow in eventManifestTable.Rows)
{
var formattedFile = (string)emrow[1];
if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
&& formattedFile.EndsWith("]", StringComparison.Ordinal))
{
var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
if (String.Equals(String.Concat("Config_", fileId, "ResourceFile"), xmlFileConfigId))
{
if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest))
{
eventManifest.Add(new XAttribute("ResourceFile", row.FieldAsString(4)));
}
manifestGenerated = true;
}
else if (String.Equals(String.Concat("Config_", fileId, "MessageFile"), xmlFileConfigId))
{
if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest))
{
eventManifest.Add(new XAttribute("MessageFile", row.FieldAsString(4)));
}
manifestGenerated = true;
}
}
}
}
if (manifestGenerated)
{
continue;
}
var action = "setValue";
var flags = row.FieldAsInteger(5);
if (0x1 == (flags & 0x1) && 0x2 == (flags & 0x2))
{
this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, xmlFileTable.Name, row.Fields[5].Column.Name, row[5]));
}
else if (0x1 == (flags & 0x1))
{
action = "createElement";
}
else if (0x2 == (flags & 0x2))
{
action = "deleteValue";
}
var selectionLanguage = (0x100 == (flags & 0x100)) ? "XPath" : null;
var preserveModifiedDate = 0x00001000 == (flags & 0x00001000);
var permanent = 0x00010000 == (flags & 0x00010000);
if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(6), out var component))
{
var xmlFile = new XElement(UtilConstants.XmlFileName,
AttributeIfNotNull("Id", row, 0),
AttributeIfNotNull("File", row, 1),
AttributeIfNotNull("ElementPath", row, 2),
AttributeIfNotNull("Name", row, 3),
AttributeIfNotNull("Value", row, 4),
AttributeIfNotNull("Action", action),
AttributeIfNotNull("SelectionLanguage", selectionLanguage),
AttributeIfTrue("PreserveModifiedDate", preserveModifiedDate),
AttributeIfTrue("Permanent", permanent),
NumericAttributeIfNotNull("Sequence", row, 7)
);
component.Add(xmlFile);
}
else
{
this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlFileTable.Name, row.GetPrimaryKey(), "Component_", (string)row[6], "Component"));
}
}
}
}
///
/// Finalize the eventManifest table.
/// This function must be called after FinalizeXmlFileTable
///
/// The collection of all tables.
private void FinalizeEventManifestTable(TableIndexedCollection tables)
{
if (tables.TryGetTable("EventManifest", out var eventManifestTable))
{
foreach (var row in eventManifestTable.Rows)
{
var eventManifest = this.DecompilerHelper.GetIndexedElement(row);
var formattedFile = row.FieldAsString(1);
// try to "de-format" the File column's value to determine the proper parent File element
if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
&& formattedFile.EndsWith("]", StringComparison.Ordinal))
{
var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
{
file.Add(eventManifest);
}
}
else
{
this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "EventManifest"));
}
}
}
}
private void AddPermissionAttributes(XElement element, Row row, int column, string[] specialPermissions)
{
var permissions = row.FieldAsInteger(column);
for (var i = 0; i < 32; i++)
{
if (0 != ((permissions >> i) & 1))
{
string name = null;
if (16 > i && specialPermissions.Length > i)
{
name = specialPermissions[i];
}
else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16))
{
name = UtilConstants.StandardPermissions[i - 16];
}
else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28))
{
name = UtilConstants.GenericPermissions[i - 28];
}
if (!String.IsNullOrEmpty(name))
{
element.Add(new XAttribute(name, "yes"));
}
else
{
this.Messaging.Write(WarningMessages.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(), i));
}
}
}
}
private static XAttribute AttributeIfNotNull(string name, string value)
{
return value == null ? null : new XAttribute(name, value);
}
private static XAttribute AttributeIfNotNull(string name, bool value)
{
return new XAttribute(name, value ? "yes" : "no");
}
private static XAttribute AttributeIfNotNull(string name, Row row, int field)
{
if (row[field] != null)
{
return new XAttribute(name, row.FieldAsString(field));
}
return null;
}
private static XAttribute NumericAttributeIfNotNull(string name, Row row, int field)
{
if (row[field] != null)
{
return new XAttribute(name, row.FieldAsInteger(field));
}
return null;
}
private static XAttribute AttributeIfTrue(string name, bool value)
{
return value ? new XAttribute(name, "yes") : null;
}
}
internal static class XElementExtensions
{
public static XElement AttributeIfNotNull(this XElement element, string name, Row row, int field)
{
if (row[field] != null)
{
element.Add(new XAttribute(name, row.FieldAsString(field)));
}
return element;
}
public static XElement NumericAttributeIfNotNull(this XElement element, string name, Row row, int field)
{
if (row[field] != null)
{
element.Add(new XAttribute(name, row.FieldAsInteger(field)));
}
return element;
}
}
}