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 MaxAdGroupDescriptionLength = 1024; public const int MaxAdGroupLoopDigits = 3; private const int MinLeadingRelativePathSegmentLength = 3; private const int MinSingleLeadingRelativePathSegmentLength = 2; private const int MinLastRelativePathSegmentLength = 12; 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, string valueLabel = "AD-Gruppenname") { var effectiveSegments = (sanitizedSegments ?? Array.Empty()).Where(i => i != null).ToArray(); var effectiveFolderName = folderName ?? string.Empty; var currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath); var originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags); var measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, 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 (measuredValue.Length > maxLength) { 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; currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath); originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags); measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags); } var initialValue = MaterializeTemplateValue( templateValue, allowRelativePath, GetCurrentRelativePath(sanitizedSegments, defaultRelativePath), sanitizedSegments, folderName, replacementTags); var result = new BoundedTemplateContext { SanitizedSegments = effectiveSegments, FolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : effectiveFolderName, OriginalValue = initialValue, FinalValue = originalValue, WasShortened = !string.Equals(initialValue, originalValue, StringComparison.Ordinal), Strategy = strategy }; if (result.WasShortened) { cLogManager.DefaultLogger.LogEntry( LogLevels.Warning, $"{valueLabel} gekuerzt ({logContext}): '{result.OriginalValue}' ({GetMeasuredTemplateLength(result.OriginalValue)}) -> '{result.FinalValue}' ({GetMeasuredTemplateLength(result.FinalValue)}), Strategie: {result.Strategy}, Limit: {maxLength}."); } if (measuredValue.Length > maxLength) { cLogManager.DefaultLogger.LogEntry( LogLevels.Warning, $"{valueLabel} ueberschreitet weiterhin das sichere Limit ({logContext}): '{result.FinalValue}' ({measuredValue.Length}), Limit: {maxLength}."); } 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 string MaterializeTemplateValueForLength( string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName, IDictionary replacementTags) { return NormalizeLoopPlaceholderLength( MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags)); } private static string NormalizeLoopPlaceholderLength(string templateValue) { if (string.IsNullOrWhiteSpace(templateValue)) return templateValue ?? string.Empty; return Regex.Replace( templateValue, @"{{(?[^}]*)(?LOOP)(?[^{]*)}}", match => match.Groups["prefix"].Value + new string('9', MaxAdGroupLoopDigits) + match.Groups["postfix"].Value, RegexOptions.IgnoreCase); } private static int GetMeasuredTemplateLength(string templateValue) { return NormalizeLoopPlaceholderLength(templateValue).Length; } private static string GetCurrentRelativePath(string[] sanitizedSegments, string fallbackRelativePath) { if (sanitizedSegments != null && sanitizedSegments.Length > 0) return string.Join("_", sanitizedSegments); return fallbackRelativePath ?? string.Empty; } 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; if (segments.Length > 2) { var candidateIndex = -1; var candidateLength = MinLeadingRelativePathSegmentLength; 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; } segments = segments.Skip(1).ToArray(); return true; } if (segments.Length == 2) { if (segments[0].Length > MinSingleLeadingRelativePathSegmentLength) { segments[0] = segments[0].Substring(0, segments[0].Length - 1); return true; } if (segments[lastIndex].Length > MinLastRelativePathSegmentLength) { segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1); return true; } if (segments[0].Length > 1) { segments[0] = segments[0].Substring(0, segments[0].Length - 1); return true; } if (segments[lastIndex].Length > 1) { segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1); return true; } return false; } 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; } } }