using ZA.CoreService.ESBCertificateManager.Models; using ZA.CoreService.ESBCertificateManager.Services; namespace ZA.CoreService.ESBCertificateManager.Setup; public sealed class AddTargetPathForm : Form { private readonly SetupCoordinator _coordinator; private readonly CertificateProbeService _probeService = new(); private readonly CertificateTargetOption? _existing; private readonly int? _preferredSonicContainerId; private readonly ComboBox _cmbContainer; private readonly TextBox _txtFullPath; private readonly TextBox _txtBackupFolder; private readonly CheckBox _chkBackup; private readonly Label _lblProbeResult; private readonly Button _btnProbe; private readonly Button _btnSave; private CertificateProbeResult? _lastProbe; private bool _running; public int? CreatedCertificateTargetId { get; private set; } public bool IsEditMode => _existing is not null; public AddTargetPathForm( SetupCoordinator coordinator, int? preferredSonicContainerId = null, CertificateTargetOption? existing = null) { _coordinator = coordinator; _preferredSonicContainerId = preferredSonicContainerId; _existing = existing; Text = IsEditMode ? "Zertifikat-Pfad bearbeiten" : "Zertifikat-Pfad hinzufügen"; StartPosition = FormStartPosition.CenterParent; FormBorderStyle = FormBorderStyle.FixedDialog; MaximizeBox = false; MinimizeBox = false; ShowInTaskbar = false; ClientSize = new Size(640, 560); BackColor = SettingsUi.Background; ForeColor = SettingsUi.Text; Font = new Font("Segoe UI", 10f); Label title = new() { Text = IsEditMode ? "Zertifikat-Pfad bearbeiten" : "Zertifikat-Datei-Pfad", Location = new Point(28, 18), AutoSize = true, Font = new Font("Segoe UI Semibold", 18f, FontStyle.Bold), ForeColor = SettingsUi.Text }; Label subtitle = new() { Text = "UNC- oder lokaler Dateipfad zur .cer/.crt/.pem/.pfx/.p12-Datei. " + "Der Container ist nur das Neustart-Ziel.", Location = new Point(30, 54), MaximumSize = new Size(580, 40), AutoSize = true, ForeColor = SettingsUi.Muted }; Panel card = SettingsUi.CreateCard(); card.Location = new Point(28, 100); card.Size = new Size(584, 360); card.Paint += SettingsUi.PaintCardBorder; _txtFullPath = SettingsUi.CreateTextBox(); _txtFullPath.Text = _existing?.FullTargetPath ?? DemoCertificatePath.FullPath; _txtFullPath.PlaceholderText = DemoCertificatePath.FullPath; Button browse = SettingsUi.CreateGhostButton("…"); browse.Size = new Size(40, 32); browse.Click += (_, _) => BrowseForFile(); _btnProbe = SettingsUi.CreateGhostButton("Prüfen"); _btnProbe.Size = new Size(100, 32); _btnProbe.Click += (_, _) => RunProbe(); _lblProbeResult = new Label { AutoSize = false, Size = new Size(548, 52), ForeColor = SettingsUi.Muted, Font = new Font("Segoe UI", 9f), Text = "Noch nicht geprüft. Bitte Pfad prüfen – bei vorhandener " + "Datei wird der Ablauf angezeigt." }; _cmbContainer = SettingsUi.CreateComboBox(); _txtBackupFolder = SettingsUi.CreateTextBox(); _txtBackupFolder.Text = _existing?.BackupDirectoryName ?? "Backup"; _chkBackup = new CheckBox { Text = "Backup vor dem Überschreiben", AutoSize = true, Checked = _existing?.BackupEnabled ?? true, ForeColor = SettingsUi.Text, Font = new Font("Segoe UI", 9.5f) }; Label pathLabel = SettingsUi.CreateFieldLabel( "Vollständiger Pfad zur Zertifikatsdatei"); pathLabel.Location = new Point(18, 16); _txtFullPath.Location = new Point(18, 38); _txtFullPath.Width = 370; browse.Location = new Point(394, 38); _btnProbe.Location = new Point(440, 38); _lblProbeResult.Location = new Point(18, 78); Label containerLabel = SettingsUi.CreateFieldLabel( "Neustart-Ziel (Sonic-Container) – nicht der Dateipfad"); containerLabel.Location = new Point(18, 140); _cmbContainer.Location = new Point(18, 162); _cmbContainer.Width = 548; Label hint = new() { Text = "Nach dem Austausch wird dieser Container über MfApi " + "neu gestartet. Der Pfad oben ist unabhängig davon.", Location = new Point(18, 202), MaximumSize = new Size(548, 36), AutoSize = true, ForeColor = SettingsUi.Muted, Font = new Font("Segoe UI", 8.5f) }; AddField(card, "Backup-Ordner", _txtBackupFolder, 18, 250, 220); _chkBackup.Location = new Point(258, 272); card.Controls.Add(_chkBackup); card.Controls.Add(pathLabel); card.Controls.Add(_txtFullPath); card.Controls.Add(browse); card.Controls.Add(_btnProbe); card.Controls.Add(_lblProbeResult); card.Controls.Add(containerLabel); card.Controls.Add(_cmbContainer); card.Controls.Add(hint); _btnSave = SettingsUi.CreatePrimaryButton( IsEditMode ? "Änderungen speichern" : "Pfad anlegen"); _btnSave.Location = new Point(392, 490); _btnSave.Size = new Size(220, 40); _btnSave.Click += async (_, _) => await SaveAsync(); Button cancel = SettingsUi.CreateGhostButton("Abbrechen"); cancel.Location = new Point(270, 490); cancel.Size = new Size(110, 40); cancel.Click += (_, _) => { DialogResult = DialogResult.Cancel; Close(); }; Controls.Add(title); Controls.Add(subtitle); Controls.Add(card); Controls.Add(cancel); Controls.Add(_btnSave); Shown += async (_, _) => await LoadContainersAsync(); } private void BrowseForFile() { using OpenFileDialog dialog = new() { Title = "Vorhandene Zertifikatsdatei wählen", Filter = "Zertifikate (*.cer;*.crt;*.pem;*.pfx;*.p12)|" + "*.cer;*.crt;*.pem;*.pfx;*.p12|" + "Alle Dateien (*.*)|*.*", CheckFileExists = false, Multiselect = false }; if (dialog.ShowDialog(this) != DialogResult.OK) { return; } _txtFullPath.Text = dialog.FileName; RunProbe(); } private void RunProbe() { _lastProbe = _probeService.Probe( _txtFullPath.Text, () => PromptForPassword(this)); if (!_lastProbe.Success) { _lblProbeResult.ForeColor = Color.FromArgb(220, 100, 100); _lblProbeResult.Text = _lastProbe.Message; return; } if (!_lastProbe.FileExists) { _lblProbeResult.ForeColor = Color.FromArgb(230, 180, 80); _lblProbeResult.Text = _lastProbe.Message + " Pfad kann trotzdem gespeichert werden " + "(erstes Deployment)."; return; } CertificateInfo cert = _lastProbe.Certificate!; string expiry = cert.ValidUntil.ToLocalTime() .ToString("dd.MM.yyyy HH:mm"); _lblProbeResult.ForeColor = cert.IsCurrentlyValid ? Color.FromArgb(90, 200, 140) : Color.FromArgb(220, 100, 100); _lblProbeResult.Text = $"Datei vorhanden · Subject: {cert.Subject}\n" + $"Ablauf: {expiry} · Fingerprint: {cert.FingerprintSha256}"; } private async Task LoadContainersAsync() { try { IReadOnlyList containers = await _coordinator.LoadContainersAsync(); _cmbContainer.Items.Clear(); int selected = 0; int? preferredId = _existing?.SonicContainerId ?? _preferredSonicContainerId; for (int index = 0; index < containers.Count; index++) { _cmbContainer.Items.Add(containers[index]); if (preferredId is int id && containers[index].SonicContainerId == id) { selected = index; } } if (_cmbContainer.Items.Count > 0) { _cmbContainer.SelectedIndex = selected; } } catch (Exception ex) { MessageBox.Show( this, ex.Message, "Zertifikat-Pfad", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private async Task SaveAsync() { if (_running) { return; } if (_cmbContainer.SelectedItem is not ContainerOption container) { MessageBox.Show( this, "Bitte ein Neustart-Ziel (Sonic-Container) wählen.", "Zertifikat-Pfad", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (!CertificateProbeService.TrySplitTargetPath( _txtFullPath.Text, out string directory, out string fileName, out string? splitError)) { MessageBox.Show( this, splitError ?? "Ungültiger Pfad.", "Zertifikat-Pfad", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } // Vor dem Speichern immer prüfen RunProbe(); if (_lastProbe is null || !_lastProbe.Success) { MessageBox.Show( this, _lastProbe?.Message ?? "Pfad konnte nicht geprüft werden.", "Zertifikat-Pfad", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (!_lastProbe.FileExists) { DialogResult confirm = MessageBox.Show( this, "Am Ziel liegt noch keine Zertifikatsdatei.\n\n" + "Pfad trotzdem speichern?", "Datei fehlt", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (confirm != DialogResult.Yes) { return; } } _running = true; _btnSave.Enabled = false; Cursor = Cursors.WaitCursor; try { if (_existing is not null) { await _coordinator.UpdateCertificateTargetAsync( _existing.CertificateTargetId, container.SonicContainerId, directory, fileName, _chkBackup.Checked, _txtBackupFolder.Text); CreatedCertificateTargetId = _existing.CertificateTargetId; } else { CreatedCertificateTargetId = await _coordinator.AddCertificateTargetAsync( container.SonicContainerId, directory, fileName, _chkBackup.Checked, _txtBackupFolder.Text); } DialogResult = DialogResult.OK; Close(); } catch (Exception ex) { MessageBox.Show( this, ex.Message, "Zertifikat-Pfad", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { _running = false; _btnSave.Enabled = true; Cursor = Cursors.Default; } } private static string? PromptForPassword(IWin32Window owner) { using Form dialog = new() { Text = "PFX/P12-Kennwort", FormBorderStyle = FormBorderStyle.FixedDialog, StartPosition = FormStartPosition.CenterParent, ClientSize = new Size(360, 140), MaximizeBox = false, MinimizeBox = false, ShowInTaskbar = false, BackColor = SettingsUi.Background, ForeColor = SettingsUi.Text }; Label label = new() { Text = "Kennwort für die Zertifikatsdatei:", Location = new Point(16, 16), AutoSize = true }; TextBox txtPwd = new() { Location = new Point(16, 44), Width = 320, UseSystemPasswordChar = true }; Button btnOk = SettingsUi.CreatePrimaryButton("OK"); btnOk.Location = new Point(196, 90); btnOk.Size = new Size(140, 32); btnOk.DialogResult = DialogResult.OK; Button btnCancel = SettingsUi.CreateGhostButton("Abbrechen"); btnCancel.Location = new Point(100, 90); btnCancel.Size = new Size(90, 32); btnCancel.DialogResult = DialogResult.Cancel; dialog.Controls.Add(label); dialog.Controls.Add(txtPwd); dialog.Controls.Add(btnOk); dialog.Controls.Add(btnCancel); dialog.AcceptButton = btnOk; dialog.CancelButton = btnCancel; return dialog.ShowDialog(owner) == DialogResult.OK ? txtPwd.Text : null; } private static void AddField( Control parent, string label, Control input, int x, int y, int width) { Label fieldLabel = SettingsUi.CreateFieldLabel(label); fieldLabel.Location = new Point(x, y); input.Location = new Point(x, y + 22); input.Width = width; parent.Controls.Add(fieldLabel); parent.Controls.Add(input); } }