Updated to .NET Framework 4.7.2 due to platform limitations, added email cc, bcc and some fixes.

This commit is contained in:
2026-04-21 11:46:55 +02:00
parent c267069d02
commit b8b702bda3
40 changed files with 5666 additions and 2064 deletions
@@ -0,0 +1,517 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BizTalkMonitorConfigGenerator.Configuration;
using BizTalkMonitorConfigGenerator.Logging;
using Microsoft.BizTalk.ExplorerOM;
namespace BizTalkMonitorConfigGenerator.BizTalk
{
public sealed class BizTalkCatalogDiscoveryService
{
private readonly BizTalkOptions _options;
private readonly OutputOptions _outputOptions;
private readonly GeneratorFileLogger _logger;
public BizTalkCatalogDiscoveryService(BizTalkOptions options, OutputOptions outputOptions, GeneratorFileLogger logger)
{
_options = options ?? new BizTalkOptions();
_outputOptions = outputOptions ?? new OutputOptions();
_logger = logger;
}
public IReadOnlyCollection<DiscoveredEndpoint> Discover()
{
var discovered = new List<DiscoveredEndpoint>();
var catalog = new BtsCatalogExplorer();
catalog.ConnectionString = _options.ConnectionString;
foreach (Application application in catalog.Applications)
{
if (application == null)
{
continue;
}
if (ShouldSkipApplication(application))
{
_logger.Info("BizTalk-Anwendung übersprungen: " + application.Name);
continue;
}
if (_options.IncludeSendPorts)
{
DiscoverSendPorts(application, discovered);
}
if (_options.IncludeReceiveLocations)
{
DiscoverReceiveLocations(application, discovered);
}
}
return discovered
.OrderBy(x => x.MappedApplicationName, StringComparer.OrdinalIgnoreCase)
.ThenBy(x => x.TargetName, StringComparer.OrdinalIgnoreCase)
.ThenBy(x => x.NormalizedEndpoint, StringComparer.OrdinalIgnoreCase)
.ToList();
}
private void DiscoverSendPorts(Application application, List<DiscoveredEndpoint> discovered)
{
foreach (SendPort sendPort in application.SendPorts)
{
if (sendPort == null)
{
continue;
}
if (_options.OnlyStartedSendPorts && sendPort.Status != PortStatus.Started)
{
_logger.Discovery("SKIP", "SendPort", application.Name, sendPort.Name, ResolveAdapterName(sendPort.PrimaryTransport), SafeAddress(sendPort.PrimaryTransport), string.Empty, "SendPort ist nicht gestartet. Status=" + sendPort.Status);
continue;
}
if (sendPort.IsDynamic && !_options.IncludeDynamicSendPorts)
{
_logger.Discovery("SKIP", "SendPort", application.Name, sendPort.Name, ResolveAdapterName(sendPort.PrimaryTransport), SafeAddress(sendPort.PrimaryTransport), string.Empty, "Dynamischer SendPort wird per Konfiguration übersprungen.");
continue;
}
AddTransportIfSupported(discovered, application.Name, "SendPort", sendPort.Name, sendPort.Name, sendPort.PrimaryTransport, false);
if (_options.IncludeSecondaryTransportOnSendPorts)
{
AddTransportIfSupported(discovered, application.Name, "SendPortSecondary", sendPort.Name + " (Secondary Transport)", sendPort.Name, sendPort.SecondaryTransport, true);
}
}
}
private void DiscoverReceiveLocations(Application application, List<DiscoveredEndpoint> discovered)
{
foreach (ReceivePort receivePort in application.ReceivePorts)
{
if (receivePort == null)
{
continue;
}
foreach (ReceiveLocation receiveLocation in receivePort.ReceiveLocations)
{
if (receiveLocation == null)
{
continue;
}
if (_options.OnlyEnabledReceiveLocations && !receiveLocation.Enable)
{
_logger.Discovery("SKIP", "ReceiveLocation", application.Name, receiveLocation.Name, ResolveAdapterName(receiveLocation.TransportType), receiveLocation.Address, string.Empty, "Receive Location ist deaktiviert.");
continue;
}
if (ShouldSkipArtifact(receiveLocation.Name))
{
_logger.Discovery("SKIP", "ReceiveLocation", application.Name, receiveLocation.Name, ResolveAdapterName(receiveLocation.TransportType), receiveLocation.Address, string.Empty, "Receive Location entspricht einem Ignore-Pattern.");
continue;
}
AddEndpointIfSupported(
discovered,
application.Name,
"ReceiveLocation",
receivePort.Name + " / " + receiveLocation.Name,
receiveLocation.Name,
ResolveAdapterName(receiveLocation.TransportType),
receiveLocation.Address,
false);
}
}
}
private void AddTransportIfSupported(List<DiscoveredEndpoint> discovered, string bizTalkApplicationName, string artifactType, string targetNameBase, string artifactName, TransportInfo transport, bool isSecondary)
{
if (transport == null)
{
_logger.Discovery("SKIP", artifactType, bizTalkApplicationName, artifactName, string.Empty, string.Empty, string.Empty, "Kein Transport konfiguriert.");
return;
}
AddEndpointIfSupported(
discovered,
bizTalkApplicationName,
artifactType,
targetNameBase + (isSecondary ? " (Secondary)" : " (Primary)"),
artifactName,
ResolveAdapterName(transport),
SafeAddress(transport),
isSecondary);
}
private void AddEndpointIfSupported(List<DiscoveredEndpoint> discovered, string bizTalkApplicationName, string artifactType, string targetNameBase, string artifactName, string adapterName, string sourceAddress, bool isSecondary)
{
if (ShouldSkipArtifact(artifactName))
{
_logger.Discovery("SKIP", artifactType, bizTalkApplicationName, artifactName, adapterName, sourceAddress, string.Empty, "Artefakt entspricht einem Ignore-Pattern.");
return;
}
string normalizedEndpoint;
int? port;
string httpMethod;
string reason;
if (!TryNormalizeEndpoint(sourceAddress, adapterName, out normalizedEndpoint, out port, out httpMethod, out reason))
{
_logger.Discovery("SKIP", artifactType, bizTalkApplicationName, artifactName, adapterName, sourceAddress, string.Empty, reason);
return;
}
var mappedApplication = ResolveMappedApplicationName(bizTalkApplicationName, artifactType, artifactName);
var targetName = BuildTargetName(artifactType, targetNameBase, adapterName, isSecondary);
var endpoint = new DiscoveredEndpoint
{
ArtifactType = artifactType,
BizTalkApplicationName = bizTalkApplicationName,
MappedApplicationName = mappedApplication,
ArtifactName = artifactName,
AdapterName = adapterName,
SourceAddress = sourceAddress,
NormalizedEndpoint = normalizedEndpoint,
TargetName = targetName,
HttpMethod = httpMethod,
Port = port,
ValidateUncPathAccess = null,
Detail = "Endpoint erfolgreich aus BizTalk übernommen."
};
discovered.Add(endpoint);
_logger.Discovery("ADD", artifactType, bizTalkApplicationName, artifactName, adapterName, sourceAddress, normalizedEndpoint, "Zuordnung zu ApplicationName='" + mappedApplication + "'.");
}
private bool ShouldSkipApplication(Application application)
{
if (application == null)
{
return true;
}
if (_options.IgnoreSystemApplications && application.IsSystem)
{
return true;
}
return _options.IgnoreApplications.Any(pattern => WildcardMatch(application.Name, pattern));
}
private bool ShouldSkipArtifact(string artifactName)
{
if (string.IsNullOrWhiteSpace(artifactName))
{
return false;
}
return _options.IgnoreArtifactNamePatterns.Any(pattern => WildcardMatch(artifactName, pattern));
}
private string ResolveMappedApplicationName(string bizTalkApplicationName, string artifactType, string artifactName)
{
foreach (var rule in _options.ArtifactMappings.Where(r => r != null && !string.IsNullOrWhiteSpace(r.ApplicationName)))
{
if (!string.IsNullOrWhiteSpace(rule.ArtifactType) && !string.Equals(rule.ArtifactType.Trim(), artifactType, StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (WildcardMatch(artifactName, rule.ArtifactNamePattern))
{
return rule.ApplicationName.Trim();
}
}
foreach (var rule in _options.ApplicationMappings.Where(r => r != null && !string.IsNullOrWhiteSpace(r.ApplicationName)))
{
if (WildcardMatch(bizTalkApplicationName, rule.BizTalkApplicationName))
{
return rule.ApplicationName.Trim();
}
}
return string.IsNullOrWhiteSpace(bizTalkApplicationName) ? "Unassigned Application" : bizTalkApplicationName.Trim();
}
private string BuildTargetName(string artifactType, string baseName, string adapterName, bool isSecondary)
{
var prefix = string.Equals(artifactType, "ReceiveLocation", StringComparison.OrdinalIgnoreCase)
? "ReceiveLocation"
: "SendPort";
var name = prefix + ": " + baseName;
if (!string.IsNullOrWhiteSpace(adapterName))
{
name += " [" + adapterName + "]";
}
if (isSecondary && name.IndexOf("Secondary", StringComparison.OrdinalIgnoreCase) < 0)
{
name += " [Secondary]";
}
return name;
}
private bool TryNormalizeEndpoint(string sourceAddress, string adapterName, out string normalizedEndpoint, out int? port, out string httpMethod, out string reason)
{
normalizedEndpoint = string.Empty;
port = null;
httpMethod = null;
reason = string.Empty;
var address = (sourceAddress ?? string.Empty).Trim();
var adapter = (adapterName ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(address))
{
reason = "Address/URI ist leer.";
return false;
}
if (AdapterContains(adapter, "SMTP"))
{
reason = "SMTP-Adapter wird uebersprungen, weil dessen Address-Wert keine pruefbare Netzwerkadresse, sondern eine Empfaengerliste ist.";
return false;
}
if (LooksLikeEmailAddressList(address))
{
reason = "Adresse sieht wie eine SMTP-/E-Mail-Empfaengerliste aus und wird uebersprungen.";
return false;
}
if (address.StartsWith("\\\\", StringComparison.Ordinal))
{
normalizedEndpoint = address;
return true;
}
if (Path.IsPathRooted(address))
{
if (!_options.IncludeLocalFilePaths)
{
reason = "Lokaler Dateipfad wird per Konfiguration nicht übernommen.";
return false;
}
normalizedEndpoint = address;
return true;
}
Uri uri;
if (Uri.TryCreate(address, UriKind.Absolute, out uri))
{
switch ((uri.Scheme ?? string.Empty).ToLowerInvariant())
{
case "http":
case "https":
normalizedEndpoint = uri.AbsoluteUri;
httpMethod = _outputOptions.DefaultHttpMethod;
return true;
case "ftp":
case "sftp":
case "tcp":
case "icmp":
case "file":
normalizedEndpoint = uri.IsAbsoluteUri ? uri.AbsoluteUri : address;
return true;
case "net.tcp":
if (string.IsNullOrWhiteSpace(uri.Host) || uri.Port <= 0)
{
reason = "net.tcp-Adresse konnte nicht auf Host/Port aufgelöst werden.";
return false;
}
normalizedEndpoint = "tcp://" + uri.Host + ":" + uri.Port + uri.PathAndQuery;
port = uri.Port;
return true;
case "net.pipe":
case "npipe":
reason = "Named-Pipe-Endpunkte werden vom Monitor nicht unterstützt.";
return false;
default:
reason = "Schema '" + uri.Scheme + "' wird vom Monitor nicht unterstützt.";
return false;
}
}
if (LooksLikeHostPort(address))
{
if (AdapterContains(adapter, "SFTP"))
{
normalizedEndpoint = "sftp://" + address;
port = ParsePort(address);
return true;
}
if (AdapterContains(adapter, "FTP"))
{
normalizedEndpoint = "ftp://" + address;
port = ParsePort(address);
return true;
}
normalizedEndpoint = "tcp://" + address;
port = ParsePort(address);
return true;
}
if (AdapterContains(adapter, "SFTP"))
{
normalizedEndpoint = "sftp://" + address.TrimStart('/');
return true;
}
if (AdapterContains(adapter, "FTP"))
{
normalizedEndpoint = "ftp://" + address.TrimStart('/');
return true;
}
if (AdapterContains(adapter, "HTTP") || AdapterContains(adapter, "SOAP") || AdapterContains(adapter, "WCF"))
{
reason = "HTTP/WCF-Adapteradresse konnte nicht als absolute URI erkannt werden.";
return false;
}
normalizedEndpoint = address;
return true;
}
private static string ResolveAdapterName(TransportInfo transport)
{
try
{
return transport == null || transport.TransportType == null ? string.Empty : transport.TransportType.Name;
}
catch
{
return string.Empty;
}
}
private static string ResolveAdapterName(ProtocolType transportType)
{
try
{
return transportType == null ? string.Empty : transportType.Name;
}
catch
{
return string.Empty;
}
}
private static string SafeAddress(TransportInfo transport)
{
try
{
return transport == null ? string.Empty : transport.Address;
}
catch
{
return string.Empty;
}
}
private static bool AdapterContains(string adapterName, string fragment)
{
return !string.IsNullOrWhiteSpace(adapterName)
&& adapterName.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0;
}
private static bool LooksLikeEmailAddressList(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
var trimmed = value.Trim();
if (trimmed.IndexOf('@') < 0)
{
return false;
}
if (trimmed.IndexOf("http://", StringComparison.OrdinalIgnoreCase) >= 0
|| trimmed.IndexOf("https://", StringComparison.OrdinalIgnoreCase) >= 0
|| trimmed.IndexOf("sftp://", StringComparison.OrdinalIgnoreCase) >= 0
|| trimmed.IndexOf("ftp://", StringComparison.OrdinalIgnoreCase) >= 0
|| trimmed.IndexOf("tcp://", StringComparison.OrdinalIgnoreCase) >= 0
|| trimmed.IndexOf("file://", StringComparison.OrdinalIgnoreCase) >= 0
|| trimmed.StartsWith("\\", StringComparison.Ordinal))
{
return false;
}
var entries = trimmed.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
if (entries.Length == 0)
{
return false;
}
foreach (var entry in entries)
{
var part = entry.Trim();
if (string.IsNullOrWhiteSpace(part))
{
continue;
}
if (part.IndexOf('@') <= 0 || part.IndexOf(' ') >= 0)
{
return false;
}
}
return true;
}
private static bool WildcardMatch(string input, string pattern)
{
if (string.IsNullOrWhiteSpace(pattern))
{
return false;
}
input = input ?? string.Empty;
var regex = "^" + Regex.Escape(pattern.Trim()).Replace("\\*", ".*").Replace("\\?", ".") + "$";
return Regex.IsMatch(input, regex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
private static bool LooksLikeHostPort(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
return Regex.IsMatch(value.Trim(), @"^[a-zA-Z0-9._-]+:\d{1,5}($|/.*$)");
}
private static int? ParsePort(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
var match = Regex.Match(value, @":(?<port>\d{1,5})(?:$|/)");
int parsed;
if (match.Success && int.TryParse(match.Groups["port"].Value, out parsed))
{
return parsed;
}
return null;
}
}
}
@@ -0,0 +1,18 @@
namespace BizTalkMonitorConfigGenerator.BizTalk
{
public sealed class DiscoveredEndpoint
{
public string ArtifactType { get; set; }
public string BizTalkApplicationName { get; set; }
public string MappedApplicationName { get; set; }
public string ArtifactName { get; set; }
public string AdapterName { get; set; }
public string SourceAddress { get; set; }
public string NormalizedEndpoint { get; set; }
public string TargetName { get; set; }
public string HttpMethod { get; set; }
public int? Port { get; set; }
public bool? ValidateUncPathAccess { get; set; }
public string Detail { get; set; }
}
}