From 69558f3155a5dfb20332ca057cfc15c4b8dce45d Mon Sep 17 00:00:00 2001 From: Marco Wende Date: Wed, 10 Jun 2026 21:57:50 +0200 Subject: [PATCH] Add FirefoxAddinManager.ps1 --- FirefoxAddinManager.ps1 | 1529 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 1528 insertions(+), 1 deletion(-) diff --git a/FirefoxAddinManager.ps1 b/FirefoxAddinManager.ps1 index 14d7fd7..cc178c3 100644 --- a/FirefoxAddinManager.ps1 +++ b/FirefoxAddinManager.ps1 @@ -1 +1,1528 @@ -I1JlcXVpcmVzIC1WZXJzaW9uIDUuMQojIEZpcmVmb3ggQWRkLW9uIE1hbmFnZXIgLSBNYXNjaGluZW5iYXNpZXJ0ZSBCZXJlaXRzdGVsbHVuZwojIEZ1ZXIgbWFzY2hpbmVud2VpdGUgRGVwbG95bWVudC1PcHRpb25lbiBhbHMgQWRtaW5pc3RyYXRvciBhdXNmdWVocmVuLgoKQWRkLVR5cGUgLUFzc2VtYmx5TmFtZSBTeXN0ZW0uV2luZG93cy5Gb3JtcwpBZGQtVHlwZSAtQXNzZW1ibHlOYW1lIFN5c3RlbS5EcmF3aW5nCgojIC0tLS0gSGlsZnNmdW5rdGlvbmVuIC0tLS0KCmZ1bmN0aW9uIEZpbmQtRmlyZWZveFByb2ZpbGUgewogICAgJGFwcERhdGEgPSBbRW52aXJvbm1lbnRdOjpHZXRGb2xkZXJQYXRoKCJBcHBsaWNhdGlvbkRhdGEiKQogICAgJHByb2ZpbGVzUGF0aCA9IEpvaW4tUGF0aCAkYXBwRGF0YSAiTW96aWxsYVxGaXJlZm94XFByb2ZpbGVzIgogICAgaWYgKC1ub3QgKFRlc3QtUGF0aCAkcHJvZmlsZXNQYXRoKSkgeyByZXR1cm4gJG51bGwgfQogICAgJHByb2ZpbGVzID0gR2V0LUNoaWxkSXRlbSAkcHJvZmlsZXNQYXRoIC1EaXJlY3RvcnkgfAogICAgICAgIFdoZXJlLU9iamVjdCB7ICRfLk5hbWUgLW1hdGNoICdcLmRlZmF1bHQnIC1vciAkXy5OYW1lIC1tYXRjaCAnZGVmYXVsdC1yZWxlYXNlJyB9CiAgICBpZiAoJHByb2ZpbGVzKSB7IHJldHVybiAkcHJvZmlsZXNbMF0uRnVsbE5hbWUgfQogICAgJGFsbCA9IEdldC1DaGlsZEl0ZW0gJHByb2ZpbGVzUGF0aCAtRGlyZWN0b3J5CiAgICBpZiAoJGFsbCkgeyByZXR1cm4gJGFsbFswXS5GdWxsTmFtZSB9CiAgICByZXR1cm4gJG51bGwKfQ== \ No newline at end of file +#Requires -Version 5.1 +# Firefox Add-on Manager - Maschinenbasierte Bereitstellung +# Fuer maschinenweite Deployment-Optionen als Administrator ausfuehren. + +Add-Type -AssemblyName System.Windows.Forms +Add-Type -AssemblyName System.Drawing + +# ---- Hilfsfunktionen ---- + +function Find-FirefoxProfile { + $appData = [Environment]::GetFolderPath("ApplicationData") + $profilesPath = Join-Path $appData "Mozilla\Firefox\Profiles" + if (-not (Test-Path $profilesPath)) { return $null } + $profiles = Get-ChildItem $profilesPath -Directory | + Where-Object { $_.Name -match '\.default' -or $_.Name -match 'default-release' } + if ($profiles) { return $profiles[0].FullName } + $all = Get-ChildItem $profilesPath -Directory + if ($all) { return $all[0].FullName } + return $null +} + +function Find-FirefoxInstallDir { + $candidates = @( + "C:\Program Files\Mozilla Firefox", + "C:\Program Files (x86)\Mozilla Firefox" + ) + foreach ($c in $candidates) { + if (Test-Path (Join-Path $c "firefox.exe")) { return $c } + } + # Suche in Registry + try { + $reg = Get-ItemProperty "HKLM:\SOFTWARE\Mozilla\Mozilla Firefox" -ErrorAction Stop + $installDir = $reg."Install Directory" + if ($installDir -and (Test-Path $installDir)) { return $installDir } + } catch {} + return $null +} + +function Get-InstalledAddons { + param([string]$ProfilePath) + $extensionsJson = Join-Path $ProfilePath "extensions.json" + if (-not (Test-Path $extensionsJson)) { return @() } + $json = Get-Content $extensionsJson -Raw | ConvertFrom-Json + return $json.addons | + Where-Object { $_.type -eq "extension" } | + Select-Object id, defaultLocale, version, active +} + +function Install-AddonFromFile { + param([string]$ProfilePath, [string]$XpiPath) + $extensionsDir = Join-Path $ProfilePath "extensions" + if (-not (Test-Path $extensionsDir)) { + New-Item -ItemType Directory -Path $extensionsDir | Out-Null + } + Add-Type -AssemblyName System.IO.Compression.FileSystem + $zip = [System.IO.Compression.ZipFile]::OpenRead($XpiPath) + try { + $manifestEntry = $zip.Entries | Where-Object { $_.Name -eq "manifest.json" } + if (-not $manifestEntry) { throw "Kein manifest.json in der XPI-Datei gefunden." } + $reader = New-Object System.IO.StreamReader($manifestEntry.Open()) + $manifest = $reader.ReadToEnd() | ConvertFrom-Json + $reader.Close() + $addonId = $null + if ($manifest.browser_specific_settings.gecko.id) { + $addonId = $manifest.browser_specific_settings.gecko.id + } elseif ($manifest.applications.gecko.id) { + $addonId = $manifest.applications.gecko.id + } else { + $addonId = [System.IO.Path]::GetFileNameWithoutExtension($XpiPath) + "@local" + } + } finally { + $zip.Dispose() + } + $dest = Join-Path $extensionsDir "$addonId.xpi" + Copy-Item -Path $XpiPath -Destination $dest -Force + return $addonId +} + +function Open-AddonPage { + param([string]$Url) + Start-Process "firefox" $Url -ErrorAction SilentlyContinue + if ($LASTEXITCODE -ne 0) { Start-Process $Url } +} + +function Test-IsAdmin { + $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object System.Security.Principal.WindowsPrincipal($id) + return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Test-IntuneManaged { + # Intune schreibt Firefox-Policies unter HKLM\SOFTWARE\Policies\Mozilla\Firefox + # Wenn dieser Key existiert und NICHT von uns stammt, verwaltet Intune Firefox + $key = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox" + if (Test-Path $key) { + $props = Get-ItemProperty $key -ErrorAction SilentlyContinue + # Intune setzt typischerweise DisableAppUpdate, DontCheckDefaultBrowser o.ae. + if ($props) { return $true } + } + return $false +} + +function Get-ActiveFirefoxPolicies { + # Liest alle aktiven Registry-Policies aus + $base = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox" + $result = @() + if (-not (Test-Path $base)) { return $result } + function Read-RegTree { + param([string]$Path, [string]$Indent = "") + $props = Get-ItemProperty $Path -ErrorAction SilentlyContinue + if ($props) { + foreach ($p in $props.PSObject.Properties) { + if ($p.Name -notmatch '^PS') { + $result += $Indent + $p.Name + " = " + $p.Value + } + } + } + Get-ChildItem $Path -ErrorAction SilentlyContinue | ForEach-Object { + $result += $Indent + "[" + $_.PSChildName + "]" + Read-RegTree -Path $_.PSPath -Indent ($Indent + " ") + } + } + Read-RegTree -Path $base + return $result +} + +function Export-RegFile { + param([string]$OutputPath, [array]$Addons) + $lines = @() + $lines += "Windows Registry Editor Version 5.00" + $lines += "" + $lines += "; Firefox Add-on Deployment via Intune Custom Policy" + $lines += "; Importieren: doppelklick auf .reg oder via Intune > Devices > Configuration > Custom (OMA-URI)" + $lines += "" + $lines += "[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings]" + $lines += "" + foreach ($a in $Addons) { + $url = if ($a.Url -and $a.Url.StartsWith("http")) { $a.Url } else { + "https://addons.mozilla.org/firefox/downloads/latest/latest.xpi" + } + # Registry-Schluessel-Namen muessen Backslashes escapen + $escapedId = $a.Id -replace '\\', '\\' + $lines += '[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings\' + $escapedId + ']' + $lines += '"installation_mode"="force_installed"' + $lines += '"install_url"="' + ($url -replace '\\', '\\') + '"' + $lines += "" + } + $lines | Out-File -FilePath $OutputPath -Encoding Unicode +} + +# ---- Add-on Datenbank ---- +$AllAddons = [System.Collections.Generic.List[PSCustomObject]]::new() +$AllAddons.Add([PSCustomObject]@{ + Name="uBlock Origin"; Id="uBlock0@raymondhill.net" + Url="https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi" + StoreUrl="https://addons.mozilla.org/firefox/addon/ublock-origin/" + Beschreibung="Effizienter Werbeblocker" +}) +$AllAddons.Add([PSCustomObject]@{ + Name="Privacy Badger"; Id="jid1-MnnxcxisBPnSXQ@jetpack" + Url="https://addons.mozilla.org/firefox/downloads/latest/privacy-badger17/latest.xpi" + StoreUrl="https://addons.mozilla.org/firefox/addon/privacy-badger17/" + Beschreibung="Schutz vor Tracking" +}) +$AllAddons.Add([PSCustomObject]@{ + Name="Bitwarden"; Id="{446900e4-71c2-419f-a6a7-df9c091e268b}" + Url="https://addons.mozilla.org/firefox/downloads/latest/bitwarden-password-manager/latest.xpi" + StoreUrl="https://addons.mozilla.org/firefox/addon/bitwarden-password-manager/" + Beschreibung="Passwort-Manager" +}) +$AllAddons.Add([PSCustomObject]@{ + Name="Dark Reader"; Id="addon@darkreader.org" + Url="https://addons.mozilla.org/firefox/downloads/latest/darkreader/latest.xpi" + StoreUrl="https://addons.mozilla.org/firefox/addon/darkreader/" + Beschreibung="Dunkles Design fuer alle Seiten" +}) +$AllAddons.Add([PSCustomObject]@{ + Name="Cookie AutoDelete"; Id="CookieAutoDelete@kennydo.com" + Url="https://addons.mozilla.org/firefox/downloads/latest/cookie-autodelete/latest.xpi" + StoreUrl="https://addons.mozilla.org/firefox/addon/cookie-autodelete/" + Beschreibung="Cookies automatisch loeschen" +}) +$AllAddons.Add([PSCustomObject]@{ + Name="Ghostery"; Id="firefox@ghostery.com" + Url="https://addons.mozilla.org/firefox/downloads/latest/ghostery/latest.xpi" + StoreUrl="https://addons.mozilla.org/firefox/addon/ghostery/" + Beschreibung="Anti-Tracking und Werbeblocker" +}) + +# ---- GUI ---- + +$form = New-Object System.Windows.Forms.Form +$form.Text = "Firefox Add-on Manager - Maschinenbasierte Bereitstellung" +$form.Size = New-Object System.Drawing.Size(900, 680) +$form.StartPosition = "CenterScreen" +$form.FormBorderStyle = "FixedSingle" +$form.MaximizeBox = $false +$form.BackColor = [System.Drawing.Color]::FromArgb(245, 245, 250) + +# Banner +$banner = New-Object System.Windows.Forms.Panel +$banner.Size = New-Object System.Drawing.Size(900, 60) +$banner.Location = New-Object System.Drawing.Point(0, 0) +$banner.BackColor = [System.Drawing.Color]::FromArgb(255, 100, 20) +$form.Controls.Add($banner) + +$titleLabel = New-Object System.Windows.Forms.Label +$titleLabel.Text = "Firefox Add-on Manager" +$titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 16, [System.Drawing.FontStyle]::Bold) +$titleLabel.ForeColor = [System.Drawing.Color]::White +$titleLabel.AutoSize = $true +$titleLabel.Location = New-Object System.Drawing.Point(15, 10) +$banner.Controls.Add($titleLabel) + +$adminLabel = New-Object System.Windows.Forms.Label +if (Test-IsAdmin) { + $adminLabel.Text = "Administrator-Modus aktiv" + $adminLabel.ForeColor = [System.Drawing.Color]::LightGreen +} else { + $adminLabel.Text = "KEIN Admin - maschinenweite Bereitstellung eingeschraenkt" + $adminLabel.ForeColor = [System.Drawing.Color]::Yellow +} +$adminLabel.Font = New-Object System.Drawing.Font("Segoe UI", 8) +$adminLabel.AutoSize = $true +$adminLabel.Location = New-Object System.Drawing.Point(17, 40) +$banner.Controls.Add($adminLabel) + +# TabControl +$tabs = New-Object System.Windows.Forms.TabControl +$tabs.Location = New-Object System.Drawing.Point(10, 70) +$tabs.Size = New-Object System.Drawing.Size(870, 535) +$tabs.Font = New-Object System.Drawing.Font("Segoe UI", 10) +$form.Controls.Add($tabs) + +# =========================================================== +# TAB 1: Maschinenbasierte Bereitstellung (policies.json) +# =========================================================== +$tabDeploy = New-Object System.Windows.Forms.TabPage +$tabDeploy.Text = "Maschinenweite Bereitstellung" +$tabs.Controls.Add($tabDeploy) + +# Info-Box +$pnlInfo = New-Object System.Windows.Forms.Panel +$pnlInfo.Location = New-Object System.Drawing.Point(10, 10) +$pnlInfo.Size = New-Object System.Drawing.Size(845, 55) +$pnlInfo.BackColor = [System.Drawing.Color]::FromArgb(220, 235, 255) +$pnlInfo.BorderStyle = "FixedSingle" +$tabDeploy.Controls.Add($pnlInfo) + +$lblInfoText = New-Object System.Windows.Forms.Label +$lblInfoText.Text = "Add-ons werden per Firefox Enterprise Policy (policies.json) oder Windows Registry fuer ALLE Benutzer" + + " dieser Maschine zwingend installiert. Erfordert Administratorrechte und Firefox-Neustart." +$lblInfoText.Font = New-Object System.Drawing.Font("Segoe UI", 8) +$lblInfoText.Location = New-Object System.Drawing.Point(5, 5) +$lblInfoText.Size = New-Object System.Drawing.Size(833, 44) +$pnlInfo.Controls.Add($lblInfoText) + +# Interne URL Warnung +$pnlUrlWarn = New-Object System.Windows.Forms.Panel +$pnlUrlWarn.Location = New-Object System.Drawing.Point(10, 75) +$pnlUrlWarn.Size = New-Object System.Drawing.Size(845, 28) +$pnlUrlWarn.BackColor = [System.Drawing.Color]::FromArgb(255, 240, 200) +$pnlUrlWarn.BorderStyle = "FixedSingle" +$tabDeploy.Controls.Add($pnlUrlWarn) + +$lblUrlWarn = New-Object System.Windows.Forms.Label +$lblUrlWarn.Text = "Intune/Firmen-Umgebung: addons.mozilla.org ist moeglicherweise blockiert. " + + "Bitte XPIs herunterladen, intern hosten und interne URL eintragen (Spalte 'Interne URL')." +$lblUrlWarn.Font = New-Object System.Drawing.Font("Segoe UI", 8) +$lblUrlWarn.ForeColor = [System.Drawing.Color]::DarkOrange +$lblUrlWarn.Location = New-Object System.Drawing.Point(5, 6) +$lblUrlWarn.AutoSize = $true +$pnlUrlWarn.Controls.Add($lblUrlWarn) + +# Add-on Auswahl +$grpSelect = New-Object System.Windows.Forms.GroupBox +$grpSelect.Text = "Add-ons auswaehlen (Doppelklick = interne URL bearbeiten)" +$grpSelect.Location = New-Object System.Drawing.Point(10, 110) +$grpSelect.Size = New-Object System.Drawing.Size(845, 175) +$grpSelect.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$tabDeploy.Controls.Add($grpSelect) + +$lvDeploy = New-Object System.Windows.Forms.ListView +$lvDeploy.Location = New-Object System.Drawing.Point(10, 18) +$lvDeploy.Size = New-Object System.Drawing.Size(822, 148) +$lvDeploy.View = [System.Windows.Forms.View]::Details +$lvDeploy.CheckBoxes = $true +$lvDeploy.FullRowSelect = $true +$lvDeploy.GridLines = $true +$lvDeploy.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$lvDeploy.Columns.Add("Add-on Name", 160) | Out-Null +$lvDeploy.Columns.Add("Add-on ID", 210) | Out-Null +$lvDeploy.Columns.Add("Interne URL (leer = AMO)", 420) | Out-Null + +foreach ($addon in $AllAddons) { + $item = New-Object System.Windows.Forms.ListViewItem($addon.Name) + $item.SubItems.Add($addon.Id) | Out-Null + $item.SubItems.Add("") | Out-Null # Interne URL leer = AMO wird genutzt + $item.Tag = $addon + $lvDeploy.Items.Add($item) | Out-Null +} + +# Doppelklick: interne URL bearbeiten +$lvDeploy.Add_DoubleClick({ + if ($lvDeploy.SelectedItems.Count -eq 0) { return } + $sel = $lvDeploy.SelectedItems[0] + $currentUrl = $sel.SubItems[2].Text + $dlg = New-Object System.Windows.Forms.Form + $dlg.Text = "Interne URL fuer: " + $sel.Text + $dlg.Size = New-Object System.Drawing.Size(620, 130) + $dlg.StartPosition = "CenterParent" + $dlg.FormBorderStyle = "FixedDialog" + $dlg.MaximizeBox = $false; $dlg.MinimizeBox = $false + + $lbl = New-Object System.Windows.Forms.Label + $lbl.Text = "Interne HTTPS-URL zur XPI-Datei (leer lassen = addons.mozilla.org wird verwendet):" + $lbl.Location = New-Object System.Drawing.Point(10, 10) + $lbl.Size = New-Object System.Drawing.Size(590, 18) + $lbl.Font = New-Object System.Drawing.Font("Segoe UI", 8) + $dlg.Controls.Add($lbl) + + $txt = New-Object System.Windows.Forms.TextBox + $txt.Location = New-Object System.Drawing.Point(10, 32) + $txt.Size = New-Object System.Drawing.Size(590, 23) + $txt.Font = New-Object System.Drawing.Font("Segoe UI", 9) + $txt.Text = $currentUrl + $dlg.Controls.Add($txt) + + $btnOk = New-Object System.Windows.Forms.Button + $btnOk.Text = "OK"; $btnOk.Location = New-Object System.Drawing.Point(440, 62) + $btnOk.Size = New-Object System.Drawing.Size(75, 26) + $btnOk.DialogResult = "OK" + $dlg.Controls.Add($btnOk); $dlg.AcceptButton = $btnOk + + $btnClear = New-Object System.Windows.Forms.Button + $btnClear.Text = "Leeren"; $btnClear.Location = New-Object System.Drawing.Point(520, 62) + $btnClear.Size = New-Object System.Drawing.Size(75, 26) + $btnClear.Add_Click({ $txt.Text = ""; $dlg.DialogResult = "OK"; $dlg.Close() }) + $dlg.Controls.Add($btnClear) + + if ($dlg.ShowDialog() -eq "OK") { + $sel.SubItems[2].Text = $txt.Text.Trim() + if ($txt.Text.Trim() -ne "") { + $sel.ForeColor = [System.Drawing.Color]::DarkGreen + } else { + $sel.ForeColor = [System.Drawing.Color]::Black + } + } +}) +$grpSelect.Controls.Add($lvDeploy) + +# XPI herunterladen Button (nebeneinander mit Auswahl) +$btnDownloadXpis = New-Object System.Windows.Forms.Button +$btnDownloadXpis.Text = "XPIs herunterladen (fuer internes Hosting)" +$btnDownloadXpis.Size = New-Object System.Drawing.Size(280, 30) +$btnDownloadXpis.Location = New-Object System.Drawing.Point(10, 290) +$btnDownloadXpis.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 80) +$btnDownloadXpis.ForeColor = [System.Drawing.Color]::White +$btnDownloadXpis.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) +$btnDownloadXpis.FlatStyle = "Flat" +$btnDownloadXpis.FlatAppearance.BorderSize = 0 +$btnDownloadXpis.Add_Click({ + $selected = $lvDeploy.Items | Where-Object { $_.Checked } + if ($selected.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show("Bitte mindestens ein Add-on auswaehlen.", "Hinweis", "OK", "Information") + return + } + $fbd = New-Object System.Windows.Forms.FolderBrowserDialog + $fbd.Description = "Zielordner fuer XPI-Dateien auswaehlen" + if ($fbd.ShowDialog() -ne "OK") { return } + + $errors = @() + foreach ($item in $selected) { + $addon = $item.Tag + $url = $addon.Url + $fileName = $addon.Name -replace '[^\w]', '_' + $dest = Join-Path $fbd.SelectedPath ($fileName + ".xpi") + try { + $wc = New-Object System.Net.WebClient + $wc.DownloadFile($url, $dest) + # Interne URL vorschlagen + $item.SubItems[2].Text = "(XPI gespeichert: $dest - bitte interne URL eintragen)" + $item.ForeColor = [System.Drawing.Color]::DarkBlue + } catch { + $errors += $addon.Name + ": " + $_ + } + } + if ($errors.Count -gt 0) { + [System.Windows.Forms.MessageBox]::Show( + "Fehler beim Herunterladen:`n" + ($errors -join "`n"), + "Fehler", "OK", "Error") + } else { + [System.Windows.Forms.MessageBox]::Show( + "XPI-Dateien gespeichert in:`n" + $fbd.SelectedPath + + "`n`nNaechste Schritte:`n" + + "1. Dateien auf internen Webserver / SharePoint / Azure Blob hochladen`n" + + "2. HTTPS-URL per Doppelklick in der Liste eintragen`n" + + "3. Bereitstellung ausfuehren", + "Download abgeschlossen", "OK", "Information") + } +}) +$tabDeploy.Controls.Add($btnDownloadXpis) + +# Eigene Add-on ID hinzufuegen +$grpCustom = New-Object System.Windows.Forms.GroupBox +$grpCustom.Text = "Eigene Add-on ID hinzufuegen (z.B. aus eigenem Add-on)" +$grpCustom.Location = New-Object System.Drawing.Point(10, 283) +$grpCustom.Size = New-Object System.Drawing.Size(845, 72) +$grpCustom.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$tabDeploy.Controls.Add($grpCustom) + +$lblCustomIdHint = New-Object System.Windows.Forms.Label +$lblCustomIdHint.Text = "Add-on ID (z.B. myaddon@firma.de):" +$lblCustomIdHint.Font = New-Object System.Drawing.Font("Segoe UI", 8) +$lblCustomIdHint.ForeColor = [System.Drawing.Color]::Gray +$lblCustomIdHint.Location = New-Object System.Drawing.Point(10, 8) +$lblCustomIdHint.AutoSize = $true +$grpCustom.Controls.Add($lblCustomIdHint) + +$txtCustomId = New-Object System.Windows.Forms.TextBox +$txtCustomId.Location = New-Object System.Drawing.Point(10, 25) +$txtCustomId.Size = New-Object System.Drawing.Size(380, 23) +$txtCustomId.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$grpCustom.Controls.Add($txtCustomId) + +$lblCustomUrlHint = New-Object System.Windows.Forms.Label +$lblCustomUrlHint.Text = "Download-URL der XPI (optional):" +$lblCustomUrlHint.Font = New-Object System.Drawing.Font("Segoe UI", 8) +$lblCustomUrlHint.ForeColor = [System.Drawing.Color]::Gray +$lblCustomUrlHint.Location = New-Object System.Drawing.Point(400, 8) +$lblCustomUrlHint.AutoSize = $true +$grpCustom.Controls.Add($lblCustomUrlHint) + +$txtCustomUrl = New-Object System.Windows.Forms.TextBox +$txtCustomUrl.Location = New-Object System.Drawing.Point(400, 25) +$txtCustomUrl.Size = New-Object System.Drawing.Size(318, 23) +$txtCustomUrl.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$grpCustom.Controls.Add($txtCustomUrl) + +$btnAddCustom = New-Object System.Windows.Forms.Button +$btnAddCustom.Text = "+ Hinzufuegen" +$btnAddCustom.Location = New-Object System.Drawing.Point(728, 34) +$btnAddCustom.Size = New-Object System.Drawing.Size(108, 27) +$btnAddCustom.Add_Click({ + if ($txtCustomId.Text.Trim() -eq "") { + [System.Windows.Forms.MessageBox]::Show("Bitte Add-on ID eingeben.", "Hinweis", "OK", "Information") + return + } + $newItem = New-Object System.Windows.Forms.ListViewItem("[Eigene] " + $txtCustomId.Text.Trim()) + $newItem.SubItems.Add("Eigenes Add-on") | Out-Null + $newItem.SubItems.Add($txtCustomId.Text.Trim()) | Out-Null + $customAddon = [PSCustomObject]@{ + Name="[Eigene] " + $txtCustomId.Text.Trim() + Id=$txtCustomId.Text.Trim() + Url=$txtCustomUrl.Text.Trim() + Beschreibung="Eigenes Add-on" + } + $newItem.Tag = $customAddon + $newItem.Checked = $true + $lvDeploy.Items.Add($newItem) | Out-Null + $txtCustomId.Text = "" + $txtCustomUrl.Text = "" +}) +$grpCustom.Controls.Add($btnAddCustom) + +# Bereitstellungs-Optionen +$grpMethod = New-Object System.Windows.Forms.GroupBox +$grpMethod.Text = "Bereitstellungs-Methode" +$grpMethod.Location = New-Object System.Drawing.Point(10, 352) +$grpMethod.Size = New-Object System.Drawing.Size(845, 70) +$grpMethod.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$tabDeploy.Controls.Add($grpMethod) + +$rbPoliciesJson = New-Object System.Windows.Forms.RadioButton +$rbPoliciesJson.Text = "policies.json (direkt in Firefox-Installationsordner - empfohlen)" +$rbPoliciesJson.Location = New-Object System.Drawing.Point(10, 20) +$rbPoliciesJson.AutoSize = $true +$rbPoliciesJson.Checked = $true +$rbPoliciesJson.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$grpMethod.Controls.Add($rbPoliciesJson) + +$rbRegistry = New-Object System.Windows.Forms.RadioButton +$rbRegistry.Text = "Windows Registry (HKLM - GPO-kompatibel, fuer Gruppenrichtlinien-Umgebungen)" +$rbRegistry.Location = New-Object System.Drawing.Point(10, 42) +$rbRegistry.AutoSize = $true +$rbRegistry.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$grpMethod.Controls.Add($rbRegistry) + +# Aktions-Buttons +$btnDeploy = New-Object System.Windows.Forms.Button +$btnDeploy.Text = "Jetzt bereitstellen (diese Maschine)" +$btnDeploy.Size = New-Object System.Drawing.Size(260, 40) +$btnDeploy.Location = New-Object System.Drawing.Point(10, 435) +$btnDeploy.BackColor = [System.Drawing.Color]::FromArgb(255, 100, 20) +$btnDeploy.ForeColor = [System.Drawing.Color]::White +$btnDeploy.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) +$btnDeploy.FlatStyle = "Flat" +$btnDeploy.FlatAppearance.BorderSize = 0 +$btnDeploy.Add_Click({ + $selected = $lvDeploy.Items | Where-Object { $_.Checked } + if ($selected.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show( + "Bitte mindestens ein Add-on auswaehlen.", "Hinweis", "OK", "Information") + return + } + if (-not (Test-IsAdmin)) { + [System.Windows.Forms.MessageBox]::Show( + "Maschinenweite Bereitstellung erfordert Administratorrechte.`n" + + "Bitte das Skript als Administrator ausfuehren.", + "Kein Administrator", "OK", "Warning") + return + } + + # Interne URL aus ListView-Spalte 2 uebernehmen (per Doppelklick eingetragen) + $addonsToInstall = $selected | ForEach-Object { + $clone = [PSCustomObject]@{ + Name = $_.Tag.Name + Id = $_.Tag.Id + Url = $_.Tag.Url + Beschreibung= $_.Tag.Beschreibung + } + $internalUrl = $_.SubItems[2].Text.Trim() + if ($internalUrl -ne "" -and $internalUrl.StartsWith("http")) { + $clone.Url = $internalUrl + } + $clone + } + + if ($rbPoliciesJson.Checked) { + # --- policies.json Methode --- + $ffDir = Find-FirefoxInstallDir + if (-not $ffDir) { + [System.Windows.Forms.MessageBox]::Show( + "Firefox-Installationsverzeichnis nicht gefunden.`n" + + "Bitte Firefox installieren oder den Pfad pruefen.", + "Fehler", "OK", "Error") + return + } + $distributionDir = Join-Path $ffDir "distribution" + if (-not (Test-Path $distributionDir)) { + New-Item -ItemType Directory -Path $distributionDir | Out-Null + } + $policiesFile = Join-Path $distributionDir "policies.json" + + # Vorhandene policies.json einlesen oder neu erstellen + if (Test-Path $policiesFile) { + $existingJson = Get-Content $policiesFile -Raw | ConvertFrom-Json + } else { + $existingJson = [PSCustomObject]@{ policies = [PSCustomObject]@{} } + } + if (-not $existingJson.policies.PSObject.Properties["ExtensionSettings"]) { + $existingJson.policies | Add-Member -NotePropertyName "ExtensionSettings" ` + -NotePropertyValue ([PSCustomObject]@{}) -Force + } + + foreach ($a in $addonsToInstall) { + $setting = [PSCustomObject]@{ + installation_mode = "force_installed" + } + if ($a.Url -and $a.Url.StartsWith("http")) { + $setting | Add-Member -NotePropertyName "install_url" -NotePropertyValue $a.Url + } + $existingJson.policies.ExtensionSettings | + Add-Member -NotePropertyName $a.Id -NotePropertyValue $setting -Force + } + + $existingJson | ConvertTo-Json -Depth 10 | Set-Content $policiesFile -Encoding UTF8 + [System.Windows.Forms.MessageBox]::Show( + "policies.json erfolgreich geschrieben:`n$policiesFile`n`n" + + "Die folgenden Add-ons werden beim naechsten Firefox-Start automatisch fuer alle Benutzer installiert:`n" + + ($addonsToInstall | ForEach-Object { " - " + $_.Name } | Out-String), + "Bereitstellung erfolgreich", "OK", "Information") + + } else { + # --- Registry Methode (korrekte ExtensionSettings-Struktur) --- + # Firefox liest: HKLM\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings\ + # Werte: installation_mode (REG_SZ) und install_url (REG_SZ) + $regBase = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings" + try { + if (-not (Test-Path $regBase)) { + New-Item -Path $regBase -Force | Out-Null + } + $written = @() + foreach ($a in $addonsToInstall) { + $addonKey = Join-Path $regBase $a.Id + if (-not (Test-Path $addonKey)) { + New-Item -Path $addonKey -Force | Out-Null + } + Set-ItemProperty -Path $addonKey -Name "installation_mode" ` + -Value "force_installed" -Type String + $url = if ($a.Url -and $a.Url.StartsWith("http")) { $a.Url } else { + "https://addons.mozilla.org/firefox/downloads/latest/latest.xpi" + } + Set-ItemProperty -Path $addonKey -Name "install_url" -Value $url -Type String + + # Verifizieren + $verify = Get-ItemProperty -Path $addonKey -ErrorAction SilentlyContinue + if ($verify.installation_mode -eq "force_installed") { + $written += $a.Name + " [OK] -> " + $addonKey + } else { + $written += $a.Name + " [FEHLER - Eintrag nicht verifiziert]" + } + } + $summary = $written | Out-String + [System.Windows.Forms.MessageBox]::Show( + "Registry-Eintraege gesetzt unter:`n$regBase\`n`n" + + "Ergebnis:`n" + $summary + + "`nFirefox beim naechsten Start installiert die Add-ons automatisch fuer alle Benutzer.", + "Bereitstellung erfolgreich", "OK", "Information") + } catch { + [System.Windows.Forms.MessageBox]::Show( + "Registry-Fehler: $_`n`nBitte als Administrator ausfuehren.", + "Fehler", "OK", "Error") + } + } +}) +$tabDeploy.Controls.Add($btnDeploy) + +$btnExportScript = New-Object System.Windows.Forms.Button +$btnExportScript.Text = "Deployment-Skript exportieren" +$btnExportScript.Size = New-Object System.Drawing.Size(240, 40) +$btnExportScript.Location = New-Object System.Drawing.Point(280, 435) +$btnExportScript.BackColor = [System.Drawing.Color]::FromArgb(70, 130, 180) +$btnExportScript.ForeColor = [System.Drawing.Color]::White +$btnExportScript.Font = New-Object System.Drawing.Font("Segoe UI", 10) +$btnExportScript.FlatStyle = "Flat" +$btnExportScript.FlatAppearance.BorderSize = 0 +$btnExportScript.Add_Click({ + $selected = $lvDeploy.Items | Where-Object { $_.Checked } + if ($selected.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show( + "Bitte mindestens ein Add-on auswaehlen.", "Hinweis", "OK", "Information") + return + } + $sfd = New-Object System.Windows.Forms.SaveFileDialog + $sfd.Filter = "PowerShell-Skript (*.ps1)|*.ps1" + $sfd.FileName = "Deploy-FirefoxAddons.ps1" + if ($sfd.ShowDialog() -ne "OK") { return } + + $addonsToInstall = $selected | ForEach-Object { + $clone = [PSCustomObject]@{ + Name = $_.Tag.Name + Id = $_.Tag.Id + Url = $_.Tag.Url + Beschreibung= $_.Tag.Beschreibung + } + $internalUrl = $_.SubItems[2].Text.Trim() + if ($internalUrl -ne "" -and $internalUrl.StartsWith("http")) { + $clone.Url = $internalUrl + } + $clone + } + + $lines = @() + $lines += "#Requires -RunAsAdministrator" + $lines += "# Automatisch generiertes Firefox Add-on Deployment-Skript" + $lines += "# Erstellt mit Firefox Add-on Manager" + $lines += "" + $lines += '$ffDir = "C:\Program Files\Mozilla Firefox"' + $lines += '$distributionDir = Join-Path $ffDir "distribution"' + $lines += 'if (-not (Test-Path $distributionDir)) { New-Item -ItemType Directory -Path $distributionDir | Out-Null }' + $lines += '$policiesFile = Join-Path $distributionDir "policies.json"' + $lines += "" + + if ($rbPoliciesJson.Checked) { + $lines += "# policies.json erstellen" + $lines += '$policies = @{' + $lines += ' policies = @{' + $lines += ' ExtensionSettings = @{' + foreach ($a in $addonsToInstall) { + $lines += ' "' + $a.Id + '" = @{' + $lines += ' installation_mode = "force_installed"' + if ($a.Url -and $a.Url.StartsWith("http")) { + $lines += ' install_url = "' + $a.Url + '"' + } + $lines += ' }' + } + $lines += ' }' + $lines += ' }' + $lines += '}' + $lines += '$policies | ConvertTo-Json -Depth 10 | Set-Content $policiesFile -Encoding UTF8' + $lines += 'Write-Host "Deployment abgeschlossen. Firefox neu starten." -ForegroundColor Green' + } else { + $lines += "# Registry ExtensionSettings setzen" + $lines += '# Firefox liest: HKLM\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings\' + $lines += '$regBase = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings"' + $lines += 'if (-not (Test-Path $regBase)) { New-Item -Path $regBase -Force | Out-Null }' + foreach ($a in $addonsToInstall) { + $url = if ($a.Url -and $a.Url.StartsWith("http")) { $a.Url } else { + "https://addons.mozilla.org/firefox/downloads/latest/latest.xpi" + } + $lines += "" + $lines += "# " + $a.Name + $lines += '$addonKey = "' + ('HKLM:\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings\' + $a.Id) + '"' + $lines += 'New-Item -Path $addonKey -Force | Out-Null' + $lines += 'Set-ItemProperty -Path $addonKey -Name "installation_mode" -Value "force_installed" -Type String' + $lines += 'Set-ItemProperty -Path $addonKey -Name "install_url" -Value "' + $url + '" -Type String' + } + $lines += "" + $lines += 'Write-Host "Registry-Eintraege gesetzt. Firefox neu starten." -ForegroundColor Green' + } + + $lines | Out-File -FilePath $sfd.FileName -Encoding UTF8 + [System.Windows.Forms.MessageBox]::Show( + "Skript exportiert:`n" + $sfd.FileName, + "Export erfolgreich", "OK", "Information") +}) +$tabDeploy.Controls.Add($btnExportScript) + +$btnShowCurrent = New-Object System.Windows.Forms.Button +$btnShowCurrent.Text = "Aktuelle policies.json anzeigen" +$btnShowCurrent.Size = New-Object System.Drawing.Size(240, 40) +$btnShowCurrent.Location = New-Object System.Drawing.Point(530, 435) +$btnShowCurrent.BackColor = [System.Drawing.Color]::FromArgb(100, 100, 100) +$btnShowCurrent.ForeColor = [System.Drawing.Color]::White +$btnShowCurrent.Font = New-Object System.Drawing.Font("Segoe UI", 10) +$btnShowCurrent.FlatStyle = "Flat" +$btnShowCurrent.FlatAppearance.BorderSize = 0 +$btnShowCurrent.Add_Click({ + $ffDir = Find-FirefoxInstallDir + if (-not $ffDir) { + [System.Windows.Forms.MessageBox]::Show("Firefox-Installationsverzeichnis nicht gefunden.", "Hinweis", "OK", "Information") + return + } + $pf = Join-Path $ffDir "distribution\policies.json" + if (Test-Path $pf) { + $content = Get-Content $pf -Raw + $dlg = New-Object System.Windows.Forms.Form + $dlg.Text = "policies.json - " + $pf + $dlg.Size = New-Object System.Drawing.Size(700, 500) + $dlg.StartPosition = "CenterParent" + $tb = New-Object System.Windows.Forms.TextBox + $tb.Multiline = $true; $tb.ScrollBars = "Both"; $tb.ReadOnly = $true + $tb.Dock = "Fill"; $tb.Font = New-Object System.Drawing.Font("Consolas", 9) + $tb.Text = $content + $dlg.Controls.Add($tb) + $dlg.ShowDialog() | Out-Null + } else { + [System.Windows.Forms.MessageBox]::Show( + "Keine policies.json gefunden unter:`n" + (Join-Path $ffDir "distribution"), + "Nicht gefunden", "OK", "Information") + } +}) +$tabDeploy.Controls.Add($btnShowCurrent) + +# =========================================================== +# TAB 2: Beliebte Add-ons (Benutzer-Store) +# =========================================================== +$tabPopular = New-Object System.Windows.Forms.TabPage +$tabPopular.Text = "Store - Einzelinstallation" +$tabs.Controls.Add($tabPopular) + +$lblPopInfo = New-Object System.Windows.Forms.Label +$lblPopInfo.Text = "Add-on auswaehlen und per Firefox Store installieren (nur fuer aktuellen Benutzer)." +$lblPopInfo.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$lblPopInfo.ForeColor = [System.Drawing.Color]::Gray +$lblPopInfo.Location = New-Object System.Drawing.Point(10, 10) +$lblPopInfo.AutoSize = $true +$tabPopular.Controls.Add($lblPopInfo) + +$lvPopular = New-Object System.Windows.Forms.ListView +$lvPopular.Location = New-Object System.Drawing.Point(10, 35) +$lvPopular.Size = New-Object System.Drawing.Size(845, 390) +$lvPopular.View = [System.Windows.Forms.View]::Details +$lvPopular.FullRowSelect = $true +$lvPopular.GridLines = $true +$lvPopular.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$lvPopular.Columns.Add("Add-on Name", 200) | Out-Null +$lvPopular.Columns.Add("Beschreibung", 450) | Out-Null +$lvPopular.Columns.Add("Quelle", 160) | Out-Null +foreach ($addon in $AllAddons) { + $item = New-Object System.Windows.Forms.ListViewItem($addon.Name) + $item.SubItems.Add($addon.Beschreibung) | Out-Null + $item.SubItems.Add("Mozilla Store") | Out-Null + $item.Tag = $addon + $lvPopular.Items.Add($item) | Out-Null +} +$tabPopular.Controls.Add($lvPopular) + +$btnInstallPopular = New-Object System.Windows.Forms.Button +$btnInstallPopular.Text = "Im Firefox Store oeffnen" +$btnInstallPopular.Size = New-Object System.Drawing.Size(220, 35) +$btnInstallPopular.Location = New-Object System.Drawing.Point(10, 440) +$btnInstallPopular.BackColor = [System.Drawing.Color]::FromArgb(255, 100, 20) +$btnInstallPopular.ForeColor = [System.Drawing.Color]::White +$btnInstallPopular.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) +$btnInstallPopular.FlatStyle = "Flat" +$btnInstallPopular.FlatAppearance.BorderSize = 0 +$btnInstallPopular.Add_Click({ + if ($lvPopular.SelectedItems.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show("Bitte zuerst ein Add-on auswaehlen.", "Hinweis", "OK", "Information") + return + } + $addon = $lvPopular.SelectedItems[0].Tag + $url = if ($addon.StoreUrl) { $addon.StoreUrl } else { $addon.Url } + Open-AddonPage -Url $url +}) +$tabPopular.Controls.Add($btnInstallPopular) + +$btnOpenAMO = New-Object System.Windows.Forms.Button +$btnOpenAMO.Text = "Alle Add-ons durchsuchen" +$btnOpenAMO.Size = New-Object System.Drawing.Size(220, 35) +$btnOpenAMO.Location = New-Object System.Drawing.Point(240, 440) +$btnOpenAMO.BackColor = [System.Drawing.Color]::FromArgb(70, 130, 180) +$btnOpenAMO.ForeColor = [System.Drawing.Color]::White +$btnOpenAMO.Font = New-Object System.Drawing.Font("Segoe UI", 10) +$btnOpenAMO.FlatStyle = "Flat" +$btnOpenAMO.FlatAppearance.BorderSize = 0 +$btnOpenAMO.Add_Click({ Open-AddonPage -Url "https://addons.mozilla.org/de/firefox/" }) +$tabPopular.Controls.Add($btnOpenAMO) + +# =========================================================== +# TAB 3: XPI direkt installieren +# =========================================================== +$tabXpi = New-Object System.Windows.Forms.TabPage +$tabXpi.Text = "XPI-Datei installieren" +$tabs.Controls.Add($tabXpi) + +$lblXpiInfo = New-Object System.Windows.Forms.Label +$lblXpiInfo.Text = "Installiere ein Add-on direkt aus einer lokalen .xpi-Datei in ein Firefox-Profil." +$lblXpiInfo.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$lblXpiInfo.ForeColor = [System.Drawing.Color]::Gray +$lblXpiInfo.Location = New-Object System.Drawing.Point(10, 10) +$lblXpiInfo.AutoSize = $true +$tabXpi.Controls.Add($lblXpiInfo) + +$lblXpiPath = New-Object System.Windows.Forms.Label +$lblXpiPath.Text = "XPI-Datei:"; $lblXpiPath.Location = New-Object System.Drawing.Point(10, 50) +$lblXpiPath.AutoSize = $true; $tabXpi.Controls.Add($lblXpiPath) + +$txtXpiPath = New-Object System.Windows.Forms.TextBox +$txtXpiPath.Location = New-Object System.Drawing.Point(10, 70) +$txtXpiPath.Size = New-Object System.Drawing.Size(650, 25) +$txtXpiPath.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$tabXpi.Controls.Add($txtXpiPath) + +$btnBrowseXpi = New-Object System.Windows.Forms.Button +$btnBrowseXpi.Text = "Durchsuchen..."; $btnBrowseXpi.Location = New-Object System.Drawing.Point(670, 68) +$btnBrowseXpi.Size = New-Object System.Drawing.Size(160, 28) +$btnBrowseXpi.Add_Click({ + $ofd = New-Object System.Windows.Forms.OpenFileDialog + $ofd.Filter = "Firefox Add-on (*.xpi)|*.xpi" + if ($ofd.ShowDialog() -eq "OK") { $txtXpiPath.Text = $ofd.FileName } +}) +$tabXpi.Controls.Add($btnBrowseXpi) + +$lblProfile = New-Object System.Windows.Forms.Label +$lblProfile.Text = "Firefox-Profil:"; $lblProfile.Location = New-Object System.Drawing.Point(10, 110) +$lblProfile.AutoSize = $true; $tabXpi.Controls.Add($lblProfile) + +$txtProfile = New-Object System.Windows.Forms.TextBox +$txtProfile.Location = New-Object System.Drawing.Point(10, 130) +$txtProfile.Size = New-Object System.Drawing.Size(650, 25) +$txtProfile.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$dp = Find-FirefoxProfile +if ($dp) { $txtProfile.Text = $dp } +$tabXpi.Controls.Add($txtProfile) + +$btnBrowseProfile = New-Object System.Windows.Forms.Button +$btnBrowseProfile.Text = "Profil waehlen..."; $btnBrowseProfile.Location = New-Object System.Drawing.Point(670, 128) +$btnBrowseProfile.Size = New-Object System.Drawing.Size(160, 28) +$btnBrowseProfile.Add_Click({ + $fbd = New-Object System.Windows.Forms.FolderBrowserDialog + if ($fbd.ShowDialog() -eq "OK") { $txtProfile.Text = $fbd.SelectedPath } +}) +$tabXpi.Controls.Add($btnBrowseProfile) + +$btnInstallXpi = New-Object System.Windows.Forms.Button +$btnInstallXpi.Text = "Add-on installieren" +$btnInstallXpi.Size = New-Object System.Drawing.Size(200, 40) +$btnInstallXpi.Location = New-Object System.Drawing.Point(10, 180) +$btnInstallXpi.BackColor = [System.Drawing.Color]::FromArgb(255, 100, 20) +$btnInstallXpi.ForeColor = [System.Drawing.Color]::White +$btnInstallXpi.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) +$btnInstallXpi.FlatStyle = "Flat"; $btnInstallXpi.FlatAppearance.BorderSize = 0 +$btnInstallXpi.Add_Click({ + if (-not (Test-Path $txtXpiPath.Text)) { + [System.Windows.Forms.MessageBox]::Show("XPI-Datei nicht gefunden.", "Fehler", "OK", "Error"); return + } + if (-not (Test-Path $txtProfile.Text)) { + [System.Windows.Forms.MessageBox]::Show("Profilordner nicht gefunden.", "Fehler", "OK", "Error"); return + } + $ff = Get-Process -Name "firefox" -ErrorAction SilentlyContinue + if ($ff) { + $ans = [System.Windows.Forms.MessageBox]::Show( + "Firefox ist geoeffnet. Jetzt beenden?", "Firefox beenden", "YesNo", "Warning") + if ($ans -eq "Yes") { $ff | Stop-Process -Force; Start-Sleep 2 } else { return } + } + try { + $id = Install-AddonFromFile -ProfilePath $txtProfile.Text -XpiPath $txtXpiPath.Text + [System.Windows.Forms.MessageBox]::Show( + "Installiert! ID: $id`nFirefox neu starten.", "Erfolg", "OK", "Information") + $txtXpiPath.Text = "" + } catch { + [System.Windows.Forms.MessageBox]::Show("Fehler: $_", "Fehler", "OK", "Error") + } +}) +$tabXpi.Controls.Add($btnInstallXpi) + +# =========================================================== +# TAB 4: Installierte Add-ons +# =========================================================== +$tabInstalled = New-Object System.Windows.Forms.TabPage +$tabInstalled.Text = "Installierte Add-ons" +$tabs.Controls.Add($tabInstalled) + +$lvInstalled = New-Object System.Windows.Forms.ListView +$lvInstalled.Location = New-Object System.Drawing.Point(10, 10) +$lvInstalled.Size = New-Object System.Drawing.Size(845, 420) +$lvInstalled.View = [System.Windows.Forms.View]::Details +$lvInstalled.FullRowSelect = $true; $lvInstalled.GridLines = $true +$lvInstalled.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$lvInstalled.Columns.Add("Name", 220) | Out-Null +$lvInstalled.Columns.Add("Version", 80) | Out-Null +$lvInstalled.Columns.Add("Aktiv", 60) | Out-Null +$lvInstalled.Columns.Add("ID", 460) | Out-Null +$tabInstalled.Controls.Add($lvInstalled) + +$btnRefresh = New-Object System.Windows.Forms.Button +$btnRefresh.Text = "Aktualisieren"; $btnRefresh.Size = New-Object System.Drawing.Size(160, 35) +$btnRefresh.Location = New-Object System.Drawing.Point(10, 445) +$btnRefresh.BackColor = [System.Drawing.Color]::FromArgb(70, 130, 180) +$btnRefresh.ForeColor = [System.Drawing.Color]::White +$btnRefresh.Font = New-Object System.Drawing.Font("Segoe UI", 10) +$btnRefresh.FlatStyle = "Flat"; $btnRefresh.FlatAppearance.BorderSize = 0 +$btnRefresh.Add_Click({ + $lvInstalled.Items.Clear() + $profilePath = Find-FirefoxProfile + if (-not $profilePath) { + [System.Windows.Forms.MessageBox]::Show("Kein Firefox-Profil gefunden.", "Hinweis", "OK", "Information") + return + } + $addons = Get-InstalledAddons -ProfilePath $profilePath + foreach ($a in $addons) { + $name = if ($a.defaultLocale.name) { $a.defaultLocale.name } else { $a.id } + $item = New-Object System.Windows.Forms.ListViewItem($name) + $item.SubItems.Add($a.version) | Out-Null + $item.SubItems.Add($(if ($a.active) { "Ja" } else { "Nein" })) | Out-Null + $item.SubItems.Add($a.id) | Out-Null + if (-not $a.active) { $item.ForeColor = [System.Drawing.Color]::Gray } + $lvInstalled.Items.Add($item) | Out-Null + } + if ($lvInstalled.Items.Count -eq 0) { + $lvInstalled.Items.Add( + (New-Object System.Windows.Forms.ListViewItem("Keine Add-ons gefunden."))) | Out-Null + } +}) +$tabInstalled.Controls.Add($btnRefresh) + +# =========================================================== +# TAB 5: Diagnose & Intune +# =========================================================== +$tabDiag = New-Object System.Windows.Forms.TabPage +$tabDiag.Text = "Diagnose / Intune" +$tabs.Controls.Add($tabDiag) + +# Intune-Warnung +$pnlIntune = New-Object System.Windows.Forms.Panel +$pnlIntune.Location = New-Object System.Drawing.Point(10, 8) +$pnlIntune.Size = New-Object System.Drawing.Size(845, 28) +$pnlIntune.BorderStyle = "FixedSingle" +$tabDiag.Controls.Add($pnlIntune) +$lblIntuneStatus = New-Object System.Windows.Forms.Label +$lblIntuneStatus.Font = New-Object System.Drawing.Font("Segoe UI", 8, [System.Drawing.FontStyle]::Bold) +$lblIntuneStatus.Location = New-Object System.Drawing.Point(6, 6) +$lblIntuneStatus.Size = New-Object System.Drawing.Size(830, 18) +if (Test-IntuneManaged) { + $pnlIntune.BackColor = [System.Drawing.Color]::FromArgb(255, 240, 200) + $lblIntuneStatus.Text = "INTUNE erkannt - Firefox wird via Intune verwaltet. policies.json wird ignoriert. Registry-Methode oder .reg-Export verwenden." + $lblIntuneStatus.ForeColor = [System.Drawing.Color]::DarkOrange +} else { + $pnlIntune.BackColor = [System.Drawing.Color]::FromArgb(220, 255, 220) + $lblIntuneStatus.Text = "Kein Intune-Management erkannt. policies.json und Registry-Methode sollten funktionieren." + $lblIntuneStatus.ForeColor = [System.Drawing.Color]::DarkGreen +} +$pnlIntune.Controls.Add($lblIntuneStatus) + +# Policy-Konflikt-Checker +$grpCheck = New-Object System.Windows.Forms.GroupBox +$grpCheck.Text = "Policy-Konflikt-Pruefung - moegliche Gruende warum Add-ons nicht installiert werden" +$grpCheck.Location = New-Object System.Drawing.Point(10, 44) +$grpCheck.Size = New-Object System.Drawing.Size(845, 220) +$grpCheck.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$tabDiag.Controls.Add($grpCheck) + +$lvChecks = New-Object System.Windows.Forms.ListView +$lvChecks.Location = New-Object System.Drawing.Point(10, 18) +$lvChecks.Size = New-Object System.Drawing.Size(822, 192) +$lvChecks.View = [System.Windows.Forms.View]::Details +$lvChecks.FullRowSelect = $true +$lvChecks.GridLines = $true +$lvChecks.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$lvChecks.Columns.Add("Status", 55) | Out-Null +$lvChecks.Columns.Add("Policy / Einstellung", 240) | Out-Null +$lvChecks.Columns.Add("Aktueller Wert", 200) | Out-Null +$lvChecks.Columns.Add("Auswirkung / Loesung", 300) | Out-Null +$grpCheck.Controls.Add($lvChecks) + +function Invoke-PolicyCheck { + $lvChecks.Items.Clear() + $base = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox" + + # Hilfsfunktion: Eintrag hinzufuegen + function Add-CheckItem { + param([string]$Status, [string]$Policy, [string]$Value, [string]$Info, [string]$Color) + $item = New-Object System.Windows.Forms.ListViewItem($Status) + $item.SubItems.Add($Policy) | Out-Null + $item.SubItems.Add($Value) | Out-Null + $item.SubItems.Add($Info) | Out-Null + switch ($Color) { + "red" { $item.BackColor = [System.Drawing.Color]::FromArgb(255, 210, 210) } + "yellow" { $item.BackColor = [System.Drawing.Color]::FromArgb(255, 245, 200) } + "green" { $item.BackColor = [System.Drawing.Color]::FromArgb(210, 255, 210) } + } + $lvChecks.Items.Add($item) | Out-Null + } + + # 1. BlockAboutAddons - sperrt about:addons komplett + $val = (Get-ItemProperty "$base" -Name "BlockAboutAddons" -ErrorAction SilentlyContinue).BlockAboutAddons + if ($val -eq 1) { + Add-CheckItem "FEHLER" "BlockAboutAddons" "1 (aktiv)" "Sperrt Add-on-Manager komplett. Auf 0 setzen oder entfernen." "red" + } else { + Add-CheckItem "OK" "BlockAboutAddons" "(nicht gesetzt)" "Kein Problem." "green" + } + + # 2. InstallAddonsPermission - steuert wer Add-ons installieren darf + $allowKey = "$base\InstallAddonsPermission\Allow" + $blockKey = "$base\InstallAddonsPermission\Block" + $hasAllow = Test-Path $allowKey + $hasBlock = Test-Path $blockKey + if ($hasBlock) { + $blockVals = (Get-ItemProperty $blockKey -ErrorAction SilentlyContinue).PSObject.Properties | + Where-Object { $_.Name -notmatch '^PS' } | ForEach-Object { $_.Value } + if ($blockVals -contains "" -or $blockVals -contains "*") { + Add-CheckItem "FEHLER" "InstallAddonsPermission\Block" "" "Alle Installationen geblockt! Eintrag entfernen." "red" + } else { + Add-CheckItem "WARN" "InstallAddonsPermission\Block" ($blockVals -join ", ") "Bestimmte Quellen geblockt. Pruefen ob AMO/interne URL betroffen." "yellow" + } + } else { + Add-CheckItem "OK" "InstallAddonsPermission" "(keine Block-Regel)" "Kein Problem." "green" + } + + # 3. ExtensionSettings - ist force_installed korrekt gesetzt? + $extKey = "$base\ExtensionSettings" + if (Test-Path $extKey) { + $subkeys = Get-ChildItem $extKey -ErrorAction SilentlyContinue + if ($subkeys) { + foreach ($sk in $subkeys) { + $mode = (Get-ItemProperty $sk.PSPath -Name "installation_mode" -ErrorAction SilentlyContinue).installation_mode + $url = (Get-ItemProperty $sk.PSPath -Name "install_url" -ErrorAction SilentlyContinue).install_url + if ($mode -eq "force_installed") { + if ($url) { + Add-CheckItem "OK" ("ExtensionSettings\" + $sk.PSChildName) "force_installed" "install_url: $url" "green" + } else { + Add-CheckItem "WARN" ("ExtensionSettings\" + $sk.PSChildName) "force_installed" "Keine install_url - Firefox muss AMO erreichen koennen." "yellow" + } + } elseif ($mode -eq "blocked") { + Add-CheckItem "FEHLER" ("ExtensionSettings\" + $sk.PSChildName) "blocked" "Dieses Add-on ist explizit geblockt!" "red" + } elseif ($mode) { + Add-CheckItem "WARN" ("ExtensionSettings\" + $sk.PSChildName) $mode "Modus ist nicht force_installed." "yellow" + } + } + # Wildcard-Eintrag pruefen (blockiert alle nicht explizit erlaubten) + $wildcard = Get-ItemProperty (Join-Path $extKey "*") -ErrorAction SilentlyContinue + if ($wildcard) { + $wMode = $wildcard.installation_mode + if ($wMode -eq "blocked") { + Add-CheckItem "FEHLER" 'ExtensionSettings\* (Wildcard)' "blocked" "Alle Add-ons ausser explizit force_installed sind geblockt. Kann force_installed verhindern wenn Reihenfolge falsch." "red" + } elseif ($wMode) { + Add-CheckItem "WARN" 'ExtensionSettings\* (Wildcard)' $wMode "Wildcard-Regel aktiv - pruefen ob sie force_installed ueberschreibt." "yellow" + } + } + } else { + Add-CheckItem "WARN" "ExtensionSettings" "(leer)" "Kein Add-on als force_installed eingetragen." "yellow" + } + } else { + Add-CheckItem "WARN" "ExtensionSettings" "(nicht vorhanden)" "Noch keine Extension-Policy gesetzt." "yellow" + } + + # 4. DisabledCiphers / WebsiteFilter die AMO blockieren koennten + $proxyKey = Get-ItemProperty "$base\Proxy" -ErrorAction SilentlyContinue + if ($proxyKey) { + Add-CheckItem "INFO" "Proxy-Policy aktiv" ($proxyKey | Out-String).Trim().Split("`n")[0] "Proxy kann AMO-Download blockieren - interne URL empfohlen." "yellow" + } + + # 5. AppAutoUpdate - verhindert nicht Installation, aber Info + $upd = (Get-ItemProperty "$base" -Name "DisableAppUpdate" -ErrorAction SilentlyContinue).DisableAppUpdate + if ($upd -eq 1) { + Add-CheckItem "INFO" "DisableAppUpdate" "1 (aktiv)" "Kein Problem fuer Add-ons, aber Firefox-Updates deaktiviert." "yellow" + } + + # 6. ExtensionUpdate deaktiviert? + $extUpd = (Get-ItemProperty "$base" -Name "ExtensionUpdate" -ErrorAction SilentlyContinue).ExtensionUpdate + if ($extUpd -eq 0) { + Add-CheckItem "WARN" "ExtensionUpdate" "0 (deaktiviert)" "Add-on-Updates deaktiviert. Erstinstallation via force_installed funktioniert trotzdem." "yellow" + } + + if ($lvChecks.Items.Count -eq 0) { + Add-CheckItem "INFO" "(keine Policies gefunden)" "" "HKLM\SOFTWARE\Policies\Mozilla\Firefox existiert nicht." "yellow" + } +} + +$btnRunCheck = New-Object System.Windows.Forms.Button +$btnRunCheck.Text = "Policy-Check ausfuehren" +$btnRunCheck.Size = New-Object System.Drawing.Size(200, 30) +$btnRunCheck.Location = New-Object System.Drawing.Point(10, 272) +$btnRunCheck.BackColor = [System.Drawing.Color]::FromArgb(180, 0, 0) +$btnRunCheck.ForeColor = [System.Drawing.Color]::White +$btnRunCheck.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) +$btnRunCheck.FlatStyle = "Flat" +$btnRunCheck.FlatAppearance.BorderSize = 0 +$btnRunCheck.Add_Click({ Invoke-PolicyCheck }) +$tabDiag.Controls.Add($btnRunCheck) + +$btnFixSelected = New-Object System.Windows.Forms.Button +$btnFixSelected.Text = "Markierten FEHLER beheben" +$btnFixSelected.Size = New-Object System.Drawing.Size(210, 30) +$btnFixSelected.Location = New-Object System.Drawing.Point(220, 272) +$btnFixSelected.BackColor = [System.Drawing.Color]::FromArgb(140, 0, 0) +$btnFixSelected.ForeColor = [System.Drawing.Color]::White +$btnFixSelected.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) +$btnFixSelected.FlatStyle = "Flat" +$btnFixSelected.FlatAppearance.BorderSize = 0 +$btnFixSelected.Add_Click({ + if ($lvChecks.SelectedItems.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show( + "Bitte zuerst einen FEHLER-Eintrag in der Liste auswaehlen.", "Hinweis", "OK", "Information") + return + } + if (-not (Test-IsAdmin)) { + [System.Windows.Forms.MessageBox]::Show( + "Administratorrechte erforderlich.", "Kein Admin", "OK", "Warning"); return + } + $sel = $lvChecks.SelectedItems[0] + $status = $sel.SubItems[0].Text + $policy = $sel.SubItems[1].Text + $curVal = $sel.SubItems[2].Text + + $extBase = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings" + + # Fall 1: Explizit geblocktes Add-on (z.B. uBlock0@raymondhill.net -> blocked) + if ($status -eq "FEHLER" -and $policy -match "^ExtensionSettings\\(.+)$" -and + $Matches[1] -ne "*" -and $curVal -eq "blocked") { + $addonId = $Matches[1] + $addonKey = Join-Path $extBase $addonId + $ans = [System.Windows.Forms.MessageBox]::Show( + "Add-on '$addonId' ist explizit geblockt.`n`n" + + "Was soll gemacht werden?`n" + + "[Ja] Auf 'force_installed' aendern (Add-on wird zwingend installiert)`n" + + "[Nein] Block-Eintrag komplett loeschen (Add-on wird nicht mehr explizit geblockt)", + "Blocked-Eintrag beheben", "YesNo", "Question") + if ($ans -eq "Yes") { + if (-not (Test-Path $addonKey)) { New-Item -Path $addonKey -Force | Out-Null } + Set-ItemProperty -Path $addonKey -Name "installation_mode" -Value "force_installed" -Type String + # install_url setzen falls bekannt + $known = $AllAddons | Where-Object { $_.Id -eq $addonId } + if ($known -and $known.Url) { + Set-ItemProperty -Path $addonKey -Name "install_url" -Value $known.Url -Type String + [System.Windows.Forms.MessageBox]::Show( + "Geaendert: '$addonId'`nModus: force_installed`nURL: $($known.Url)`n`nFirefox neu starten.", + "Behoben", "OK", "Information") + } else { + [System.Windows.Forms.MessageBox]::Show( + "Geaendert: '$addonId' -> force_installed`nHinweis: install_url nicht bekannt - bitte manuell eintragen oder AMO muss erreichbar sein.`n`nFirefox neu starten.", + "Behoben", "OK", "Information") + } + } else { + if (Test-Path $addonKey) { + Remove-Item -Path $addonKey -Recurse -Force + [System.Windows.Forms.MessageBox]::Show( + "Block-Eintrag fuer '$addonId' geloescht.`n`nFirefox neu starten.", + "Geloescht", "OK", "Information") + } + } + } + # Fall 2: Wildcard geblockt + elseif ($status -eq "FEHLER" -and $policy -match "\*") { + $wildcardKey = Join-Path $extBase "*" + $ans = [System.Windows.Forms.MessageBox]::Show( + "Wildcard (*) ist auf 'blocked' gesetzt.`n`n" + + "[Ja] Wildcard-Eintrag loeschen (empfohlen wenn Intune force_installed nicht respektiert)`n" + + "[Nein] Wildcard auf 'allowed' aendern (alle Add-ons erlaubt, weniger sicher)", + "Wildcard beheben", "YesNo", "Question") + if ($ans -eq "Yes") { + if (Test-Path $wildcardKey) { Remove-Item -Path $wildcardKey -Recurse -Force } + [System.Windows.Forms.MessageBox]::Show( + "Wildcard-Eintrag geloescht.`nFirefox neu starten.", + "Geloescht", "OK", "Information") + } else { + if (-not (Test-Path $wildcardKey)) { New-Item -Path $wildcardKey -Force | Out-Null } + Set-ItemProperty -Path $wildcardKey -Name "installation_mode" -Value "allowed" -Type String + [System.Windows.Forms.MessageBox]::Show( + "Wildcard auf 'allowed' gesetzt.`nFirefox neu starten.", + "Geaendert", "OK", "Information") + } + } + else { + [System.Windows.Forms.MessageBox]::Show( + "Fuer diesen Eintrag ist keine automatische Korrektur verfuegbar.`nBitte manuell pruefen.", + "Hinweis", "OK", "Information") + } + Invoke-PolicyCheck +}) +$tabDiag.Controls.Add($btnFixSelected) + +$btnFixWildcard = New-Object System.Windows.Forms.Button +$btnFixWildcard.Text = "Alle FEHLERs beheben" +$btnFixWildcard.Size = New-Object System.Drawing.Size(185, 30) +$btnFixWildcard.Location = New-Object System.Drawing.Point(440, 272) +$btnFixWildcard.BackColor = [System.Drawing.Color]::FromArgb(180, 80, 0) +$btnFixWildcard.ForeColor = [System.Drawing.Color]::White +$btnFixWildcard.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) +$btnFixWildcard.FlatStyle = "Flat" +$btnFixWildcard.FlatAppearance.BorderSize = 0 +$btnFixWildcard.Add_Click({ + if (-not (Test-IsAdmin)) { + [System.Windows.Forms.MessageBox]::Show( + "Administratorrechte erforderlich.", "Kein Admin", "OK", "Warning"); return + } + $errors = $lvChecks.Items | Where-Object { $_.SubItems[0].Text -eq "FEHLER" } + if ($errors.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show("Keine FEHLER-Eintraege gefunden.", "Hinweis", "OK", "Information") + return + } + $report = @() + $extBase = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\ExtensionSettings" + foreach ($err in $errors) { + $policy = $err.SubItems[1].Text + # Explizit geblocktes Add-on -> auf force_installed setzen + if ($policy -match "^ExtensionSettings\\(.+)$" -and $Matches[1] -ne "*" -and + $err.SubItems[2].Text -eq "blocked") { + $addonId = $Matches[1] + $addonKey = Join-Path $extBase $addonId + if (-not (Test-Path $addonKey)) { New-Item -Path $addonKey -Force | Out-Null } + Set-ItemProperty -Path $addonKey -Name "installation_mode" -Value "force_installed" -Type String + $known = $AllAddons | Where-Object { $_.Id -eq $addonId } + if ($known -and $known.Url) { + Set-ItemProperty -Path $addonKey -Name "install_url" -Value $known.Url -Type String + } + $report += "force_installed gesetzt: $addonId" + } + # Wildcard -> loeschen + elseif ($policy -match "\*") { + $wildcardKey = Join-Path $extBase "*" + if (Test-Path $wildcardKey) { Remove-Item -Path $wildcardKey -Recurse -Force } + $report += "Wildcard (*) entfernt" + } + } + [System.Windows.Forms.MessageBox]::Show( + "Automatische Korrektur abgeschlossen:`n`n" + ($report -join "`n") + + "`n`nFirefox neu starten.", + "Alle Fehler behoben", "OK", "Information") + Invoke-PolicyCheck +}) +$tabDiag.Controls.Add($btnFixWildcard) + +# Aktive Policies (Rohansicht) +$grpPolicies = New-Object System.Windows.Forms.GroupBox +$grpPolicies.Text = "Registry-Rohansicht (HKLM\SOFTWARE\Policies\Mozilla\Firefox)" +$grpPolicies.Location = New-Object System.Drawing.Point(10, 310) +$grpPolicies.Size = New-Object System.Drawing.Size(845, 120) +$grpPolicies.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$tabDiag.Controls.Add($grpPolicies) + +$txtPolicies = New-Object System.Windows.Forms.TextBox +$txtPolicies.Multiline = $true +$txtPolicies.ScrollBars = "Both" +$txtPolicies.ReadOnly = $true +$txtPolicies.Location = New-Object System.Drawing.Point(10, 18) +$txtPolicies.Size = New-Object System.Drawing.Size(822, 92) +$txtPolicies.Font = New-Object System.Drawing.Font("Consolas", 8) +$txtPolicies.WordWrap = $false +$grpPolicies.Controls.Add($txtPolicies) + +# Buttons +$btnOpenAboutPolicies = New-Object System.Windows.Forms.Button +$btnOpenAboutPolicies.Text = "about:policies oeffnen" +$btnOpenAboutPolicies.Size = New-Object System.Drawing.Size(190, 32) +$btnOpenAboutPolicies.Location = New-Object System.Drawing.Point(10, 438) +$btnOpenAboutPolicies.BackColor = [System.Drawing.Color]::FromArgb(255, 100, 20) +$btnOpenAboutPolicies.ForeColor = [System.Drawing.Color]::White +$btnOpenAboutPolicies.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) +$btnOpenAboutPolicies.FlatStyle = "Flat"; $btnOpenAboutPolicies.FlatAppearance.BorderSize = 0 +$btnOpenAboutPolicies.Add_Click({ + try { Start-Process "firefox" "about:policies" } + catch { [System.Windows.Forms.MessageBox]::Show("Fehler: $_", "Fehler", "OK", "Error") } +}) +$tabDiag.Controls.Add($btnOpenAboutPolicies) + +$btnRefreshPolicies = New-Object System.Windows.Forms.Button +$btnRefreshPolicies.Text = "Rohansicht laden" +$btnRefreshPolicies.Size = New-Object System.Drawing.Size(160, 32) +$btnRefreshPolicies.Location = New-Object System.Drawing.Point(210, 438) +$btnRefreshPolicies.BackColor = [System.Drawing.Color]::FromArgb(70, 130, 180) +$btnRefreshPolicies.ForeColor = [System.Drawing.Color]::White +$btnRefreshPolicies.Font = New-Object System.Drawing.Font("Segoe UI", 9) +$btnRefreshPolicies.FlatStyle = "Flat"; $btnRefreshPolicies.FlatAppearance.BorderSize = 0 +$btnRefreshPolicies.Add_Click({ + $lines = Get-ActiveFirefoxPolicies + $txtPolicies.Text = if ($lines.Count -eq 0) { + "(Keine Registry-Policies gefunden)" + } else { $lines -join "`r`n" } +}) +$tabDiag.Controls.Add($btnRefreshPolicies) + +$btnExportReg = New-Object System.Windows.Forms.Button +$btnExportReg.Text = ".reg fuer Intune exportieren" +$btnExportReg.Size = New-Object System.Drawing.Size(210, 32) +$btnExportReg.Location = New-Object System.Drawing.Point(380, 438) +$btnExportReg.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 80) +$btnExportReg.ForeColor = [System.Drawing.Color]::White +$btnExportReg.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) +$btnExportReg.FlatStyle = "Flat"; $btnExportReg.FlatAppearance.BorderSize = 0 +$btnExportReg.Add_Click({ + $selected = $lvDeploy.Items | Where-Object { $_.Checked } + if ($selected.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show( + "Bitte zuerst im Tab 'Maschinenweite Bereitstellung' Add-ons auswaehlen.", + "Hinweis", "OK", "Information"); return + } + $sfd = New-Object System.Windows.Forms.SaveFileDialog + $sfd.Filter = "Registry-Datei (*.reg)|*.reg" + $sfd.FileName = "Firefox-Addons-Intune.reg" + if ($sfd.ShowDialog() -ne "OK") { return } + $addonsToExport = $selected | ForEach-Object { + $clone = [PSCustomObject]@{ Name=$_.Tag.Name; Id=$_.Tag.Id; Url=$_.Tag.Url; Beschreibung=$_.Tag.Beschreibung } + $internalUrl = $_.SubItems[2].Text.Trim() + if ($internalUrl -ne "" -and $internalUrl.StartsWith("http")) { $clone.Url = $internalUrl } + $clone + } + Export-RegFile -OutputPath $sfd.FileName -Addons $addonsToExport + [System.Windows.Forms.MessageBox]::Show( + "Exportiert: $($sfd.FileName)`n`nIntune OMA-URI:`n./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~mozilla_firefox~Extensions/ExtensionSettings`n`nAlternativ: .reg via Intune PowerShell-Skript importieren:`n reg import `"Firefox-Addons-Intune.reg`"", + "Export erfolgreich", "OK", "Information") +}) +$tabDiag.Controls.Add($btnExportReg) + +$btnImportReg = New-Object System.Windows.Forms.Button +$btnImportReg.Text = "REG-Datei einlesen & anwenden" +$btnImportReg.Size = New-Object System.Drawing.Size(240, 32) +$btnImportReg.Location = New-Object System.Drawing.Point(600, 438) +$btnImportReg.BackColor = [System.Drawing.Color]::FromArgb(60, 90, 160) +$btnImportReg.ForeColor = [System.Drawing.Color]::White +$btnImportReg.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) +$btnImportReg.FlatStyle = "Flat" +$btnImportReg.FlatAppearance.BorderSize = 0 +$btnImportReg.Add_Click({ + if (-not (Test-IsAdmin)) { + [System.Windows.Forms.MessageBox]::Show( + "Administratorrechte erforderlich.", "Kein Admin", "OK", "Warning"); return + } + + $ofd = New-Object System.Windows.Forms.OpenFileDialog + $ofd.Filter = "Registry-Datei (*.reg)|*.reg" + $ofd.Title = "REG-Datei fuer Intune-Policy auswaehlen" + if ($ofd.ShowDialog() -ne "OK") { return } + + # REG-Datei parsen + $lines = Get-Content $ofd.FileName -Encoding Unicode -ErrorAction SilentlyContinue + if (-not $lines) { + $lines = Get-Content $ofd.FileName -Encoding UTF8 -ErrorAction SilentlyContinue + } + if (-not $lines) { + [System.Windows.Forms.MessageBox]::Show("Datei konnte nicht gelesen werden.", "Fehler", "OK", "Error") + return + } + + $currentKey = $null + $applied = @() + $skipped = @() + $errors = @() + + foreach ($line in $lines) { + $line = $line.Trim() + if ($line -eq "" -or $line.StartsWith(";")) { continue } + if ($line.StartsWith("Windows Registry Editor")) { continue } + + # Schluessel-Zeile: [HKEY_LOCAL_MACHINE\...] + if ($line -match '^\[(.+)\]$') { + $rawKey = $Matches[1] + # Nur Firefox-Policy-Keys erlauben + if ($rawKey -match 'SOFTWARE\\Policies\\Mozilla\\Firefox') { + # HKEY_LOCAL_MACHINE -> HKLM: + $psKey = $rawKey ` + -replace '^HKEY_LOCAL_MACHINE', 'HKLM:' ` + -replace '^HKEY_CURRENT_USER', 'HKCU:' + $currentKey = $psKey + } else { + $currentKey = $null + $skipped += "Uebersprungen (nicht Firefox-Policy): $rawKey" + } + continue + } + + # Wert-Zeile nur verarbeiten wenn Key erlaubt + if (-not $currentKey) { continue } + + # Wert parsen: "Name"="Wert" oder "Name"=dword:00000001 + if ($line -match '^"(.+)"=(dword|hex|qword):(.+)$') { + $name = $Matches[1] + $type = $Matches[2] + $value = $Matches[3] + try { + if (-not (Test-Path $currentKey)) { New-Item -Path $currentKey -Force | Out-Null } + switch ($type) { + "dword" { + $int = [Convert]::ToInt32($value, 16) + Set-ItemProperty -Path $currentKey -Name $name -Value $int -Type DWord + } + default { + Set-ItemProperty -Path $currentKey -Name $name -Value $value -Type String + } + } + $applied += "$currentKey -> $name = [$type] $value" + } catch { + $errors += "Fehler bei $currentKey -> $name : $_" + } + } elseif ($line -match '^"(.+)"="(.*)"$') { + $name = $Matches[1] + $value = $Matches[2] -replace '\\"', '"' + try { + if (-not (Test-Path $currentKey)) { New-Item -Path $currentKey -Force | Out-Null } + Set-ItemProperty -Path $currentKey -Name $name -Value $value -Type String + $applied += "$currentKey -> $name = $value" + } catch { + $errors += "Fehler bei $currentKey -> $name : $_" + } + } elseif ($line -match '^@="(.*)"$') { + # Standard-Wert + $value = $Matches[1] + try { + if (-not (Test-Path $currentKey)) { New-Item -Path $currentKey -Force | Out-Null } + Set-ItemProperty -Path $currentKey -Name "(Default)" -Value $value -Type String + $applied += "$currentKey -> (Default) = $value" + } catch { + $errors += "Fehler bei $currentKey -> Default : $_" + } + } elseif ($line -match '^"-(.+)"$') { + # Wert loeschen + $name = $Matches[1] + try { + Remove-ItemProperty -Path $currentKey -Name $name -ErrorAction SilentlyContinue + $applied += "Geloescht: $currentKey -> $name" + } catch { + $errors += "Loeschen fehlgeschlagen: $currentKey -> $name" + } + } elseif ($line -match '^\[-(.+)\]$') { + # Schluessel loeschen + $delKey = $Matches[1] -replace '^HKEY_LOCAL_MACHINE', 'HKLM:' + if (Test-Path $delKey) { + Remove-Item -Path $delKey -Recurse -Force -ErrorAction SilentlyContinue + $applied += "Schluessel geloescht: $delKey" + } + } + } + + # Ergebnisbericht + $dlg = New-Object System.Windows.Forms.Form + $dlg.Text = "Import-Ergebnis: " + [System.IO.Path]::GetFileName($ofd.FileName) + $dlg.Size = New-Object System.Drawing.Size(750, 500) + $dlg.StartPosition = "CenterParent" + + $tbResult = New-Object System.Windows.Forms.TextBox + $tbResult.Multiline = $true; $tbResult.ScrollBars = "Both" + $tbResult.ReadOnly = $true; $tbResult.Dock = "Fill" + $tbResult.Font = New-Object System.Drawing.Font("Consolas", 8) + $tbResult.WordWrap = $false + + $summary = "" + if ($applied.Count -gt 0) { + $summary += "=== ANGEWENDET ($($applied.Count)) ===`r`n" + $summary += ($applied -join "`r`n") + "`r`n`r`n" + } + if ($skipped.Count -gt 0) { + $summary += "=== UEBERSPRUNGEN ($($skipped.Count)) ===`r`n" + $summary += ($skipped -join "`r`n") + "`r`n`r`n" + } + if ($errors.Count -gt 0) { + $summary += "=== FEHLER ($($errors.Count)) ===`r`n" + $summary += ($errors -join "`r`n") + "`r`n`r`n" + } + $summary += "`r`nFirefox neu starten damit die Aenderungen wirksam werden." + $tbResult.Text = $summary + + $dlg.Controls.Add($tbResult) + $dlg.ShowDialog() | Out-Null + + Invoke-PolicyCheck + $btnRefreshPolicies.PerformClick() +}) +$tabDiag.Controls.Add($btnImportReg) + +$lblOmaUri = New-Object System.Windows.Forms.Label +$lblOmaUri.Text = "OMA-URI: ./Device/Vendor/MSFT/Policy/Config/Firefox~Policy~mozilla_firefox~Extensions/ExtensionSettings" +$lblOmaUri.Font = New-Object System.Drawing.Font("Consolas", 7) +$lblOmaUri.ForeColor = [System.Drawing.Color]::DarkBlue +$lblOmaUri.Location = New-Object System.Drawing.Point(10, 478) +$lblOmaUri.Size = New-Object System.Drawing.Size(845, 14) +$tabDiag.Controls.Add($lblOmaUri) + +# Statusleiste +$statusBar = New-Object System.Windows.Forms.StatusStrip +$statusLabel = New-Object System.Windows.Forms.ToolStripStatusLabel +$statusLabel2 = New-Object System.Windows.Forms.ToolStripStatusLabel +$statusLabel3 = New-Object System.Windows.Forms.ToolStripStatusLabel +$ffInstallDir = Find-FirefoxInstallDir +if ($ffInstallDir) { + $statusLabel.Text = "Firefox: $ffInstallDir" +} else { + $statusLabel.Text = "Firefox-Installation nicht gefunden" + $statusLabel.ForeColor = [System.Drawing.Color]::Red +} +$statusLabel2.Text = " | " + $(if (Test-IsAdmin) { "Admin: Ja" } else { "Admin: Nein" }) +$statusLabel3.Text = " | " + $(if (Test-IntuneManaged) { "Intune: erkannt" } else { "Intune: nicht erkannt" }) +$statusBar.Items.Add($statusLabel) | Out-Null +$statusBar.Items.Add($statusLabel2) | Out-Null +$statusBar.Items.Add($statusLabel3) | Out-Null +$form.Controls.Add($statusBar) + +$tabs.add_SelectedIndexChanged({ + if ($tabs.SelectedIndex -eq 3) { $btnRefresh.PerformClick() } + if ($tabs.SelectedIndex -eq 4) { + Invoke-PolicyCheck + $btnRefreshPolicies.PerformClick() + } +}) + +[void]$form.ShowDialog()