using C4IT.Logging; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace C4IT_IAM_Engine { public static class Helper { public const int MaxAdGroupNameLength = 64; public const int MaxAdGroupLoopDigits = 3; public sealed class BoundedTemplateContext { public string[] SanitizedSegments { get; set; } = Array.Empty(); public string FolderName { get; set; } = string.Empty; public bool WasShortened { get; set; } public string OriginalValue { get; set; } = string.Empty; public string FinalValue { get; set; } = string.Empty; public string Strategy { get; set; } = string.Empty; } public static string ReplaceLoopTag(this string str, int loop) { return Regex.Replace(str, @"(?{{(?[^}]*)(?LOOP)(?[^{]*)}})", loop <= 0 ? "" : "${prefix}" + loop + "${postfix}"); } public static string ReplaceTags(this string str, IDictionary dict) { if (str.Equals(string.Empty) || str == null || dict == null || dict.Count == 0) return str; return dict.Aggregate(str, (current, value) => current.Replace("{{" + value.Key + "}}", value.Value)); } public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName) { if (templateValue == null) return string.Empty; var result = Regex.Replace(templateValue, @"{{\s*NAME\s*}}", folderName ?? string.Empty, RegexOptions.IgnoreCase); if (allowRelativePath) { result = Regex.Replace(result, @"{{\s*RELATIVEPATH(?:\s*\(\s*(\d+)\s*\))?\s*}}", match => { if (sanitizedSegments == null || sanitizedSegments.Length == 0) return string.Empty; if (!match.Groups[1].Success) return defaultRelativePath; if (!int.TryParse(match.Groups[1].Value, out var segmentIndex) || segmentIndex < 0) return defaultRelativePath; var segmentCount = Math.Min(sanitizedSegments.Length, segmentIndex + 1); var skip = sanitizedSegments.Length - segmentCount; return string.Join("_", sanitizedSegments.Skip(skip)); }, RegexOptions.IgnoreCase); } return result; } public static BoundedTemplateContext GetBoundedAdGroupTemplateContext( string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName, IDictionary replacementTags, int maxLength, string logContext) { var effectiveSegments = (sanitizedSegments ?? Array.Empty()).Where(i => i != null).ToArray(); var effectiveFolderName = folderName ?? string.Empty; var availableLength = Math.Max(1, maxLength - GetLoopReservationLength(templateValue)); var originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, effectiveSegments, effectiveFolderName, replacementTags); var usesRelativePath = allowRelativePath && Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*RELATIVEPATH", RegexOptions.IgnoreCase); var usesName = Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*NAME\s*}}", RegexOptions.IgnoreCase); var strategy = string.Empty; while (originalValue.Length > availableLength) { var changed = false; if (usesRelativePath && TryShortenRelativePath(ref effectiveSegments)) { effectiveFolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : string.Empty; if (string.IsNullOrWhiteSpace(strategy)) strategy = "truncate-relativepath"; changed = true; } else if (usesName && !usesRelativePath && TryShortenName(ref effectiveFolderName)) { if (string.IsNullOrWhiteSpace(strategy)) strategy = "truncate-name"; changed = true; } if (!changed) break; originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, effectiveSegments, effectiveFolderName, replacementTags); } var result = new BoundedTemplateContext { SanitizedSegments = effectiveSegments, FolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : effectiveFolderName, OriginalValue = MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags), FinalValue = originalValue, WasShortened = !string.Equals( MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags), originalValue, StringComparison.Ordinal), Strategy = strategy }; if (result.WasShortened) { cLogManager.DefaultLogger.LogEntry( LogLevels.Warning, $"AD-Gruppenname gekuerzt ({logContext}): '{result.OriginalValue}' ({result.OriginalValue.Length}) -> '{result.FinalValue}' ({result.FinalValue.Length}), Strategie: {result.Strategy}, Limit: {availableLength}."); } if (result.FinalValue.Length > availableLength) { cLogManager.DefaultLogger.LogEntry( LogLevels.Warning, $"AD-Gruppenname ueberschreitet weiterhin das sichere Limit ({logContext}): '{result.FinalValue}' ({result.FinalValue.Length}), Limit: {availableLength}."); } return result; } public static string SanitizePathSegment(string segment) { if (string.IsNullOrEmpty(segment)) return string.Empty; return Regex.Replace(segment, @"[\s\-]", "_"); } public static void CreatePathWithWriteAccess(string FilePath) { try { var PF = Environment.ExpandEnvironmentVariables(FilePath); Directory.CreateDirectory(PF); } catch { } } public static string MaskAllButLastAndFirst(this string input, char maskingChar = '*') { if (input.Length > 3) { var pattern = @"^(.{1})(.+)(.{1})$"; var match = Regex.Match(input, pattern); var mask = new string(maskingChar, match.Groups[2].Length); return $"{match.Groups[1]}{mask}{match.Groups[3]}"; } else return new string(maskingChar, input.Length); } private static string MaterializeTemplateValue( string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName, IDictionary replacementTags) { return ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName) .ReplaceTags(replacementTags) .ToUpper(); } private static int GetLoopReservationLength(string templateValue) { if (string.IsNullOrWhiteSpace(templateValue)) return 0; var reservation = 0; foreach (Match match in Regex.Matches(templateValue, @"{{(?[^}]*)(?LOOP)(?[^{]*)}}", RegexOptions.IgnoreCase)) { reservation += match.Groups["prefix"].Value.Length + MaxAdGroupLoopDigits + match.Groups["postfix"].Value.Length; } return reservation; } private static bool TryShortenRelativePath(ref string[] segments) { if (segments == null || segments.Length == 0) return false; var lastIndex = segments.Length - 1; var candidateIndex = -1; var candidateLength = 1; for (var i = 0; i < lastIndex; i++) { if (string.IsNullOrWhiteSpace(segments[i]) || segments[i].Length <= candidateLength) continue; candidateIndex = i; candidateLength = segments[i].Length; } if (candidateIndex >= 0) { segments[candidateIndex] = segments[candidateIndex].Substring(0, segments[candidateIndex].Length - 1); return true; } if (segments.Length > 1) { segments = segments.Skip(1).ToArray(); return true; } if (segments[lastIndex].Length > 1) { segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1); return true; } return false; } private static bool TryShortenName(ref string folderName) { if (string.IsNullOrWhiteSpace(folderName) || folderName.Length <= 1) return false; folderName = folderName.Substring(0, folderName.Length - 1); return true; } } }