From 66937b929829873acc4017a4978745a7b09ab051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Preu=C3=9F?= Date: Sat, 14 Oct 2023 08:31:12 +0200 Subject: [PATCH] Adding first Version --- Source/.gitattributes | 63 ++ Source/.gitignore | 363 ++++++++++++ Source/App.xaml | 8 + Source/App.xaml.cs | 50 ++ Source/AssemblyInfo.cs | 6 + Source/Core/Data/ConsumingCallback.cs | 8 + Source/Core/Data/FileContainer.cs | 104 ++++ Source/Core/Data/FileDefinition.cs | 12 + Source/Core/Data/InfoData.cs | 12 + Source/Core/Events/FileEvent.cs | 12 + Source/Core/Events/PropertiesEvent.cs | 12 + .../Fritzbox/Networking/Discover/Discover.cs | 105 ++++ .../Core/Fritzbox/Networking/HTTP/Request.cs | 35 ++ Source/Core/Fritzbox/Networking/Info/JASON.cs | 94 +++ Source/Core/Fritzbox/Networking/Info/JUIS.cs | 96 ++++ .../Fritzbox/Networking/Info/SystemStatus.cs | 50 ++ Source/Core/Interfaces/Changeable.cs | 8 + Source/Core/Interfaces/Consumable.cs | 9 + Source/Core/Interfaces/EditorInstance.cs | 14 + Source/Core/Parser/ExportFile.cs | 191 +++++++ Source/Core/Parser/PropertyList.cs | 41 ++ Source/Definitions/Files.json | 130 +++++ Source/FRED.csproj | 58 ++ Source/FRED.sln | 28 + Source/FodyWeavers.xml | 3 + Source/UI/Assets/Binary.xaml | 17 + Source/UI/Assets/Crypted.xaml | 25 + Source/UI/Assets/Files.xaml | 28 + Source/UI/Assets/FritzBox.xaml | 24 + Source/UI/Assets/Icons.xaml | 15 + Source/UI/Assets/Info.xaml | 25 + Source/UI/Assets/Logo.xaml | 540 ++++++++++++++++++ Source/UI/Assets/Properties.xaml | 27 + Source/UI/Assets/Settings.xaml | 11 + Source/UI/Assets/Unknown.xaml | 15 + Source/UI/Assets/XML.xaml | 13 + Source/UI/Content/File.xaml | 44 ++ Source/UI/Content/File.xaml.cs | 38 ++ Source/UI/Content/Info.xaml | 36 ++ Source/UI/Content/Info.xaml.cs | 147 +++++ Source/UI/Content/Properties.xaml | 36 ++ Source/UI/Content/Properties.xaml.cs | 39 ++ Source/UI/Controls/DynamicEditor.cs | 28 + Source/UI/Controls/HEXEditor.cs | 40 ++ Source/UI/Controls/Navigation.cs | 259 +++++++++ Source/UI/Controls/TextEditor.cs | 120 ++++ Source/UI/Converters/MultiplyConverter.cs | 23 + Source/UI/Converters/NavigationConverter.cs | 37 ++ Source/UI/Design/Button.xaml | 21 + Source/UI/Design/Editor.xaml | 33 ++ Source/UI/Design/Input.xaml | 16 + Source/UI/Design/List.xaml | 55 ++ Source/UI/Design/Miscellaneous.xaml | 12 + Source/UI/Design/Navigation.xaml | 140 +++++ Source/UI/Design/Scrollbar.xaml | 89 +++ Source/UI/Design/Tab.xaml | 35 ++ Source/UI/Design/UserControl.xaml | 14 + Source/UI/Design/Window.xaml | 7 + Source/UI/Editor.xaml | 102 ++++ Source/UI/Editor.xaml.cs | 235 ++++++++ Source/UI/Syntax/Config.xshd | 87 +++ Source/UI/TargetSeletor.xaml | 74 +++ Source/UI/TargetSeletor.xaml.cs | 137 +++++ 63 files changed, 4156 insertions(+) create mode 100644 Source/.gitattributes create mode 100644 Source/.gitignore create mode 100644 Source/App.xaml create mode 100644 Source/App.xaml.cs create mode 100644 Source/AssemblyInfo.cs create mode 100644 Source/Core/Data/ConsumingCallback.cs create mode 100644 Source/Core/Data/FileContainer.cs create mode 100644 Source/Core/Data/FileDefinition.cs create mode 100644 Source/Core/Data/InfoData.cs create mode 100644 Source/Core/Events/FileEvent.cs create mode 100644 Source/Core/Events/PropertiesEvent.cs create mode 100644 Source/Core/Fritzbox/Networking/Discover/Discover.cs create mode 100644 Source/Core/Fritzbox/Networking/HTTP/Request.cs create mode 100644 Source/Core/Fritzbox/Networking/Info/JASON.cs create mode 100644 Source/Core/Fritzbox/Networking/Info/JUIS.cs create mode 100644 Source/Core/Fritzbox/Networking/Info/SystemStatus.cs create mode 100644 Source/Core/Interfaces/Changeable.cs create mode 100644 Source/Core/Interfaces/Consumable.cs create mode 100644 Source/Core/Interfaces/EditorInstance.cs create mode 100644 Source/Core/Parser/ExportFile.cs create mode 100644 Source/Core/Parser/PropertyList.cs create mode 100644 Source/Definitions/Files.json create mode 100644 Source/FRED.csproj create mode 100644 Source/FRED.sln create mode 100644 Source/FodyWeavers.xml create mode 100644 Source/UI/Assets/Binary.xaml create mode 100644 Source/UI/Assets/Crypted.xaml create mode 100644 Source/UI/Assets/Files.xaml create mode 100644 Source/UI/Assets/FritzBox.xaml create mode 100644 Source/UI/Assets/Icons.xaml create mode 100644 Source/UI/Assets/Info.xaml create mode 100644 Source/UI/Assets/Logo.xaml create mode 100644 Source/UI/Assets/Properties.xaml create mode 100644 Source/UI/Assets/Settings.xaml create mode 100644 Source/UI/Assets/Unknown.xaml create mode 100644 Source/UI/Assets/XML.xaml create mode 100644 Source/UI/Content/File.xaml create mode 100644 Source/UI/Content/File.xaml.cs create mode 100644 Source/UI/Content/Info.xaml create mode 100644 Source/UI/Content/Info.xaml.cs create mode 100644 Source/UI/Content/Properties.xaml create mode 100644 Source/UI/Content/Properties.xaml.cs create mode 100644 Source/UI/Controls/DynamicEditor.cs create mode 100644 Source/UI/Controls/HEXEditor.cs create mode 100644 Source/UI/Controls/Navigation.cs create mode 100644 Source/UI/Controls/TextEditor.cs create mode 100644 Source/UI/Converters/MultiplyConverter.cs create mode 100644 Source/UI/Converters/NavigationConverter.cs create mode 100644 Source/UI/Design/Button.xaml create mode 100644 Source/UI/Design/Editor.xaml create mode 100644 Source/UI/Design/Input.xaml create mode 100644 Source/UI/Design/List.xaml create mode 100644 Source/UI/Design/Miscellaneous.xaml create mode 100644 Source/UI/Design/Navigation.xaml create mode 100644 Source/UI/Design/Scrollbar.xaml create mode 100644 Source/UI/Design/Tab.xaml create mode 100644 Source/UI/Design/UserControl.xaml create mode 100644 Source/UI/Design/Window.xaml create mode 100644 Source/UI/Editor.xaml create mode 100644 Source/UI/Editor.xaml.cs create mode 100644 Source/UI/Syntax/Config.xshd create mode 100644 Source/UI/TargetSeletor.xaml create mode 100644 Source/UI/TargetSeletor.xaml.cs diff --git a/Source/.gitattributes b/Source/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/Source/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/Source/.gitignore b/Source/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/Source/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/Source/App.xaml b/Source/App.xaml new file mode 100644 index 0000000..cef37dd --- /dev/null +++ b/Source/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/Source/App.xaml.cs b/Source/App.xaml.cs new file mode 100644 index 0000000..3ea3724 --- /dev/null +++ b/Source/App.xaml.cs @@ -0,0 +1,50 @@ +using FRED.Core.Fritzbox.Networking.Discover; +using FRED.Core.Parser; +using FRED.UI; +using System; +using System.Reflection; +using System.Windows; + +namespace FRED { + public partial class App : Application { + + public App() { + TargetSeletor selector = new TargetSeletor(); + Editor editor = new Editor(); + + selector.Closing += delegate(object? sender, System.ComponentModel.CancelEventArgs e) { + Environment.Exit(0); + }; + + editor.Closing += delegate(object? sender, System.ComponentModel.CancelEventArgs e) { + e.Cancel = true; + editor.Hide(); + + if(!(bool) typeof(Window).GetProperty("IsDisposed", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(selector)) { + selector.Show(); + } + }; + + // Start Discovering-Service to find the Fritz!Box + Discovery.AddObserver(selector); + Discovery.SearchForDevices(); + + selector.Show(delegate(String file) { + if(new Uri(file).IsFile) { + System.Diagnostics.Debug.Print("Selected File: " + file); + } else { + System.Diagnostics.Debug.Print("Hostname: " + file); + // Open Login/Auth + return; + } + + ExportFile parser = new ExportFile(file); + editor.SetConsumer(parser); + parser.SetObserver(editor); + + editor.Show(); + parser.Parse(); + }); + } + } +} diff --git a/Source/AssemblyInfo.cs b/Source/AssemblyInfo.cs new file mode 100644 index 0000000..061bf86 --- /dev/null +++ b/Source/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, + ResourceDictionaryLocation.SourceAssembly +)] diff --git a/Source/Core/Data/ConsumingCallback.cs b/Source/Core/Data/ConsumingCallback.cs new file mode 100644 index 0000000..5cebd49 --- /dev/null +++ b/Source/Core/Data/ConsumingCallback.cs @@ -0,0 +1,8 @@ +using System; + +namespace FRED.Core.Data { + public class ConsumingCallback { + public String? Name { get; set; } = null; + public Action? Callback { get; set; } = null; + } +} diff --git a/Source/Core/Data/FileContainer.cs b/Source/Core/Data/FileContainer.cs new file mode 100644 index 0000000..8077f36 --- /dev/null +++ b/Source/Core/Data/FileContainer.cs @@ -0,0 +1,104 @@ +using System; +using System.Linq; + +namespace FRED.Core.Data { + public class FileContainer { + private String? name = null; + private String? type = null; + private String? subtype = null; + private String? content = null; + private bool crypted = false; + private FileDefinition? definition = null; + + public FileContainer(String name, String type) { + this.name = name; + this.type = type; + } + + public void SetDefinition(ref FileDefinition definition) { + this.definition = definition; + } + + public new String? GetType() { + return this.type; + } + + public String GetSubType() { + if(this.subtype == null) { + return ""; + } + + return this.subtype; + } + + public bool HasSubType() { + return this.subtype != null; + } + + public void SetContent(String content) { + this.content = content; + this.UpdateSubType(); + } + + public void UpdateSubType() { + + if(this.content == null) { + return; + } + + String data = this.content; + + switch (this.type) { + case "B64": + data = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(data)); + this.subtype = "Properties"; + break; + } + + if(!string.IsNullOrEmpty(data) && data.TrimStart().StartsWith("<")) { + this.subtype = "XML"; + } else if(data.Any(ch => char.IsControl(ch) && ch != '\r' && ch != '\n')) { + this.subtype = "Binary"; + } else if(data.Trim().Equals("")) { + this.subtype = "Unknown"; + } + } + + public void SetCrypted() { + this.crypted = true; + } + + public void SetUnencrypted() { + this.crypted = false; + } + + public bool IsCrypted() { + return this.crypted; + } + + public String? GetDescription() { + return this.definition?.Description; + } + + public String? GetName() { + return this.name; + } + + public String? GetSource() { + if(this.content == null) { + return null; + } + + switch (this.type) { + case "CFG": + return this.content; + case "B64": + return System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(this.content)); + case "BIN": + return this.content; + } + + return ""; + } + } +} diff --git a/Source/Core/Data/FileDefinition.cs b/Source/Core/Data/FileDefinition.cs new file mode 100644 index 0000000..03fffdd --- /dev/null +++ b/Source/Core/Data/FileDefinition.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; +using System; + +namespace FRED.Core.Data { + public class FileDefinition { + [JsonProperty] + public String? File { get; set; } = null; + + [JsonProperty] + public String? Description { get; set; } = null; + } +} diff --git a/Source/Core/Data/InfoData.cs b/Source/Core/Data/InfoData.cs new file mode 100644 index 0000000..2dd9013 --- /dev/null +++ b/Source/Core/Data/InfoData.cs @@ -0,0 +1,12 @@ +using System; + +namespace FRED.Core.Data { + public class InfoData { + public String? Name { get; set; } = null; + public String? File { get; set; } = null; + public String? Checksum { get; set; } = null; + public int? Files { get; set; } = null; + public DateTime? TimeCreated { get; set; } = null; + public DateTime? TimeChanged { get; set; } = null; + } +} diff --git a/Source/Core/Events/FileEvent.cs b/Source/Core/Events/FileEvent.cs new file mode 100644 index 0000000..6cd0921 --- /dev/null +++ b/Source/Core/Events/FileEvent.cs @@ -0,0 +1,12 @@ +using FRED.Core.Data; +using System; + +namespace FRED.Core.Events { + public class FileEvent : EventArgs { + public FileContainer? File { get; set; } + + public FileEvent(FileContainer? container) { + File = container; + } + } +} diff --git a/Source/Core/Events/PropertiesEvent.cs b/Source/Core/Events/PropertiesEvent.cs new file mode 100644 index 0000000..73639d8 --- /dev/null +++ b/Source/Core/Events/PropertiesEvent.cs @@ -0,0 +1,12 @@ +using FRED.Core.Parser; +using System; + +namespace FRED.Core.Events { + public class PropertiesEvent : EventArgs { + public PropertyList? Properties { get; set; } + + public PropertiesEvent(PropertyList? list) { + Properties = list; + } + } +} diff --git a/Source/Core/Fritzbox/Networking/Discover/Discover.cs b/Source/Core/Fritzbox/Networking/Discover/Discover.cs new file mode 100644 index 0000000..b2e9280 --- /dev/null +++ b/Source/Core/Fritzbox/Networking/Discover/Discover.cs @@ -0,0 +1,105 @@ +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Net; +using Rssdp; +using System; +using System.Collections.Generic; +using FRED.Core.Interfaces; +using FRED.Core.Data; + +namespace FRED.Core.Fritzbox.Networking.Discover { + public static class Discovery { + public static String? Box = null; + public static List> callbacks = new List>(); + public static List observers = new List(); + + public static void SearchForDevices() { + using (var deviceLocator = new SsdpDeviceLocator(new Rssdp.Infrastructure.SsdpCommunicationsServer(new SocketFactory(GetLocalIpAddress())))) { + deviceLocator.NotificationFilter = "upnp:rootdevice"; + deviceLocator.DeviceAvailable += OnDeviceResponse; + deviceLocator.StartListeningForNotifications(); + deviceLocator?.SearchAsync(); + } + } + + public static void AddObserver(Changeable callback) { + observers.Add(callback); + } + + private async static void OnDeviceResponse(object? sender, DeviceAvailableEventArgs e) { + SsdpDevice fullDevice = await e.DiscoveredDevice.GetDeviceInfo(); + + if (fullDevice.FriendlyName.Contains("FRITZ!Box") && fullDevice.PresentationUrl != null) { + if (Box == null) { + Box = fullDevice.PresentationUrl.ToString(); + + foreach(Action callback in callbacks) { + callback(Box); + } + + foreach(Changeable observer in observers) { + observer.OnChange(fullDevice, e); + } + } + + //System.Diagnostics.Debug.Print("Found " + e.DiscoveredDevice.Usn + " at " + e.DiscoveredDevice.DescriptionLocation.ToString() + ": " + fullDevice.PresentationUrl.ToString()); + } + } + + public static void WhenReady(Action callback) { + if(Box == null) { + callbacks.Add(callback); + return; + } + + callback(Box); + } + + public static string GetLocalIpAddress() { + UnicastIPAddressInformation? mostSuitableIp = null; + var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); + + foreach(var network in networkInterfaces) { + if(network.OperationalStatus != OperationalStatus.Up) { + continue; + } + + var properties = network.GetIPProperties(); + + if(properties.GatewayAddresses.Count == 0) { + continue; + } + + foreach(var address in properties.UnicastAddresses) { + if(address.Address.AddressFamily != AddressFamily.InterNetwork) { + continue; + } + + if(IPAddress.IsLoopback(address.Address)) { + continue; + } + + if(!address.IsDnsEligible) { + if(mostSuitableIp == null) { + mostSuitableIp = address; + } + + continue; + } + + if(address.PrefixOrigin != PrefixOrigin.Dhcp) { + if(mostSuitableIp == null || !mostSuitableIp.IsDnsEligible) { + mostSuitableIp = address; + } + + continue; + } + + return address.Address.ToString(); + } + } + + return mostSuitableIp != null ? mostSuitableIp.Address.ToString() : "127.0.0.1"; + } + } +} diff --git a/Source/Core/Fritzbox/Networking/HTTP/Request.cs b/Source/Core/Fritzbox/Networking/HTTP/Request.cs new file mode 100644 index 0000000..995be0d --- /dev/null +++ b/Source/Core/Fritzbox/Networking/HTTP/Request.cs @@ -0,0 +1,35 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +namespace FRED.Core.Fritzbox.Networking.HTTP { + class Request { + private static readonly HttpClient Client = new HttpClient(); + private static async Task GetAsync(String url) { + Client.DefaultRequestHeaders.Accept.Clear(); + Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); + + HttpResponseMessage response = await Client.GetAsync(url); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadAsStringAsync(); + } + + private static async Task PostAsync(String url, String data){ + HttpContent content = new StringContent(data); + HttpResponseMessage response = await Client.PostAsync(url, content); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadAsStringAsync(); + } + + public static async Task Get(String url, Action callback) { + String getResponse = await GetAsync(url); + callback(getResponse); + } + + public static async Task Post(String url, String data, Action callback) { + String getResponse = await PostAsync(url, data); + callback(getResponse); + } + } +} diff --git a/Source/Core/Fritzbox/Networking/Info/JASON.cs b/Source/Core/Fritzbox/Networking/Info/JASON.cs new file mode 100644 index 0000000..dedf156 --- /dev/null +++ b/Source/Core/Fritzbox/Networking/Info/JASON.cs @@ -0,0 +1,94 @@ +using FRED.Core.Fritzbox.Networking.Discover; +using FRED.Core.Fritzbox.Networking.HTTP; +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace FRED.Core.Fritzbox.Networking { + public class JASONData { + private static Dictionary _Data = new Dictionary(); + + public object? this[String key] { + get { + if(_Data.ContainsKey(key)) { + return _Data[key]; + } else { + return null; + } + } + + set { + if (value == null) { + return; + } + + _Data[key] = value; + } + } + + public JASONData() { + this["Flag"] = new List(); + } + + public new String ToString() { + StringBuilder output = new StringBuilder(); + output.Append("[JASON"); + + if (_Data != null) { + foreach (KeyValuePair entry in _Data) { + output.Append(" "); + output.Append(entry.Key); + output.Append("="); + + if(entry.Value == null) { + output.Append(""); + } else if (entry.Value.GetType() == typeof(List)) { + output.Append(String.Join(";", (List)entry.Value)); + } else { + output.Append(entry.Value.ToString()); + } + } + } + + output.Append("]"); + + return output.ToString(); + } + } + + public class JASON { + public static void Collect(Action callback) { + Discovery.WhenReady(delegate (String url) { + Request.Get(url + "jason_boxinfo.xml", delegate (String response) { + JASONData data = new JASONData(); + XmlDocument doc = new XmlDocument(); + + if(response == null) { + callback(null); + return; + } + + doc.LoadXml(response); + + if(doc != null && doc.DocumentElement != null) { + foreach(XmlNode node in doc.DocumentElement.ChildNodes) { + String name = node.Name.Replace("j:", ""); + + switch (name) { + case "Flag": + ((List?)data["Flag"])?.Add(node.InnerText); + break; + default: + data[name] = node.InnerText; + break; + } + } + } + + callback(data); + }).Wait(); + }); + } + } +} diff --git a/Source/Core/Fritzbox/Networking/Info/JUIS.cs b/Source/Core/Fritzbox/Networking/Info/JUIS.cs new file mode 100644 index 0000000..81267d5 --- /dev/null +++ b/Source/Core/Fritzbox/Networking/Info/JUIS.cs @@ -0,0 +1,96 @@ +using System; +using FRED.Core.Fritzbox.Networking.HTTP; +using FRED.Core.Fritzbox.Networking.Discover; +using System.Xml; +using System.Collections.Generic; +using System.Text; + +namespace FRED.Core.Fritzbox.Networking { + public class JUISData { + private static Dictionary _Data = new Dictionary(); + + public object? this[String key] { + get { + if(_Data.ContainsKey(key)) { + return _Data[key]; + } else { + return null; + } + } + + set { + if(value == null) { + return; + } + + _Data[key] = value; + } + } + + public JUISData() { + this["Flag"] = new List(); + } + + public new String ToString() { + StringBuilder output = new StringBuilder(); + output.Append("[JUIS"); + + foreach(KeyValuePair entry in _Data) { + output.Append(" "); + output.Append(entry.Key); + output.Append("="); + + if(entry.Value == null) { + output.Append(""); + } else if(entry.Value.GetType() == typeof(List)) { + output.Append(String.Join(";", (List) entry.Value)); + } else { + output.Append(entry.Value.ToString()); + } + } + + output.Append("]"); + + return output.ToString(); + } + } + + public class JUIS { + public static void Collect(Action callback) { + Discovery.WhenReady(delegate (String url) { + Request.Get(url + "juis_boxinfo.xml", delegate (String response) { + JUISData data = new JUISData(); + XmlDocument doc = new XmlDocument(); + + if(response == null) { + callback(null); + return; + } + + doc.LoadXml(response); + + if(doc != null && doc.DocumentElement != null) { + foreach(XmlNode node in doc.DocumentElement.ChildNodes) { + if(node == null) { + return; + } + + String name = node.Name.Replace("q:", ""); + + switch(name) { + case "Flag": + ((List?) data["Flag"])?.Add(node.InnerText); + break; + default: + data[name] = node.InnerText; + break; + } + } + } + + callback(data); + }).Wait(); + }); + } + } +} diff --git a/Source/Core/Fritzbox/Networking/Info/SystemStatus.cs b/Source/Core/Fritzbox/Networking/Info/SystemStatus.cs new file mode 100644 index 0000000..9c95ce7 --- /dev/null +++ b/Source/Core/Fritzbox/Networking/Info/SystemStatus.cs @@ -0,0 +1,50 @@ +using FRED.Core.Fritzbox.Networking.Discover; +using FRED.Core.Fritzbox.Networking.HTTP; +using System; +using System.Text.RegularExpressions; + +namespace FRED.Core.Fritzbox.Networking { + public class SystemStatusData { + public String? Model { get; set; } = null; + public String? Annex { get; set; } = null; + public String? Running { get; set; } = null; + public String? Reboots { get; set; } = null; + public String? Hash { get; set; } = null; + public String? State { get; set; } = null; + public String? Version { get; set; } = null; + public String? SubVersion { get; set; } = null; + public String? Branding { get; set; } = null; + + public new String ToString() { + return "[SystemStatus Model=" + Model + ", Annex=" + Annex + ", Running=" + Running + ", Reboots=" + Reboots + ", Hash=" + Hash + ", State=" + State + ", FirmwareVersion=" + Version + ", Subversion=" + SubVersion + ", Branding=" + Branding + "]"; + } + } + + public class SystemStatus { + public static void Collect(Action callback) { + Discovery.WhenReady(delegate (String url) { + Request.Get(url+ "cgi-bin/system_status", delegate (String response) { + if(response == null) { + callback(null); + return; + } + + String data = Regex.Replace(response, "(<.*?>|\r|\n)", String.Empty); + String[] parts = data.Split("-"); + + callback(new SystemStatusData() { + Model = parts[0], + Annex = parts[1], + Running = parts[2], + Reboots = parts[3], + Hash = parts[4] + "-" + parts[5], + State = parts[6], + Version = parts[7], + SubVersion = parts[8], + Branding = parts[9] + }); + }).Wait(); + }); + } + } +} diff --git a/Source/Core/Interfaces/Changeable.cs b/Source/Core/Interfaces/Changeable.cs new file mode 100644 index 0000000..fec0710 --- /dev/null +++ b/Source/Core/Interfaces/Changeable.cs @@ -0,0 +1,8 @@ +using System; + +namespace FRED.Core.Interfaces { + public interface Changeable { + public void OnChange(object? sender, EventArgs e); + public void OnReady(Action callback); + } +} diff --git a/Source/Core/Interfaces/Consumable.cs b/Source/Core/Interfaces/Consumable.cs new file mode 100644 index 0000000..4424cfb --- /dev/null +++ b/Source/Core/Interfaces/Consumable.cs @@ -0,0 +1,9 @@ +using System; + +namespace FRED.Core.Interfaces { + public interface Consumable { + public bool Get(String name, Action callback); + public bool IsReady(); + public void OnReady(Action callback); + } +} diff --git a/Source/Core/Interfaces/EditorInstance.cs b/Source/Core/Interfaces/EditorInstance.cs new file mode 100644 index 0000000..905d8eb --- /dev/null +++ b/Source/Core/Interfaces/EditorInstance.cs @@ -0,0 +1,14 @@ +using System; + +namespace FRED.Core.Interfaces { + public enum EditorHighlighter { + Default, + XML, + Binary + } + + public interface EditorInstance { + public void SetContent(String? source); + public void SetHighlighter(EditorHighlighter? type); + } +} diff --git a/Source/Core/Parser/ExportFile.cs b/Source/Core/Parser/ExportFile.cs new file mode 100644 index 0000000..6f11189 --- /dev/null +++ b/Source/Core/Parser/ExportFile.cs @@ -0,0 +1,191 @@ +using FRED.Core.Data; +using FRED.Core.Events; +using FRED.Core.Interfaces; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace FRED.Core.Parser { + public class ExportFile : Consumable { + private List on_ready = new List(); + private Dictionary files = new Dictionary(); + private Dictionary definitions = new Dictionary(); + private String? file = null; + private String? content = null; + private String? name = null; + private String? checksum = null; + private Boolean is_ready = false; + private PropertyList? properties; + public event EventHandler? observer; + + public ExportFile(String? file) { + this.file = file; + + this.LoadDefinitions(); + } + + private void LoadDefinitions() { + String content = File.ReadAllText("./Definitions/Files.json", Encoding.UTF8); + List? json = JsonConvert.DeserializeObject?>(content); + + if(json != null) { + json.ForEach(file => { + if(file != null && file.File != null) { + definitions.Add(file.File, file); + } + }); + } + } + + public FileDefinition? GetDefinition(String? name) { + if(this.definitions == null || name == null) { + return null; + } + + return this.definitions.GetValueOrDefault(name, null); + } + + public void SetObserver(Changeable? instance) { + if(instance == null) { + return; + } + + observer += instance.OnChange; + } + + public Boolean IsReady() { + return this.is_ready; + } + + public void OnReady(Action callback) { + this.on_ready.Add(callback); + } + + public bool Get(String name, Action callback) { + if(!this.is_ready) { + return false; + } + + System.Diagnostics.Debug.Print("[REQUEST] " + name); + + switch (name) { + case "Info": + DateTime time_changed = new DateTime(); + DateTime time_created = new DateTime(); + + if(this.file != null) { + time_changed = File.GetLastWriteTime(this.file); + time_created = File.GetCreationTime(this.file); + } + + callback.Invoke((object) new InfoData () { + Name = this.name, + File = this.file, + Checksum = this.checksum, + TimeChanged = time_changed, + TimeCreated = time_created, + Files = this.files.Count + }); + break; + case "Name": + callback.Invoke(this.name); + break; + case "File": + callback.Invoke(this.file); + break; + case "Checksum": + callback.Invoke(this.checksum); + break; + case "Properties": + callback.Invoke( this.properties); + break; + default: + if(files.ContainsKey(name)) { + callback.Invoke(files[name]); + } else { + callback.Invoke(null); + } + break; + } + + return true; + } + + public PropertyList? GetProperties() { + return this.properties; + } + + public void Parse() { + if(this.file == null) { + System.Diagnostics.Debug.Print("File is empty?"); + return; + } + + this.content = File.ReadAllText(this.file, Encoding.UTF8); + + Regex export = new Regex(@"^\*{4}\s(?[A-Za-z!\s0-9]+)?\sCONFIGURATION\sEXPORT\s(?[\s\S]*?)?\*{4}\sEND\sOF\sEXPORT\s(?[A-F0-9]+)?\s\*{4}\s*?$", RegexOptions.IgnoreCase | RegexOptions.Multiline); + Match match = export.Match(this.content); + + if(!match.Success) { + System.Diagnostics.Debug.Print("No Matches Found, bad export file?"); + return; + } + + this.name = match.Groups["name"].Value; + this.checksum = match.Groups["checksum"].Value; + this.content = match.Groups["content"].Value; + + // Get Properties + Regex properties = new Regex(@"^\A(?[\s\S]*?)\s(\*{4})", RegexOptions.IgnoreCase | RegexOptions.Multiline); + match = properties.Match(this.content); + String props = match.Groups["properties"].Value; + this.content = this.content.Replace(props, ""); + this.properties = new PropertyList(props); + + observer?.Invoke(this, new PropertiesEvent(this.properties)); + + this.ParseFiles(); + + this.is_ready = true; + + foreach(Action callback in this.on_ready) { + callback(); + } + } + + public void ParseFiles() { + Regex files = new Regex(@"^\*{4}\s(?:CRYPTED)?(?(CFG|BIN|B64))?FILE:(?.*)?\s(?[\s\S]*?)?\s\*{4}\sEND\sOF\sFILE\s\*{4}\s*?$", RegexOptions.IgnoreCase | RegexOptions.Multiline); + + if(this.content != null) { + foreach (Match match in files.Matches(this.content)) { + FileContainer file = new FileContainer(match.Groups["name"].Value, match.Groups["type"].Value); + + if(file != null) { + String? name = file.GetName(); + FileDefinition? definition = this.GetDefinition(name); + + if(!match.Groups["crypted"].Value.Equals("")) { + file.SetCrypted(); + } + + if(definition != null) { + file.SetDefinition(ref definition); + } + + file.SetContent(match.Groups["content"].Value); + + // @ToDo Parse Content + + if(name != null) { + this.files.Add(name, file); + observer?.Invoke(this, new FileEvent(file)); + } + } + } + } + } + } +} diff --git a/Source/Core/Parser/PropertyList.cs b/Source/Core/Parser/PropertyList.cs new file mode 100644 index 0000000..a3ff4ec --- /dev/null +++ b/Source/Core/Parser/PropertyList.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace FRED.Core.Parser { + public class PropertyList { + Dictionary properties = new Dictionary(); + + public PropertyList(String input) { + Regex content = new Regex(@"(?.*)?=(?.*)?", RegexOptions.Multiline | RegexOptions.IgnoreCase); + + foreach(Match match in content.Matches(input)) { + properties.Add(match.Groups["key"].Value, match.Groups["value"].Value); + } + } + + public Dictionary GetList() { + return this.properties; + } + + public String Get(String key) { + return this.properties[key]; + } + + public void Set(String key, String value) { + this.properties[key] = value; + } + + public new String ToString { + get { + List list = new List(); + + foreach (var pair in this.properties) { + list.Add(String.Format("[Property Name=\"{0}\", Value=\"{1}\"]", pair.Key, pair.Value)); + } + + return "{PropertyList " + string.Join(", ", list) + " }"; + } + } + } +} diff --git a/Source/Definitions/Files.json b/Source/Definitions/Files.json new file mode 100644 index 0000000..ae6bec0 --- /dev/null +++ b/Source/Definitions/Files.json @@ -0,0 +1,130 @@ +[ + { + "File": "ar7.cfg", + "Description": "Hauptkonfiguration der Fritz!Box (Netzwerk- und Systemkonfiguration)." + }, + { + "File": "wlan.cfg", + "Description": "Konfiguration des WLAN Accesspoints bzw. des WDS-Repeaters." + }, + { + "File": "voip.cfg", + "Description": "Konfiguration für VoIP" + }, + { + "File": "usb.cfg", + "Description": "Konfiguration der Dienste um NAS und den USB-Host-Anschluss" + }, + { + "File": "tr069.cfg", + "Description": "TR-069 ACS-Server, Autokonfiguration und automatische Firmwareupdates" + }, + { + "File": "tr369.cfg", + "Description": "TR-369 USP-Platform, Kommunikation zwischen Anbieter und Router" + }, + { + "File": "fx_cg", + "Description": "Telefon?" + }, + { + "File": "fx_conf", + "Description": "Hauptkonfiguration für Telefon, siehe telcfg" + }, + { + "File": "fx_def", + "Description": "Telefon?" + }, + { + "File": "fx_lcr", + "Description": "Least Cost Router Funktion für Telefonie" + }, + { + "File": "telefon_misc", + "Description": "u.A. interner Fax-Empfang" + }, + { + "File": "phonebook", + "Description": "Systemweites Telefonbuch" + }, + { + "File": "calllog", + "Description": "Optionales Logfile für calllog" + }, + { + "File": "dect_misc", + "Description": "Globale Konfiguration für dect_manager" + }, + { + "File": "fonctrl", + "Description": "Konfiguration angemeldeter DECT- und Mini-Telefone" + }, + { + "File": "dect_eeprom", + "Description": "DECT-Kalibrier EEPROM" + }, + { + "File": "dmgr_handset_user", + "Description": "Mobilteil-Konfiguration" + }, + { + "File": "tamconf", + "Description": "Anrufbeantworter-Konfig" + }, + { + "File": "vpn.cfg", + "Description": "AVM VPN Konfiguration" + }, + { + "File": "user.cfg", + "Description": "Konfiguration der Kindersicherung für usermand / usermand2" + }, + { + "File": "userstat.cfg", + "Description": "Konfiguration der Kindersicherung für usermand / usermand2" + }, + { + "File": "umts.cfg", + "Description": "UMTS-Modem Konfiguration" + }, + { + "File": "configd", + "Description": "Konfiguration für configd" + }, + { + "File": "ahausr.cfg", + "Description": "" + }, + { + "File": "aha.cfg", + "Description": "" + }, + { + "File": "ahastat.cfg", + "Description": "" + }, + { + "File": "ahadect.cfg", + "Description": "" + }, + { + "File": "ahanet.cfg", + "Description": "" + }, + { + "File": "ahaglobal.cfg", + "Description": "" + }, + { + "File": "ahapushmail.cfg", + "Description": "" + }, + { + "File": "avmnexus.cfg", + "Description": "" + }, + { + "File": "dvb.cfg", + "Description": "TV Einstellungen" + } +] \ No newline at end of file diff --git a/Source/FRED.csproj b/Source/FRED.csproj new file mode 100644 index 0000000..6a5fa13 --- /dev/null +++ b/Source/FRED.csproj @@ -0,0 +1,58 @@ + + + + WinExe + net7.0-windows + enable + true + False + False + False + Debug;Release;Packed + + + + embedded + + + + embedded + + + + embedded + + + + + + + + + MSBuild:Compile + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + Always + + + + diff --git a/Source/FRED.sln b/Source/FRED.sln new file mode 100644 index 0000000..9921e9b --- /dev/null +++ b/Source/FRED.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33829.357 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FRED", "FRED.csproj", "{600295C7-490B-4E0E-B9BD-69151531087F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Packed|Any CPU = Packed|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {600295C7-490B-4E0E-B9BD-69151531087F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {600295C7-490B-4E0E-B9BD-69151531087F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {600295C7-490B-4E0E-B9BD-69151531087F}.Packed|Any CPU.ActiveCfg = Packed|Any CPU + {600295C7-490B-4E0E-B9BD-69151531087F}.Packed|Any CPU.Build.0 = Packed|Any CPU + {600295C7-490B-4E0E-B9BD-69151531087F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {600295C7-490B-4E0E-B9BD-69151531087F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FD0D0898-4BE9-4F70-A0EF-8135EC9AABDA} + EndGlobalSection +EndGlobal diff --git a/Source/FodyWeavers.xml b/Source/FodyWeavers.xml new file mode 100644 index 0000000..5029e70 --- /dev/null +++ b/Source/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Source/UI/Assets/Binary.xaml b/Source/UI/Assets/Binary.xaml new file mode 100644 index 0000000..df1ef0c --- /dev/null +++ b/Source/UI/Assets/Binary.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Crypted.xaml b/Source/UI/Assets/Crypted.xaml new file mode 100644 index 0000000..9cb4681 --- /dev/null +++ b/Source/UI/Assets/Crypted.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Files.xaml b/Source/UI/Assets/Files.xaml new file mode 100644 index 0000000..d38bfed --- /dev/null +++ b/Source/UI/Assets/Files.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/FritzBox.xaml b/Source/UI/Assets/FritzBox.xaml new file mode 100644 index 0000000..6ce9d05 --- /dev/null +++ b/Source/UI/Assets/FritzBox.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Icons.xaml b/Source/UI/Assets/Icons.xaml new file mode 100644 index 0000000..5119232 --- /dev/null +++ b/Source/UI/Assets/Icons.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Info.xaml b/Source/UI/Assets/Info.xaml new file mode 100644 index 0000000..d8446ef --- /dev/null +++ b/Source/UI/Assets/Info.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Logo.xaml b/Source/UI/Assets/Logo.xaml new file mode 100644 index 0000000..57a8fef --- /dev/null +++ b/Source/UI/Assets/Logo.xaml @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Properties.xaml b/Source/UI/Assets/Properties.xaml new file mode 100644 index 0000000..cd41e07 --- /dev/null +++ b/Source/UI/Assets/Properties.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Settings.xaml b/Source/UI/Assets/Settings.xaml new file mode 100644 index 0000000..ac6ab30 --- /dev/null +++ b/Source/UI/Assets/Settings.xaml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/Unknown.xaml b/Source/UI/Assets/Unknown.xaml new file mode 100644 index 0000000..e3ea83a --- /dev/null +++ b/Source/UI/Assets/Unknown.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Assets/XML.xaml b/Source/UI/Assets/XML.xaml new file mode 100644 index 0000000..5d36920 --- /dev/null +++ b/Source/UI/Assets/XML.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Content/File.xaml b/Source/UI/Content/File.xaml new file mode 100644 index 0000000..d80c37c --- /dev/null +++ b/Source/UI/Content/File.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/UI/Content/File.xaml.cs b/Source/UI/Content/File.xaml.cs new file mode 100644 index 0000000..41a71a0 --- /dev/null +++ b/Source/UI/Content/File.xaml.cs @@ -0,0 +1,38 @@ +using FRED.Core.Data; +using FRED.UI.Controls; +using System; +using System.Windows.Controls; + +namespace FRED.UI.Content { + public partial class File : UserControl { + public File(String? name) { + InitializeComponent(); + } + + public void Update(FileContainer file) { + this.Filename.Content = file.GetName(); + this.Description.Content = file.GetDescription(); + + if(this.Source == null) { + return; + } + + switch(file.GetSubType()) { + case "Binary": + this.Source.CreateEditor(typeof(HEXEditor)); + this.Source.SetHighlighter(Core.Interfaces.EditorHighlighter.Binary); + break; + case "XML": + this.Source.CreateEditor(typeof(TextEditor)); + this.Source.SetHighlighter(Core.Interfaces.EditorHighlighter.XML); + break; + default: + this.Source.CreateEditor(typeof(TextEditor)); + this.Source.SetHighlighter(Core.Interfaces.EditorHighlighter.Default); + break; + } + + this.Source.SetContent(file.GetSource()); + } + } +} diff --git a/Source/UI/Content/Info.xaml b/Source/UI/Content/Info.xaml new file mode 100644 index 0000000..cb4dab8 --- /dev/null +++ b/Source/UI/Content/Info.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/UI/Content/Info.xaml.cs b/Source/UI/Content/Info.xaml.cs new file mode 100644 index 0000000..8f0e6a2 --- /dev/null +++ b/Source/UI/Content/Info.xaml.cs @@ -0,0 +1,147 @@ +using FRED.Core.Data; +using System.Windows; +using System.Windows.Controls; + +namespace FRED.UI.Content { + public partial class Info : UserControl { + public Info() { + InitializeComponent(); + + this.Description.Content = "Generelle Informationen über der Export-Backupdatei."; + } + + public void BindData(InfoData data) { + // File + Label label = new Label() { + Name = "Label_File", + Content = "File", + Width = 250, + VerticalAlignment = VerticalAlignment.Center + }; + + this.Panel.Children.Add(new DockPanel() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Children = { + label, + new TextBlock() { + Name = "Text_File", + TextWrapping = TextWrapping.Wrap, + Text = data.File + } + } + }); + + DockPanel.SetDock(label, Dock.Left); + + // Name + label = new Label() { + Name = "Label_Name", + Content = "Name", + Width = 250, + VerticalAlignment = VerticalAlignment.Center + }; + + this.Panel.Children.Add(new DockPanel() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Children = { + label, + new TextBlock() { + Name = "Text_Name", + TextWrapping = TextWrapping.Wrap, + Text = data.Name + } + } + }); + + DockPanel.SetDock(label, Dock.Left); + + // Checksum + label = new Label() { + Name = "Label_Checksum", + Content = "Checksum", + Width = 250, + VerticalAlignment = VerticalAlignment.Center + }; + + this.Panel.Children.Add(new DockPanel() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Children = { + label, + new TextBlock() { + Name = "Text_Checksum", + TextWrapping = TextWrapping.Wrap, + Text = data.Checksum + } + } + }); + + DockPanel.SetDock(label, Dock.Left); + + // Files + label = new Label() { + Name = "Label_Files", + Content = "Files", + Width = 250, + VerticalAlignment = VerticalAlignment.Center + }; + + this.Panel.Children.Add(new DockPanel() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Children = { + label, + new TextBlock() { + Name = "Text_Files", + TextWrapping = TextWrapping.Wrap, + Text = data.Files + "" + } + } + }); + + DockPanel.SetDock(label, Dock.Left); + + // TimeCreated + label = new Label() { + Name = "Label_TimeCreated", + Content = "Time Created", + Width = 250, + VerticalAlignment = VerticalAlignment.Center + }; + + this.Panel.Children.Add(new DockPanel() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Children = { + label, + new TextBlock() { + Name = "Text_TimeCreated", + TextWrapping = TextWrapping.Wrap, + Text = data.TimeCreated.ToString() + } + } + }); + + DockPanel.SetDock(label, Dock.Left); + + // TimeChanged + label = new Label() { + Name = "Label_TimeChanged", + Content = "Time Changed", + Width = 250, + VerticalAlignment = VerticalAlignment.Center + }; + + this.Panel.Children.Add(new DockPanel() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Children = { + label, + new TextBlock() { + Name = "Text_TimeChanged", + TextWrapping = TextWrapping.Wrap, + Text = data.TimeChanged.ToString() + } + } + }); + + DockPanel.SetDock(label, Dock.Left); + } + } +} diff --git a/Source/UI/Content/Properties.xaml b/Source/UI/Content/Properties.xaml new file mode 100644 index 0000000..85cd966 --- /dev/null +++ b/Source/UI/Content/Properties.xaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/UI/Content/Properties.xaml.cs b/Source/UI/Content/Properties.xaml.cs new file mode 100644 index 0000000..93468e9 --- /dev/null +++ b/Source/UI/Content/Properties.xaml.cs @@ -0,0 +1,39 @@ +using FRED.Core.Parser; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; + +namespace FRED.UI.Content { + public partial class Properties : UserControl { + public Properties() { + InitializeComponent(); + + this.Description.Content = "Diese Werte sind die Export-Parameter, die am Anfang der Datei stehen."; + } + + public void BindData(PropertyList properties) { + foreach(KeyValuePair entry in properties.GetList()) { + Label label = new Label() { + Name = "Label_" + entry.Key, + Content = entry.Key, + Width = 250, + VerticalAlignment = VerticalAlignment.Center + }; + + this.Panel.Children.Add(new DockPanel() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Children = { + label, + new TextBox() { + Name = "Input_" + entry.Key, + Text = entry.Value, + HorizontalAlignment = HorizontalAlignment.Stretch + } + } + }); + + DockPanel.SetDock(label, Dock.Left); + } + } + } +} diff --git a/Source/UI/Controls/DynamicEditor.cs b/Source/UI/Controls/DynamicEditor.cs new file mode 100644 index 0000000..f1de9e7 --- /dev/null +++ b/Source/UI/Controls/DynamicEditor.cs @@ -0,0 +1,28 @@ +using FRED.Core.Interfaces; +using System; +using System.Windows.Controls; + +namespace FRED.UI.Controls { + public class DynamicEditor : UserControl { + EditorInstance? instance = null; + + public DynamicEditor() {} + + public void SetContent(String? source) { + instance?.SetContent(source); + } + + public void SetHighlighter(EditorHighlighter? type) { + instance?.SetHighlighter(type); + } + public void CreateEditor(Type? type) { + if(type == typeof(HEXEditor)) { + this.instance = new HEXEditor(); + } else if (type == typeof(TextEditor)) { + this.instance = new TextEditor(); + } + + this.Content = instance; + } + } +} diff --git a/Source/UI/Controls/HEXEditor.cs b/Source/UI/Controls/HEXEditor.cs new file mode 100644 index 0000000..7ebd8f8 --- /dev/null +++ b/Source/UI/Controls/HEXEditor.cs @@ -0,0 +1,40 @@ +using FRED.Core.Interfaces; +using System; +using System.IO; +using System.Windows; +using System.Windows.Controls; +using WpfHexaEditor; + +namespace FRED.UI.Controls { + public class HEXEditor : UserControl, EditorInstance { + HexEditor editor = new HexEditor() { + BorderThickness = new Thickness(0, 0, 0, 0), + AllowContextMenu = false + }; + + public HEXEditor() { + this.Content = editor; + } + + public void SetContent(String? source) { + MemoryStream stream = new MemoryStream(); + StreamWriter writer = new StreamWriter(stream); + + if(source == null) { + writer.Write(source); + writer.Flush(); + } + + stream.Position = 0; + this.editor.Stream = stream; + } + + public void SetHighlighter(EditorHighlighter? type) { + switch(type) { + case EditorHighlighter.Binary: + /* Do Nothing */ + break; + } + } + } +} diff --git a/Source/UI/Controls/Navigation.cs b/Source/UI/Controls/Navigation.cs new file mode 100644 index 0000000..3f55e5c --- /dev/null +++ b/Source/UI/Controls/Navigation.cs @@ -0,0 +1,259 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +namespace FRED.UI.Controls { + public class Navigation : ItemsControl { + public Navigation() { + Loaded += OnControlLoaded; + } + + protected override void OnInitialized(EventArgs e) { + base.OnInitialized(e); + + FrameworkElementFactory conatiner = new FrameworkElementFactory(typeof(StackPanel)); + conatiner.SetValue(StackPanel.IsItemsHostProperty, true); + conatiner.SetValue(StackPanel.OrientationProperty, Orientation.Vertical); + + var template = new ItemsPanelTemplate { + VisualTree = conatiner + }; + + ItemsPanel = template; + } + + private void OnControlLoaded(object? sender, RoutedEventArgs e) { + foreach(var entry in Items) { + if(entry is NavigationItem item) { + if(item != null) { + RegisterItem(item); + } + } + } + + foreach(var entry in Items) { + if (entry is NavigationItem) { + if ((NavigationItem) entry != null){ + break; + } + } + } + } + + private void RegisterItem(NavigationItem item) { + item.ArrowVisibility = ShowArrow ? Visibility.Visible : Visibility.Hidden; + item.Collapse(); + item.OnExpandedEvent += OnItemExpanded; + item.OnCollapsedEvent += OnItemCollapsed; + } + + void OnItemExpanded(NavigationItem expandedItem) { + foreach(var entry in Items) { + if(entry is NavigationItem item) { + if(item != null) { + if(expandedItem != item) { + if(item.IsExpanded) { + item.Collapse(); + } + } + } + } + } + } + + void OnItemCollapsed(NavigationItem collapsedItem) { + foreach(var entry in Items) { + if(entry is NavigationItem item) { + if(item != null) { + if(collapsedItem != item) { + if(item.IsExpanded) { + item.Collapse(); + return; + } + } + } + } + } + + collapsedItem.Expand(); + } + + public bool ShowArrow { + get { + return (bool) GetValue(ShowArrowProperty); + } + + set { + SetValue(ShowArrowProperty, value); + } + } + + public static readonly DependencyProperty ShowArrowProperty = DependencyProperty.Register("ShowArrow", typeof(bool), typeof(Navigation), new FrameworkPropertyMetadata(true, OnShowArrowPropertyChanged)); + + private static void OnShowArrowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + Navigation control = (Navigation) d; + + if(control == null) { + return; + } + + foreach(var entry in control.Items) { + NavigationItem item = (NavigationItem) entry; + + if(item != null) { + item.ArrowVisibility = (bool) e.NewValue ? Visibility.Visible : Visibility.Hidden; + } + } + } + } + + public class NavigationItemToggleButton : ToggleButton { + public bool IsExpanded { + get { + return (bool) GetValue(IsExpandedProperty); + } + + set { + SetValue(IsExpandedProperty, value); + } + } + + public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.RegisterAttached("IsExpanded", typeof(bool), typeof(NavigationItemToggleButton), new FrameworkPropertyMetadata(false)); + + public Visibility ArrowVisibility { + get { + return (Visibility) GetValue(ArrowVisibilityProperty); + } + + set { + SetValue(ArrowVisibilityProperty, value); + } + } + + public static readonly DependencyProperty ArrowVisibilityProperty = DependencyProperty.RegisterAttached("ArrowVisibility", typeof(Visibility), typeof(NavigationItemToggleButton), new FrameworkPropertyMetadata(Visibility.Visible)); + + protected override void OnToggle() { + if(!IsExpanded) { + base.OnToggle(); + } + } + } + + public class ToggleCommand : ICommand { + private NavigationItem? owner; + + public ToggleCommand(NavigationItem owner) { + this.owner = owner; + } + + public event EventHandler? CanExecuteChanged { + add { } + remove { } + } + + public bool CanExecute(object? parameter) { + return true; // !owner.IsExpanded; + } + + public void Execute(object? parameter) { + owner?.OnToggle(); + } + } + + public class NavigationItem : HeaderedContentControl, INotifyPropertyChanged { + public event PropertyChangedEventHandler? PropertyChanged; + + protected void OnPropertyChanged(string propertyName) { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public delegate void OnExpanded(NavigationItem item); + public delegate void OnCollapsed(NavigationItem item); + public event OnExpanded? OnExpandedEvent; + public event OnCollapsed? OnCollapsedEvent; + + public NavigationItem() { + ToggleCommand = new ToggleCommand(this); + } + + protected override void OnInitialized(EventArgs e) { + base.OnInitialized(e); + + var resources = new ResourceDictionary { + Source = new Uri("/FRED;component/UI/Design/Navigation.xaml", UriKind.RelativeOrAbsolute) + }; + + Resources = resources; + Template = resources["RevealExpander"] as ControlTemplate; + } + + private void OnNavigationItemControlLoaded(object sender, RoutedEventArgs e) { } + + internal void OnToggle() { + if(IsExpanded) { + OnCollapsedEvent?.Invoke(this); + Collapse(); + } else { + OnExpandedEvent?.Invoke(this); + Expand(); + } + } + + internal void Collapse() { + IsExpanded = false; + NavigationItemToggleButton? item = (NavigationItemToggleButton) Template.FindName("ExpanderButton", this); + + if(item != null) { + item.IsExpanded = false; + } + } + + internal void Expand() { + IsExpanded = true; + NavigationItemToggleButton? item = (NavigationItemToggleButton) Template.FindName("ExpanderButton", this); + + if(item != null) { + item.IsExpanded = true; + } + } + + public bool IsExpanded { + get { + return (bool) GetValue(IsItemExpandedProperty); + } + + private set { + SetValue(IsItemExpandedProperty, value); + } + } + + public static readonly DependencyProperty IsItemExpandedProperty = DependencyProperty.RegisterAttached("IsExpanded", typeof(bool), typeof(NavigationItem), new FrameworkPropertyMetadata(false)); + + public Visibility ArrowVisibility { + get { + return (Visibility) GetValue(ArrowVisibilityProperty); + } + + set { + SetValue(ArrowVisibilityProperty, value); + } + } + + public static readonly DependencyProperty ArrowVisibilityProperty = DependencyProperty.RegisterAttached("ArrowVisibility", typeof(Visibility), typeof(NavigationItem), new FrameworkPropertyMetadata(Visibility.Visible)); + + public ICommand ToggleCommand { + get { + return (ICommand) GetValue(ToggleCommandProperty); + } + + set { + SetValue(ToggleCommandProperty, value); + } + } + + public static readonly DependencyProperty ToggleCommandProperty = DependencyProperty.RegisterAttached("ToggleCommand", typeof(ICommand), typeof(NavigationItem), new FrameworkPropertyMetadata(null)); + } +} diff --git a/Source/UI/Controls/TextEditor.cs b/Source/UI/Controls/TextEditor.cs new file mode 100644 index 0000000..77e1ee2 --- /dev/null +++ b/Source/UI/Controls/TextEditor.cs @@ -0,0 +1,120 @@ +using FRED.Core.Interfaces; +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.AvalonEdit.Highlighting.Xshd; +using System; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Windows.Controls; +using System.Xml; +using System.Xml.Linq; + +namespace FRED.UI.Controls { + public class TextEditor : UserControl, EditorInstance { + protected static String TAG_ROOT = "FAKEROOTFORPARSER"; + ICSharpCode.AvalonEdit.TextEditor editor = new ICSharpCode.AvalonEdit.TextEditor() { + SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("ini"), + ShowLineNumbers = true, + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, + FontFamily = new System.Windows.Media.FontFamily("Consolas"), + FontSize = 16, + Options = { + InheritWordWrapIndentation = false + } + }; + + public TextEditor() { + this.Content = editor; + editor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.CSharp.CSharpIndentationStrategy(editor.Options); + + this.SetHighlighter(EditorHighlighter.Default); + } + + public void SetContent(String? source) { + if(source == null) { + editor.Text = ""; + return; + } + + if(this.editor.SyntaxHighlighting == HighlightingManager.Instance.GetDefinition("XML")) { + editor.Text = BeautifyXML(source); + return; + } + + editor.Text = source; + } + + public void SetHighlighter(EditorHighlighter? type) { + switch(type) { + case EditorHighlighter.XML: + this.editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML"); + break; + case EditorHighlighter.Default: + try { + using (Stream? stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("FRED.UI.Syntax.Config.xshd")) { + if (stream == null) { + return; + } + + using (XmlTextReader reader = new XmlTextReader(stream)) { + editor.SyntaxHighlighting = HighlightingLoader.Load(reader, HighlightingManager.Instance); + } + } + } catch (Exception e) { + /* Currently Do Nothing */ + System.Diagnostics.Debug.Print(e.ToString()); + } + break; + } + } + + private String BeautifyXML(String source) { + if(source.Trim().Length == 0) { + return source; + } + + String xml = source.Replace("\t", " "); + xml = xml.Replace("\r", ""); + xml = xml.Replace("\n", " "); + + String formattedXml = Regex.Replace(xml, @"()", "$1\n"); + + if(!Regex.IsMatch(formattedXml, @"^<\?xml(:\?)?[^>]*?>")) { + formattedXml = "<" + TAG_ROOT + ">" + formattedXml; + } + + formattedXml = Regex.Replace(formattedXml, @"^<\?xml(:\?)?[^>]*?>", "<" + TAG_ROOT + ">\n"); + + formattedXml += ""; + int indentation = 0; + String[] lines = formattedXml.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + String result = ""; + + foreach(String line in lines) { + if(line.Contains(" 0) { + result += new String('\t', indentation) + line + "\r\n"; + } else { + result += line + "\r\n"; + } + + if(line.Contains("<") && !line.Contains("", "").Replace("", "").Trim(); + } catch (Exception ex) { + return "/** Fehler beim Formatieren des XML:\n" + ex.ToString() + " **/\r\n" + CleanInvalidXmlChars(result.Trim()); + } + } + private String CleanInvalidXmlChars(String xml) { + return Regex.Replace(xml, @"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]+", ""); + } + } +} diff --git a/Source/UI/Converters/MultiplyConverter.cs b/Source/UI/Converters/MultiplyConverter.cs new file mode 100644 index 0000000..9339ead --- /dev/null +++ b/Source/UI/Converters/MultiplyConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace FRED.UI.Converters { + public class MultiplyConverter : IMultiValueConverter { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { + double result = 1.0; + + for(int i = 0; i < values.Length; i++) { + if(values[i] is double) { + result *= (double)values[i]; + } + } + + return result; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { + throw new Exception("Not implemented"); + } + } +} diff --git a/Source/UI/Converters/NavigationConverter.cs b/Source/UI/Converters/NavigationConverter.cs new file mode 100644 index 0000000..47077b2 --- /dev/null +++ b/Source/UI/Converters/NavigationConverter.cs @@ -0,0 +1,37 @@ +using FRED.UI.Controls; +using System; +using System.Globalization; +using System.Windows.Controls; +using System.Windows.Data; + +namespace FRED.UI.Converters { + public class NavigationConverter : IValueConverter { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + if(value is ScrollViewer) { + ScrollViewer scroller = (ScrollViewer) value; + + if (scroller != null) { + NavigationItem header = (NavigationItem) scroller.Parent; + + if(header != null) { + double height = 0; + + if(!(header.Height is double.NaN)) { + height = header.Height; + } + + if (!(header.ActualHeight is double.NaN)) { + height = header.ActualHeight; + } + } + } + } + + return Binding.DoNothing; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new Exception("Only OneWay supported!"); + } + } +} diff --git a/Source/UI/Design/Button.xaml b/Source/UI/Design/Button.xaml new file mode 100644 index 0000000..d7c0ab2 --- /dev/null +++ b/Source/UI/Design/Button.xaml @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/Source/UI/Design/Editor.xaml b/Source/UI/Design/Editor.xaml new file mode 100644 index 0000000..76eada0 --- /dev/null +++ b/Source/UI/Design/Editor.xaml @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/Source/UI/Design/Input.xaml b/Source/UI/Design/Input.xaml new file mode 100644 index 0000000..0b93095 --- /dev/null +++ b/Source/UI/Design/Input.xaml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/Source/UI/Design/List.xaml b/Source/UI/Design/List.xaml new file mode 100644 index 0000000..344147a --- /dev/null +++ b/Source/UI/Design/List.xaml @@ -0,0 +1,55 @@ + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Design/Miscellaneous.xaml b/Source/UI/Design/Miscellaneous.xaml new file mode 100644 index 0000000..977e83f --- /dev/null +++ b/Source/UI/Design/Miscellaneous.xaml @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/Source/UI/Design/Navigation.xaml b/Source/UI/Design/Navigation.xaml new file mode 100644 index 0000000..80e5f36 --- /dev/null +++ b/Source/UI/Design/Navigation.xaml @@ -0,0 +1,140 @@ + + + 0:0:0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/UI/Design/Scrollbar.xaml b/Source/UI/Design/Scrollbar.xaml new file mode 100644 index 0000000..eda9583 --- /dev/null +++ b/Source/UI/Design/Scrollbar.xaml @@ -0,0 +1,89 @@ + + + + \ No newline at end of file diff --git a/Source/UI/Design/Tab.xaml b/Source/UI/Design/Tab.xaml new file mode 100644 index 0000000..4a57974 --- /dev/null +++ b/Source/UI/Design/Tab.xaml @@ -0,0 +1,35 @@ + + + + \ No newline at end of file diff --git a/Source/UI/Design/UserControl.xaml b/Source/UI/Design/UserControl.xaml new file mode 100644 index 0000000..17a27b5 --- /dev/null +++ b/Source/UI/Design/UserControl.xaml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/Source/UI/Design/Window.xaml b/Source/UI/Design/Window.xaml new file mode 100644 index 0000000..de6cd32 --- /dev/null +++ b/Source/UI/Design/Window.xaml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/Source/UI/Editor.xaml b/Source/UI/Editor.xaml new file mode 100644 index 0000000..0d1671f --- /dev/null +++ b/Source/UI/Editor.xaml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/UI/Editor.xaml.cs b/Source/UI/Editor.xaml.cs new file mode 100644 index 0000000..f320394 --- /dev/null +++ b/Source/UI/Editor.xaml.cs @@ -0,0 +1,235 @@ +using FRED.Core.Data; +using FRED.Core.Events; +using FRED.Core.Fritzbox.Networking; +using FRED.Core.Interfaces; +using FRED.Core.Parser; +using FRED.UI.Content; +using FRED.UI.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace FRED.UI { + public partial class Editor : Window, Changeable { + private Consumable? consumer = null; + private List consuming = new List(); + + public Editor() { + InitializeComponent(); + } + + public new void Hide() { + base.Hide(); + this.consumer = null; + this.consuming = new List(); + } + + public new void Show() { + base.Show(); + + this.AddConsumer("Name", (object? result) => { + if(result == null) { + return; + } + + BoxName.Text = (String)result; + }); + + OpenInfo(); + Focus(); + } + + private void LoadDeviceInfos() { + /* START: @ToDo Currently for Testing here */ + JASON.Collect(delegate (JASONData? data) { + System.Diagnostics.Debug.Print(data?.ToString()); + }); + + JUIS.Collect(delegate (JUISData? data) { + System.Diagnostics.Debug.Print(data?.ToString()); + }); + + SystemStatus.Collect(delegate (SystemStatusData? data) { + System.Diagnostics.Debug.Print(data?.ToString()); + }); + /* END: @ToDo Currently for Testing here */ + } + + private void AddConsumer(String name, Action callback) { + if(consumer == null) { + this.consuming.Add(new ConsumingCallback() { + Name = name, + Callback = callback + }); + return; + } + + if(!consumer.Get(name, callback)) { + this.consuming.Add(new ConsumingCallback() { + Name = name, + Callback = callback + }); + } + } + + public void OnReady(Action callback) { + callback(); + } + + public void OnChange(object? sender, EventArgs e) { + if(e.GetType() == typeof(FileEvent)) { + FileEvent file = (FileEvent) e; + + if(file == null) { + return; + } + + FileContainer? container = file.File; + NavigationItem navi = new NavigationItem(); + String icon = "Unknown"; + + container?.UpdateSubType(); + + switch(container?.GetType()) { + case "CFG": + icon = "Properties"; + break; + case "B64": + if(container.HasSubType()) { + switch(container.GetSubType()) { + case "CFG": + icon = "Properties"; + break; + case "XML": + case "Binary": + icon = container.GetSubType(); + break; + } + } + break; + case "BIN": + icon = "Binary"; + break; + } + + navi.ArrowVisibility = Visibility.Hidden; + navi.PreviewMouseDown += OnClick; + navi.Header = new StackPanel() { + Orientation = Orientation.Horizontal, + Children = { + new Image() { + Width = 16, + Source = (System.Windows.Media.ImageSource) TryFindResource("Icon" + icon) + }, + new TextBlock() { + Text = container?.GetName() + } + } + }; + + if(container != null && container.IsCrypted()) { + ((StackPanel) navi.Header).Children.Add(new Image() { + Width = 16, + Source = (System.Windows.Media.ImageSource) TryFindResource("IconCrypted") + }); + } + + Files.Children.Add(navi); + } + + this.UpdateScrollbar(); + } + + protected void UpdateScrollbar() { + this.Focus(); + this.Scroll.InvalidateScrollInfo(); + } + + public void SetConsumer(Consumable consumer) { + this.consumer = consumer; + + this.consumer.OnReady(delegate() { + foreach(ConsumingCallback callback in this.consuming) { + if(callback != null && callback.Name != null && callback.Callback != null) { + consumer.Get(callback.Name, callback.Callback); + } + } + }); + } + + private void OpenInfo() { + Info info = new Info(); + + this.AddConsumer("Info", (object? result) => { + if (result == null) { + return; + } + + info.BindData((InfoData)result); + }); + + Panel.Content = info; + } + private void OnScroll(object sender, MouseWheelEventArgs e) { + this.Scroll.ScrollToVerticalOffset(this.Scroll.VerticalOffset); + this.UpdateScrollbar(); + } + + private void OnClick(object sender, MouseButtonEventArgs e) { + String? name = null; + NavigationItem item = (NavigationItem) sender; + + if(item.Header is StackPanel) { + StackPanel panel = (StackPanel) item.Header; + TextBlock? text = panel.Children.OfType().FirstOrDefault(); + + if(text != null) { + name = text.Text; + } + } else { + name = item.Header.ToString(); + } + + switch (name) { + case "Files": + this.UpdateScrollbar(); + break; + case "Info": + this.OpenInfo(); + break; + case "Properties": + Properties properties = new Properties(); + + this.AddConsumer("Properties", (object? result) => { + if(result == null) { + return; + } + properties.BindData((PropertyList) result); + }); + + Panel.Content = properties; + break; + default: + if(name == null) { + return; + } + + File file = new File(name); + + this.AddConsumer(name, (object? result) => { + if (result == null) { + return; + } + + file?.Update((FileContainer) result); + }); + + Panel.Content = file; + break; + } + } + } +} diff --git a/Source/UI/Syntax/Config.xshd b/Source/UI/Syntax/Config.xshd new file mode 100644 index 0000000..a125fe9 --- /dev/null +++ b/Source/UI/Syntax/Config.xshd @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + (\{|\}|=) + + + + + ([a-zA-Z0-9_]+)\s(?=\=) + + + + + // + + + /\* + \*/ + + + + + " + " + + + + + + + + + true + false + yes + no + + + + + \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} + + + (([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])) + + + (?:[0-9a-fA-F]{2}\:){5}[0-9a-fA-F]{2} + + + + + \b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?(?:m|s|w|d)? + + + \b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)? + + + + + + + \{ + \} + + + ([a-zA-Z0-9_]+)\s(?=\{) + \s(?=\{) + + + + + \ No newline at end of file diff --git a/Source/UI/TargetSeletor.xaml b/Source/UI/TargetSeletor.xaml new file mode 100644 index 0000000..96a6bfd --- /dev/null +++ b/Source/UI/TargetSeletor.xaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Öffne eine *.export Sicherungsdatei, die bereits von der Fritz!Box unter System > Sicherung heruntergeladen wurde. + + + + + + + + + + + + + + Verbinde dich direkt mit der Fritz!Box um diese zu bearbeiten. + + + + + + + + + diff --git a/Source/UI/TargetSeletor.xaml.cs b/Source/UI/TargetSeletor.xaml.cs new file mode 100644 index 0000000..6d99863 --- /dev/null +++ b/Source/UI/TargetSeletor.xaml.cs @@ -0,0 +1,137 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using FRED.Core.Interfaces; +using Rssdp; + +namespace FRED.UI { + public partial class TargetSeletor : Window, Changeable { + private Action? callback = null; + System.Windows.Forms.OpenFileDialog dialog = new System.Windows.Forms.OpenFileDialog() { + Filter = "Export files (*.export)|*.export", + InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop), + RestoreDirectory = true + }; + + public TargetSeletor() { + InitializeComponent(); + + this.dialog.FileOk += delegate (object? sender, CancelEventArgs e) { + System.Diagnostics.Debug.Print(dialog.FileName); + + if (this.callback != null) { + this.callback(dialog.FileName); + } + + this.Hide(); + }; + } + + public async void OnChange(object? sender, EventArgs e) { + DeviceAvailableEventArgs b = (DeviceAvailableEventArgs) ((object) e); + SsdpDevice? device = (SsdpDevice?) sender; + SsdpDevice fullDevice = await b.DiscoveredDevice.GetDeviceInfo(); + + //System.Diagnostics.Debug.Print("FriendlyName: " + fullDevice.FriendlyName); + //System.Diagnostics.Debug.Print("SerialNumber: " + fullDevice.SerialNumber); + //System.Diagnostics.Debug.Print("Found " + b.DiscoveredDevice.Usn + " at " + b.DiscoveredDevice.DescriptionLocation.ToString() + ": " + device.PresentationUrl.ToString()); + + Dispatcher.BeginInvoke(new Action(() => { + Grid entry = new Grid() { + HorizontalAlignment = HorizontalAlignment.Stretch, + Name = "DeviceEntry", + RowDefinitions = { + new RowDefinition() { + Height = new GridLength(20,GridUnitType.Pixel) + }, + new RowDefinition() { + Height = new GridLength(0,GridUnitType.Auto) + } + }, + ColumnDefinitions = { + new ColumnDefinition() { + Width = new GridLength(50,GridUnitType.Pixel) + }, + new ColumnDefinition() { + Width = new GridLength(0,GridUnitType.Auto) + } + } + }; + + /* Hostname */ + TextBlock hostname = new TextBlock() { + Text = device?.PresentationUrl.ToString(), + FontSize = 12, + Foreground = new SolidColorBrush((Color) ColorConverter.ConvertFromString("#444444")) + }; + entry.Children.Add(hostname); + Grid.SetRow(hostname, 0); + Grid.SetColumn(hostname, 1); + + /* Name */ + TextBlock name = new TextBlock() { + Text = device?.FriendlyName, + FontSize = 16, + Foreground = new SolidColorBrush((Color) ColorConverter.ConvertFromString("#FFFFFF")) + }; + entry.Children.Add(name); + Grid.SetRow(name, 1); + Grid.SetColumn(name, 1); + + /* Icon */ + Image icon = new Image() { + Width = 40, + VerticalAlignment = VerticalAlignment.Center, + Source = (ImageSource) this.TryFindResource("IconFritzBox") + }; + entry.Children.Add(icon); + Grid.SetRow(icon, 0); + Grid.SetColumn(icon, 0); + Grid.SetRowSpan(icon, 2); + + Button button = new Button() { + Name = "Device", + HorizontalContentAlignment = HorizontalAlignment.Stretch, + Padding = new Thickness(0), + BorderThickness = new Thickness(0), + Style = (Style) this.TryFindResource("DeviceBox"), + Content = entry + }; + + button.PreviewMouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e) { + this.OnDeviceSelect(device?.PresentationUrl.ToString()); + }; + + this.Devices.Children.Add(button); + })).Wait(); + } + + private void OnDeviceSelect(String? hostname) { + if(hostname == null) { + // @ToDo Error window: Unknown input + return; + } + + if(this.callback == null) { + return; + } + + this.callback(hostname); + } + + public void Show(Action callback) { + base.Show(); + this.Focus(); + this.callback = callback; + } + + public void OnReady(Action callback) {} + + private void OpenFile(object sender, RoutedEventArgs e) { + dialog.ShowDialog(); + } + } +}