Initial commit

This commit is contained in:
Disassembler 2019-05-30 15:29:46 +02:00
parent 024c2d9a5b
commit be85aeff52
72 changed files with 5478 additions and 0 deletions

25
RCM.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.572
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCM", "RCM\RCM.csproj", "{F5912EF4-EDC9-421D-B474-4CDADB121E55}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5912EF4-EDC9-421D-B474-4CDADB121E55}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {144EB07E-AE7D-4681-B9C9-DB12547017F3}
EndGlobalSection
EndGlobal

82
RCM/Config.cs Normal file
View File

@ -0,0 +1,82 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
namespace RCM {
public static class Config {
private static readonly char[] _equals = new char[] { '=' };
private static readonly char[] _brackets = new char[] { '[', ']' };
private static readonly string _fileName = "RCM.conf";
private static Icon _emptyIcon;
public static ImageList IconList { get; } = new ImageList() { ColorDepth = ColorDepth.Depth32Bit, ImageSize = new Size(20, 20) };
public static List<Type> DataSourceTypes { get; } = new List<Type>();
public static List<Type> RecordTypes { get; } = new List<Type>();
public static List<IDataSource> DataSources { get; } = new List<IDataSource>();
public static Dictionary<Type, IConfigSection> ConfigSections { get; } = new Dictionary<Type, IConfigSection>();
private static List<JObject> _uknownSections = new List<JObject>();
public static void LoadModules() {
_emptyIcon = (Icon)new ComponentResourceManager(typeof(Config)).GetObject("EmptyIcon");
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes().Where(type => !type.IsInterface);
foreach (Type type in types) {
if (typeof(IDataSource).IsAssignableFrom(type)) {
DataSourceTypes.Add(type);
AddIconForType(type);
} else if (typeof(IRecord).IsAssignableFrom(type)) {
RecordTypes.Add(type);
AddIconForType(type);
} else if (typeof(IConfigSection).IsAssignableFrom(type)) {
ConfigSections[type] = (IConfigSection)Activator.CreateInstance(type);
}
}
}
public static void AddIconForType(Type type) {
try {
IconList.Images.Add(type.FullName, (Icon)new ComponentResourceManager(type).GetObject("Icon"));
} catch {
IconList.Images.Add(type.FullName, _emptyIcon);
}
}
public static void LoadConfiguration() {
if (File.Exists(_fileName)) {
JArray json = JArray.Parse(File.ReadAllText(_fileName));
foreach (JObject section in json) {
try {
LoadSection(section);
} catch {
_uknownSections.Add(section);
}
}
}
}
private static void LoadSection(JObject section) {
object obj = ConfigSerializer.JsonToObj(section);
if (obj is IDataSource dataSource) {
DataSources.Add(dataSource);
} else if (obj is IConfigSection configSection) {
ConfigSections[configSection.GetType()] = configSection;
}
}
public static void Save() {
JArray json = new JArray(DataSources.Select(dataSource => ConfigSerializer.ObjToJson(dataSource))
.Union(ConfigSections.Values.Select(configSection => ConfigSerializer.ObjToJson(configSection)))
.Union(_uknownSections));
File.WriteAllText(_fileName, ConfigSerializer.JsonToString(json));
}
}
}

124
RCM/Config.resx Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="EmptyIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Empty.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

167
RCM/ConfigForm.Designer.cs generated Normal file
View File

@ -0,0 +1,167 @@
namespace RCM {
partial class ConfigForm {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.treeView = new System.Windows.Forms.TreeView();
this.tabControl = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.panelDataSource = new System.Windows.Forms.Panel();
this.buttonSave = new System.Windows.Forms.Button();
this.textBoxTitle = new System.Windows.Forms.TextBox();
this.labelTitle = new System.Windows.Forms.Label();
this.tabControl.SuspendLayout();
this.tabPage1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.SuspendLayout();
//
// treeView
//
this.treeView.Dock = System.Windows.Forms.DockStyle.Fill;
this.treeView.LabelEdit = true;
this.treeView.Location = new System.Drawing.Point(0, 0);
this.treeView.Name = "treeView";
this.treeView.ShowRootLines = false;
this.treeView.Size = new System.Drawing.Size(239, 464);
this.treeView.TabIndex = 0;
this.treeView.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.TreeView_AfterLabelEdit);
this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterSelect);
this.treeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.TreeView_NodeMouseClick);
this.treeView.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TreeView_KeyDown);
this.treeView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TreeView_MouseUp);
//
// tabControl
//
this.tabControl.Controls.Add(this.tabPage1);
this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl.Location = new System.Drawing.Point(0, 0);
this.tabControl.Name = "tabControl";
this.tabControl.SelectedIndex = 0;
this.tabControl.Size = new System.Drawing.Size(574, 496);
this.tabControl.TabIndex = 1;
//
// tabPage1
//
this.tabPage1.BackColor = System.Drawing.SystemColors.Control;
this.tabPage1.Controls.Add(this.splitContainer1);
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(566, 470);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Data Sources";
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(3, 3);
this.splitContainer1.Name = "splitContainer1";
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.treeView);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.panelDataSource);
this.splitContainer1.Panel2.Controls.Add(this.buttonSave);
this.splitContainer1.Panel2.Controls.Add(this.textBoxTitle);
this.splitContainer1.Panel2.Controls.Add(this.labelTitle);
this.splitContainer1.Size = new System.Drawing.Size(560, 464);
this.splitContainer1.SplitterDistance = 239;
this.splitContainer1.TabIndex = 1;
//
// panelDataSource
//
this.panelDataSource.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelDataSource.Location = new System.Drawing.Point(10, 53);
this.panelDataSource.Name = "panelDataSource";
this.panelDataSource.Size = new System.Drawing.Size(307, 373);
this.panelDataSource.TabIndex = 19;
//
// buttonSave
//
this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.buttonSave.Enabled = false;
this.buttonSave.Location = new System.Drawing.Point(131, 432);
this.buttonSave.Name = "buttonSave";
this.buttonSave.Size = new System.Drawing.Size(175, 23);
this.buttonSave.TabIndex = 18;
this.buttonSave.Text = "Save changes";
this.buttonSave.UseVisualStyleBackColor = true;
this.buttonSave.Click += new System.EventHandler(this.ButtonSave_Click);
//
// textBoxTitle
//
this.textBoxTitle.Enabled = false;
this.textBoxTitle.Location = new System.Drawing.Point(130, 9);
this.textBoxTitle.Name = "textBoxTitle";
this.textBoxTitle.Size = new System.Drawing.Size(175, 20);
this.textBoxTitle.TabIndex = 17;
//
// labelTitle
//
this.labelTitle.AutoSize = true;
this.labelTitle.Location = new System.Drawing.Point(10, 12);
this.labelTitle.Name = "labelTitle";
this.labelTitle.Size = new System.Drawing.Size(30, 13);
this.labelTitle.TabIndex = 16;
this.labelTitle.Text = "Title:";
//
// ConfigForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(574, 496);
this.Controls.Add(this.tabControl);
this.Name = "ConfigForm";
this.Text = "Configuration - Remote Connection Manager";
this.tabControl.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
this.splitContainer1.Panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TreeView treeView;
private System.Windows.Forms.TabControl tabControl;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.Panel panelDataSource;
private System.Windows.Forms.Button buttonSave;
private System.Windows.Forms.TextBox textBoxTitle;
private System.Windows.Forms.Label labelTitle;
}
}

182
RCM/ConfigForm.cs Normal file
View File

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
public partial class ConfigForm : Form {
#region Form
protected override CreateParams CreateParams { // Workaround for control flickering reduction
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return cp;
}
}
public ConfigForm() {
InitializeComponent();
treeView.ImageList = Config.IconList;
treeView.ContextMenuStrip = GetNewDataSourceContextMenu();
foreach (IDataSource dataSource in Config.DataSources) {
string dataSourceType = dataSource.GetType().FullName;
treeView.Nodes.Add(CreateTreeNodeFromDataSource(dataSource));
SortTreeAndSelectNode(null);
}
foreach (IConfigSection configSection in Config.ConfigSections.Values) {
TabPage tabPage = new TabPage(configSection.GetType().GetDisplayName());
tabPage.Controls.Add(new ConfigFormTab(configSection));
tabControl.TabPages.Add(tabPage);
}
}
#endregion
#region TreeView display
private TreeNode CreateTreeNodeFromDataSource(IDataSource dataSource) {
string imageKey = dataSource.GetType().FullName;
return new TreeNode(dataSource.Title) { Tag = dataSource, ImageKey = imageKey, SelectedImageKey = imageKey, ContextMenuStrip = GetContextMenu(dataSource) };
}
private void SortTreeAndSelectNode(TreeNode node) {
treeView.Sort();
if (treeView.SelectedNode == node) {
ChangeSelectedDataSource();
} else {
treeView.SelectedNode = node;
}
}
#endregion
#region TreeView navigation & selection
private void TreeView_AfterSelect(object sender, TreeViewEventArgs e) => ChangeSelectedDataSource();
private void TreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) => treeView.SelectedNode = e.Node;
private void TreeView_MouseUp(object sender, MouseEventArgs e) {
if (treeView.HitTest(e.X, e.Y).Node == null) {
treeView.SelectedNode = null;
ChangeSelectedDataSource();
}
}
private void TreeView_KeyDown(object sender, KeyEventArgs e) {
if (treeView.SelectedNode == null) {
return;
}
if (e.KeyCode == Keys.F2) {
treeView.SelectedNode.BeginEdit();
} else if (e.KeyCode == Keys.Delete) {
DeleteSelectedDataSource();
}
}
private void ChangeSelectedDataSource() {
bool isSelected = treeView.SelectedNode != null;
textBoxTitle.Enabled = isSelected;
textBoxTitle.Text = "";
buttonSave.Enabled = isSelected;
LoadDataSourceControl();
}
private void LoadDataSourceControl() {
foreach (Control control in panelDataSource.Controls) {
panelDataSource.Controls.Remove(control);
control.Dispose();
}
if (treeView.SelectedNode != null) {
IDataSource dataSource = (IDataSource)treeView.SelectedNode.Tag;
textBoxTitle.Text = dataSource.Title;
panelDataSource.Controls.Add(dataSource.GetControl());
}
}
#endregion
#region DataSource create/update/delete
public delegate void AfterLabelEditWorkaround(TreeNode node);
private void TreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) {
if (e.Label == null) {
e.CancelEdit = true;
return;
}
IDataSource dataSource = (IDataSource)e.Node.Tag;
dataSource.Title = e.Label;
Config.Save();
BeginInvoke(new AfterLabelEditWorkaround(SortTreeAndSelectNode), new object[] { e.Node }); // Workaround for treeView.Sort() triggering relabel on every sorted node. See note in https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.listview.afterlabeledit?view=netframework-4.6
}
private void ButtonSave_Click(object sender, EventArgs e) {
TreeNode selectedNode = treeView.SelectedNode;
IDataSource dataSource = (IDataSource)selectedNode.Tag;
dataSource.UpdateFromControl();
dataSource.Title = selectedNode.Text = textBoxTitle.Text;
Config.Save();
SortTreeAndSelectNode(selectedNode);
}
private void NewDataSourceContextMenuItem_Click(object sender, EventArgs e) {
Type type = (Type)((ToolStripMenuItem)sender).Tag;
IDataSource dataSource = (IDataSource)Activator.CreateInstance(type);
dataSource.Title = string.Format("New {0}", type.GetDisplayName());
InsertNewDataSource(dataSource);
}
private void DuplicateDataSourceContextMenuItem_Click(object sender, EventArgs e) {
IDataSource source = (IDataSource)treeView.SelectedNode.Tag;
IDataSource dataSource = (IDataSource)ConfigSerializer.JsonToObj(ConfigSerializer.ObjToJson(source));
dataSource.Title = string.Format("{0} - Copy", dataSource.Title);
InsertNewDataSource(dataSource);
}
private void InsertNewDataSource(IDataSource dataSource) {
Config.DataSources.Add(dataSource);
Config.Save();
TreeNode node = CreateTreeNodeFromDataSource(dataSource);
treeView.Nodes.Add(node);
SortTreeAndSelectNode(node);
}
private void DeleteSelectedDataSource() {
IDataSource dataSource = (IDataSource)treeView.SelectedNode.Tag;
if (MessageBox.Show("Do you really want to delete selected data source?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
Config.DataSources.Remove(dataSource);
Config.Save();
treeView.SelectedNode.Remove();
SortTreeAndSelectNode(null);
}
}
private void DeleteDataSourceContextMenuItem_Click(object sender, EventArgs e) => DeleteSelectedDataSource();
#endregion
#region DataSource ContextMenu
private ContextMenuStrip GetNewDataSourceContextMenu() {
ContextMenuStrip menu = new ContextMenuStrip();
ToolStripMenuItem menuItem = new ToolStripMenuItem("New item");
foreach (Type type in Config.DataSourceTypes) {
ToolStripMenuItem item = new ToolStripMenuItem(type.GetDisplayName()) { Tag = type };
item.Click += NewDataSourceContextMenuItem_Click;
menuItem.DropDownItems.Add(item);
}
menu.Items.Add(menuItem);
return menu;
}
private ContextMenuStrip GetContextMenu(IDataSource dataSource) {
ContextMenuStrip menu = new ContextMenuStrip();
ToolStripMenuItem duplicateItem = new ToolStripMenuItem("Duplicate item");
duplicateItem.Click += DuplicateDataSourceContextMenuItem_Click;
menu.Items.Add(duplicateItem);
ToolStripMenuItem deleteItem = new ToolStripMenuItem("Delete item");
deleteItem.Click += DeleteDataSourceContextMenuItem_Click;
menu.Items.Add(deleteItem);
return menu;
}
#endregion
}
}

120
RCM/ConfigForm.resx Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

67
RCM/ConfigFormTab.Designer.cs generated Normal file
View File

@ -0,0 +1,67 @@
namespace RCM {
partial class ConfigFormTab {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.buttonSave = new System.Windows.Forms.Button();
this.panel = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// buttonSave
//
this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.buttonSave.Location = new System.Drawing.Point(371, 429);
this.buttonSave.Name = "buttonSave";
this.buttonSave.Size = new System.Drawing.Size(175, 23);
this.buttonSave.TabIndex = 19;
this.buttonSave.Text = "Save changes";
this.buttonSave.Click += new System.EventHandler(this.ButtonSave_Click);
//
// panel
//
this.panel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel.Location = new System.Drawing.Point(3, 3);
this.panel.Name = "panel";
this.panel.Size = new System.Drawing.Size(554, 412);
this.panel.TabIndex = 20;
//
// ConfigFormTab
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.panel);
this.Controls.Add(this.buttonSave);
this.Name = "ConfigFormTab";
this.Size = new System.Drawing.Size(560, 464);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button buttonSave;
public System.Windows.Forms.Panel panel;
}
}

27
RCM/ConfigFormTab.cs Normal file
View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
public partial class ConfigFormTab : UserControl {
private IConfigSection _configSection;
public ConfigFormTab(IConfigSection configSection) {
InitializeComponent();
Dock = DockStyle.Fill;
_configSection = configSection;
panel.Controls.Add(configSection.GetControl());
}
private void ButtonSave_Click(object sender, EventArgs e) {
_configSection.UpdateFromControl();
Config.Save();
}
}
}

120
RCM/ConfigFormTab.resx Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

90
RCM/ConfigSerializer.cs Normal file
View File

@ -0,0 +1,90 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace RCM {
public static class ConfigSerializer {
private static readonly string _typeProp = "SerializedType";
public static JObject ObjToJson(object obj) {
JObject json = JObject.FromObject(obj);
json.Add(_typeProp, obj.GetType().FullName);
return json;
}
public static object JsonToObj(JObject json) {
Type type = Type.GetType(json.GetValue(_typeProp).ToString(), true);
json.Remove(_typeProp);
return json.ToObject(type);
}
public static string JsonToString(JToken json) {
var stringWriter = new StringWriter();
var jsonWriter = new JsonTextWriter(stringWriter) { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii };
new JsonSerializer().Serialize(jsonWriter, json);
return stringWriter.ToString();
}
public static JObject StringToJson(string str) {
return JObject.Parse(str);
}
public static string ObjToString(object obj) {
return JsonToString(ObjToJson(obj));
}
public static object StringToObj(string str) {
return JsonToObj(StringToJson(str));
}
public static byte[] Decrypt(byte[] cipherbytes, string password) {
if (string.IsNullOrEmpty(password)) {
return cipherbytes;
}
byte[] salt = new byte[16];
byte[] iv = new byte[16];
Array.Copy(cipherbytes, salt, 16);
Array.Copy(cipherbytes, 16, iv, 0, 16);
using (AesManaged aes = new AesManaged() { KeySize = 128 })
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt, 1024))
using (ICryptoTransform decryptor = aes.CreateDecryptor(pbkdf2.GetBytes(16), iv))
using (MemoryStream memoryStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
using (BinaryWriter writer = new BinaryWriter(cryptoStream)) {
writer.Write(cipherbytes, 32, cipherbytes.Length - 32);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
}
public static object DecryptObject(byte[] cipherbytes, string password) {
return StringToObj(Encoding.ASCII.GetString(Decrypt(cipherbytes, password)));
}
public static byte[] Encrypt(byte[] plaintext, string password) {
if (string.IsNullOrEmpty(password)) {
return plaintext;
}
using (AesManaged aes = new AesManaged() { KeySize = 128 }) {
aes.GenerateIV();
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, 16, 1024))
using (ICryptoTransform encryptor = aes.CreateEncryptor(pbkdf2.GetBytes(16), aes.IV))
using (MemoryStream memoryStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
using (BinaryWriter writer = new BinaryWriter(cryptoStream)) {
writer.Write(plaintext);
cryptoStream.FlushFinalBlock();
return pbkdf2.Salt.Concat(aes.IV).Concat(memoryStream.ToArray()).ToArray();
}
}
}
public static byte[] EncryptObject(object obj, string password) {
return Encrypt(Encoding.ASCII.GetBytes(ObjToString(obj)), password);
}
}
}

View File

@ -0,0 +1,146 @@
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace RCM {
[DisplayName("MySQL/MariaDB")]
[Serializable]
public class MySQL : IDataSource {
public string Title { get; set; }
public HashSet<Guid> ExpandedNodes { get; } = new HashSet<Guid>();
public string Host { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Database { get; set; }
public string AESPassword { get; set; }
[NonSerialized]
private MySqlConnection _connection;
[NonSerialized]
private MySQLControl _control;
public override string ToString() => Title;
private MySqlConnection CreateConnection(string host, string username, string password, string database) {
string[] hostport = host.Split(':');
string port = hostport.Length > 1 ? hostport[1] : "3306";
return new MySqlConnection(string.Format("server={0};port={1};user={2};password={3};database={4}", hostport[0], port, username, password, database));
}
private void OpenConnection() {
if (_connection == null) {
_connection = CreateConnection(Host, Username, Password, Database);
}
_connection.Open();
}
public Dictionary<Guid, IRecord> SelectAll() {
Dictionary<Guid, IRecord> records = new Dictionary<Guid, IRecord>();
OpenConnection();
using (MySqlCommand command = new MySqlCommand("SELECT `data`, LENGTH(`data`) AS `datalength` FROM `data`", _connection))
using (MySqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
int dataBytesLength = reader.GetInt32(1);
byte[] dataBytes = new byte[dataBytesLength];
reader.GetBytes(0, 0, dataBytes, 0, dataBytesLength);
IRecord record = (IRecord)ConfigSerializer.DecryptObject(dataBytes, AESPassword);
records.Add(record.Id, record);
}
}
_connection.Close();
return records;
}
public void Insert(IRecord record) {
OpenConnection();
using (MySqlCommand command = new MySqlCommand("INSERT INTO `data` (`id`, `group`, `data`) VALUES (@id, @group, @data)", _connection)) {
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
if (record.GroupId == Guid.Empty) {
command.Parameters.AddWithValue("@group", null);
} else {
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
}
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
command.ExecuteNonQuery();
}
_connection.Close();
}
public void Update(IRecord record) {
OpenConnection();
using (MySqlCommand command = new MySqlCommand("UPDATE `data` SET `group` = @group, `data` = @data WHERE `id` = @id", _connection)) {
if (record.GroupId == Guid.Empty) {
command.Parameters.AddWithValue("@group", null);
} else {
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
}
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
command.ExecuteNonQuery();
}
_connection.Close();
}
public void Delete(IRecord record) {
OpenConnection();
using (MySqlCommand command = new MySqlCommand("DELETE FROM `data` WHERE `id` = @id", _connection)) {
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
command.ExecuteNonQuery();
}
_connection.Close();
}
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new MySQLControl(this);
}
return _control;
}
public void UpdateFromControl() {
Host = _control.textBoxHostAddress.Text;
Username = _control.textBoxUsername.Text;
Password = _control.textBoxPassword.Text;
Database = _control.textBoxDatabase.Text;
AESPassword = _control.textBoxEncPassword.Text;
_connection = null;
}
public bool SchemaExists(string host, string username, string password, string database) {
using (MySqlConnection tempConnection = CreateConnection(host, username, password, database)) {
tempConnection.Open();
using (MySqlCommand command = new MySqlCommand("SHOW TABLES LIKE \"data\"", tempConnection))
using (MySqlDataReader reader = command.ExecuteReader()) {
return reader.HasRows;
}
}
}
public void CreateSchema(string host, string username, string password, string database) {
using (MySqlConnection tempConnection = CreateConnection(host, username, password, database)) {
tempConnection.Open();
using (MySqlCommand command = new MySqlCommand(@"DROP TABLE IF EXISTS `data`;
CREATE TABLE `data` (
`id` binary(16) NOT NULL,
`group` binary(16),
`data` blob NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`group`) REFERENCES `data`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;", tempConnection)) {
command.ExecuteNonQuery();
}
}
}
public string TestConnection(string host, string username, string password, string database) {
using (MySqlConnection tempConnection = CreateConnection(host, username, password, database)) {
tempConnection.Open();
using (MySqlCommand command = new MySqlCommand("SELECT VERSION()", tempConnection)) {
return (string)command.ExecuteScalar();
}
}
}
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\..\Resources\MySQL.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -0,0 +1,206 @@
namespace RCM {
partial class MySQLControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.buttonUnmaskPassword = new System.Windows.Forms.Button();
this.textBoxPassword = new System.Windows.Forms.TextBox();
this.textBoxUsername = new System.Windows.Forms.TextBox();
this.textBoxHostAddress = new System.Windows.Forms.TextBox();
this.labelPassword = new System.Windows.Forms.Label();
this.labelUsername = new System.Windows.Forms.Label();
this.labelHostAddress = new System.Windows.Forms.Label();
this.buttonUnmaskEncPassword = new System.Windows.Forms.Button();
this.textBoxEncPassword = new System.Windows.Forms.TextBox();
this.textBoxDatabase = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.buttonTestConnection = new System.Windows.Forms.Button();
this.buttonCreateSchema = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// buttonUnmaskPassword
//
this.buttonUnmaskPassword.Location = new System.Drawing.Point(273, 51);
this.buttonUnmaskPassword.Name = "buttonUnmaskPassword";
this.buttonUnmaskPassword.Size = new System.Drawing.Size(22, 22);
this.buttonUnmaskPassword.TabIndex = 4;
this.buttonUnmaskPassword.Text = "●";
this.buttonUnmaskPassword.UseVisualStyleBackColor = true;
this.buttonUnmaskPassword.Click += new System.EventHandler(this.ButtonUnmaskPassword_Click);
//
// textBoxPassword
//
this.textBoxPassword.Location = new System.Drawing.Point(120, 52);
this.textBoxPassword.Name = "textBoxPassword";
this.textBoxPassword.Size = new System.Drawing.Size(147, 20);
this.textBoxPassword.TabIndex = 3;
this.textBoxPassword.UseSystemPasswordChar = true;
//
// textBoxUsername
//
this.textBoxUsername.Location = new System.Drawing.Point(120, 26);
this.textBoxUsername.Name = "textBoxUsername";
this.textBoxUsername.Size = new System.Drawing.Size(175, 20);
this.textBoxUsername.TabIndex = 2;
//
// textBoxHostAddress
//
this.textBoxHostAddress.Location = new System.Drawing.Point(120, 0);
this.textBoxHostAddress.Name = "textBoxHostAddress";
this.textBoxHostAddress.Size = new System.Drawing.Size(175, 20);
this.textBoxHostAddress.TabIndex = 1;
//
// labelPassword
//
this.labelPassword.AutoSize = true;
this.labelPassword.Location = new System.Drawing.Point(0, 55);
this.labelPassword.Name = "labelPassword";
this.labelPassword.Size = new System.Drawing.Size(56, 13);
this.labelPassword.TabIndex = 14;
this.labelPassword.Text = "Password:";
//
// labelUsername
//
this.labelUsername.AutoSize = true;
this.labelUsername.Location = new System.Drawing.Point(0, 29);
this.labelUsername.Name = "labelUsername";
this.labelUsername.Size = new System.Drawing.Size(58, 13);
this.labelUsername.TabIndex = 13;
this.labelUsername.Text = "Username:";
//
// labelHostAddress
//
this.labelHostAddress.AutoSize = true;
this.labelHostAddress.Location = new System.Drawing.Point(0, 3);
this.labelHostAddress.Name = "labelHostAddress";
this.labelHostAddress.Size = new System.Drawing.Size(72, 13);
this.labelHostAddress.TabIndex = 12;
this.labelHostAddress.Text = "Host address:";
//
// buttonUnmaskEncPassword
//
this.buttonUnmaskEncPassword.Location = new System.Drawing.Point(273, 104);
this.buttonUnmaskEncPassword.Name = "buttonUnmaskEncPassword";
this.buttonUnmaskEncPassword.Size = new System.Drawing.Size(22, 22);
this.buttonUnmaskEncPassword.TabIndex = 17;
this.buttonUnmaskEncPassword.Text = "●";
this.buttonUnmaskEncPassword.UseVisualStyleBackColor = true;
this.buttonUnmaskEncPassword.Click += new System.EventHandler(this.ButtonUnmaskEncPassword_Click);
//
// textBoxEncPassword
//
this.textBoxEncPassword.Location = new System.Drawing.Point(120, 105);
this.textBoxEncPassword.Name = "textBoxEncPassword";
this.textBoxEncPassword.Size = new System.Drawing.Size(147, 20);
this.textBoxEncPassword.TabIndex = 16;
this.textBoxEncPassword.UseSystemPasswordChar = true;
//
// textBoxDatabase
//
this.textBoxDatabase.Location = new System.Drawing.Point(120, 79);
this.textBoxDatabase.Name = "textBoxDatabase";
this.textBoxDatabase.Size = new System.Drawing.Size(175, 20);
this.textBoxDatabase.TabIndex = 15;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(0, 108);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(108, 13);
this.label1.TabIndex = 19;
this.label1.Text = "Encryption password:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(0, 82);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(56, 13);
this.label2.TabIndex = 18;
this.label2.Text = "Database:";
//
// buttonTestConnection
//
this.buttonTestConnection.Location = new System.Drawing.Point(120, 132);
this.buttonTestConnection.Name = "buttonTestConnection";
this.buttonTestConnection.Size = new System.Drawing.Size(175, 23);
this.buttonTestConnection.TabIndex = 20;
this.buttonTestConnection.Text = "Test connection";
this.buttonTestConnection.UseVisualStyleBackColor = true;
this.buttonTestConnection.Click += new System.EventHandler(this.ButtonTestConnection_Click);
//
// buttonCreateSchema
//
this.buttonCreateSchema.Location = new System.Drawing.Point(120, 161);
this.buttonCreateSchema.Name = "buttonCreateSchema";
this.buttonCreateSchema.Size = new System.Drawing.Size(175, 23);
this.buttonCreateSchema.TabIndex = 21;
this.buttonCreateSchema.Text = "Create schema";
this.buttonCreateSchema.UseVisualStyleBackColor = true;
this.buttonCreateSchema.Click += new System.EventHandler(this.ButtonCreateSchema_Click);
//
// MySQLControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.buttonCreateSchema);
this.Controls.Add(this.buttonTestConnection);
this.Controls.Add(this.buttonUnmaskEncPassword);
this.Controls.Add(this.textBoxEncPassword);
this.Controls.Add(this.textBoxDatabase);
this.Controls.Add(this.label1);
this.Controls.Add(this.label2);
this.Controls.Add(this.buttonUnmaskPassword);
this.Controls.Add(this.textBoxPassword);
this.Controls.Add(this.textBoxUsername);
this.Controls.Add(this.textBoxHostAddress);
this.Controls.Add(this.labelPassword);
this.Controls.Add(this.labelUsername);
this.Controls.Add(this.labelHostAddress);
this.Name = "MySQLControl";
this.Size = new System.Drawing.Size(300, 190);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button buttonUnmaskPassword;
private System.Windows.Forms.Label labelPassword;
private System.Windows.Forms.Label labelUsername;
private System.Windows.Forms.Label labelHostAddress;
public System.Windows.Forms.TextBox textBoxHostAddress;
public System.Windows.Forms.TextBox textBoxUsername;
public System.Windows.Forms.TextBox textBoxPassword;
private System.Windows.Forms.Button buttonUnmaskEncPassword;
public System.Windows.Forms.TextBox textBoxEncPassword;
public System.Windows.Forms.TextBox textBoxDatabase;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button buttonTestConnection;
private System.Windows.Forms.Button buttonCreateSchema;
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Windows.Forms;
namespace RCM {
public partial class MySQLControl : UserControl {
private MySQL _mySQL;
public MySQLControl(MySQL mySQL) {
InitializeComponent();
_mySQL = mySQL;
textBoxHostAddress.Text = mySQL.Host;
textBoxUsername.Text = mySQL.Username;
textBoxPassword.Text = mySQL.Password;
textBoxDatabase.Text = mySQL.Database;
textBoxEncPassword.Text = mySQL.AESPassword;
}
private void ButtonUnmaskPassword_Click(object sender, EventArgs e) {
textBoxPassword.UseSystemPasswordChar = !textBoxPassword.UseSystemPasswordChar;
}
private void ButtonTestConnection_Click(object sender, EventArgs e) {
try {
string version = _mySQL.TestConnection(textBoxHostAddress.Text, textBoxUsername.Text, textBoxPassword.Text, textBoxDatabase.Text);
MessageBox.Show(version, "Test successful", MessageBoxButtons.OK, MessageBoxIcon.Information);
} catch (Exception ex) {
MessageBox.Show(ex.Message, "Test failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ButtonCreateSchema_Click(object sender, EventArgs e) {
bool schemaExists = _mySQL.SchemaExists(textBoxHostAddress.Text, textBoxUsername.Text, textBoxPassword.Text, textBoxDatabase.Text);
if (!schemaExists || MessageBox.Show("Do you want to drop existing schema and create a new one?", "Schema already exists", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes) {
_mySQL.CreateSchema(textBoxHostAddress.Text, textBoxUsername.Text, textBoxPassword.Text, textBoxDatabase.Text);
MessageBox.Show("New schema successfully created", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void ButtonUnmaskEncPassword_Click(object sender, EventArgs e) {
textBoxEncPassword.UseSystemPasswordChar = !textBoxEncPassword.UseSystemPasswordChar;
}
}
}

View File

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Windows.Forms;
namespace RCM {
[Serializable]
public class SQLite : IDataSource {
public string Title { get; set; }
public HashSet<Guid> ExpandedNodes { get; } = new HashSet<Guid>();
public string FileName { get; set; }
public string AESPassword { get; set; }
[NonSerialized]
private SQLiteConnection _connection;
[NonSerialized]
private SQLiteControl _control;
public override string ToString() => Title;
private SQLiteConnection CreateConnection(string fileName, bool failIfMissing) {
return new SQLiteConnection(string.Format("Data Source={0};Version=3;FailIfMissing={1}", fileName, failIfMissing ? "True" : "False"));
}
private void OpenConnection() {
if (_connection == null) {
_connection = CreateConnection(FileName, true);
}
_connection.Open();
}
public Dictionary<Guid, IRecord> SelectAll() {
Dictionary<Guid, IRecord> records = new Dictionary<Guid, IRecord>();
OpenConnection();
using (SQLiteCommand command = new SQLiteCommand("SELECT \"data\", LENGTH(\"data\") AS \"datalength\" FROM \"data\"", _connection))
using (SQLiteDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
int dataBytesLength = reader.GetInt32(1);
byte[] dataBytes = new byte[dataBytesLength];
reader.GetBytes(0, 0, dataBytes, 0, dataBytesLength);
IRecord record = (IRecord)ConfigSerializer.DecryptObject(dataBytes, AESPassword);
records.Add(record.Id, record);
}
}
_connection.Close();
return records;
}
public void Insert(IRecord record) {
OpenConnection();
EnableForeignKeys();
using (SQLiteCommand command = new SQLiteCommand("INSERT INTO \"data\" (\"id\", \"group\", \"data\") VALUES (@id, @group, @data)", _connection)) {
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
if (record.GroupId == Guid.Empty) {
command.Parameters.AddWithValue("@group", null);
} else {
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
}
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
command.ExecuteNonQuery();
}
_connection.Close();
}
public void Update(IRecord record) {
OpenConnection();
EnableForeignKeys();
using (SQLiteCommand command = new SQLiteCommand("UPDATE \"data\" SET \"group\" = @group, \"data\" = @data WHERE \"id\" = @id", _connection)) {
if (record.GroupId == Guid.Empty) {
command.Parameters.AddWithValue("@group", null);
} else {
command.Parameters.AddWithValue("@group", record.GroupId.ToByteArray());
}
command.Parameters.AddWithValue("@data", ConfigSerializer.EncryptObject(record, AESPassword));
command.Parameters.AddWithValue("@id", record.Id);
command.ExecuteNonQuery();
}
_connection.Close();
}
public void Delete(IRecord record) {
OpenConnection();
EnableForeignKeys();
using (SQLiteCommand command = new SQLiteCommand("DELETE FROM \"data\" WHERE \"id\" = @id", _connection)) {
command.Parameters.AddWithValue("@id", record.Id.ToByteArray());
command.ExecuteNonQuery();
}
_connection.Close();
}
private void EnableForeignKeys() {
using (SQLiteCommand command = new SQLiteCommand("PRAGMA foreign_keys = ON", _connection)) {
command.ExecuteNonQuery();
}
}
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new SQLiteControl(this);
}
return _control;
}
public void UpdateFromControl() {
FileName = _control.textBoxDatabase.Text;
AESPassword = _control.textBoxEncPassword.Text;
_connection = null;
}
public void CreateSchema(string fileName) {
using (SQLiteConnection tempConnection = CreateConnection(fileName, false)) {
tempConnection.Open();
SQLiteCommand command = new SQLiteCommand(@"
DROP TABLE IF EXISTS ""data"";
CREATE TABLE ""data"" (
""id"" binary(16) NOT NULL,
""group"" binary(16),
""data"" blob NOT NULL,
PRIMARY KEY (""id""),
FOREIGN KEY (""group"") REFERENCES ""data""(""id"") ON DELETE CASCADE ON UPDATE CASCADE
);", tempConnection);
command.ExecuteNonQuery();
tempConnection.Close();
}
}
public string TestConnection(string fileName) {
using (SQLiteConnection tempConnection = CreateConnection(fileName, true)) {
tempConnection.Open();
using (SQLiteCommand command = new SQLiteCommand("PRAGMA quick_check", tempConnection)) {
return (string)command.ExecuteScalar();
}
}
}
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\..\Resources\SQLite.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -0,0 +1,146 @@
namespace RCM {
partial class SQLiteControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.buttonSelectDatabase = new System.Windows.Forms.Button();
this.buttonUnmaskEncPassword = new System.Windows.Forms.Button();
this.textBoxEncPassword = new System.Windows.Forms.TextBox();
this.textBoxDatabase = new System.Windows.Forms.TextBox();
this.labelEncPassword = new System.Windows.Forms.Label();
this.labelDatabase = new System.Windows.Forms.Label();
this.buttonTestConnection = new System.Windows.Forms.Button();
this.buttonCreateSchema = new System.Windows.Forms.Button();
this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
this.SuspendLayout();
//
// buttonSelectDatabase
//
this.buttonSelectDatabase.Location = new System.Drawing.Point(273, -1);
this.buttonSelectDatabase.Name = "buttonSelectDatabase";
this.buttonSelectDatabase.Size = new System.Drawing.Size(22, 22);
this.buttonSelectDatabase.TabIndex = 4;
this.buttonSelectDatabase.Text = "⋯";
this.buttonSelectDatabase.UseVisualStyleBackColor = true;
this.buttonSelectDatabase.Click += new System.EventHandler(this.ButtonSelectDatabase_Click);
//
// buttonUnmaskEncPassword
//
this.buttonUnmaskEncPassword.Location = new System.Drawing.Point(273, 25);
this.buttonUnmaskEncPassword.Name = "buttonUnmaskEncPassword";
this.buttonUnmaskEncPassword.Size = new System.Drawing.Size(22, 22);
this.buttonUnmaskEncPassword.TabIndex = 17;
this.buttonUnmaskEncPassword.Text = "●";
this.buttonUnmaskEncPassword.UseVisualStyleBackColor = true;
this.buttonUnmaskEncPassword.Click += new System.EventHandler(this.ButtonUnmaskEncPassword_Click);
//
// textBoxEncPassword
//
this.textBoxEncPassword.Location = new System.Drawing.Point(120, 26);
this.textBoxEncPassword.Name = "textBoxEncPassword";
this.textBoxEncPassword.Size = new System.Drawing.Size(147, 20);
this.textBoxEncPassword.TabIndex = 16;
this.textBoxEncPassword.UseSystemPasswordChar = true;
//
// textBoxDatabase
//
this.textBoxDatabase.Location = new System.Drawing.Point(120, 0);
this.textBoxDatabase.Name = "textBoxDatabase";
this.textBoxDatabase.Size = new System.Drawing.Size(147, 20);
this.textBoxDatabase.TabIndex = 15;
//
// labelEncPassword
//
this.labelEncPassword.AutoSize = true;
this.labelEncPassword.Location = new System.Drawing.Point(0, 29);
this.labelEncPassword.Name = "labelEncPassword";
this.labelEncPassword.Size = new System.Drawing.Size(108, 13);
this.labelEncPassword.TabIndex = 19;
this.labelEncPassword.Text = "Encryption password:";
//
// labelDatabase
//
this.labelDatabase.AutoSize = true;
this.labelDatabase.Location = new System.Drawing.Point(0, 3);
this.labelDatabase.Name = "labelDatabase";
this.labelDatabase.Size = new System.Drawing.Size(72, 13);
this.labelDatabase.TabIndex = 18;
this.labelDatabase.Text = "Database file:";
//
// buttonTestConnection
//
this.buttonTestConnection.Location = new System.Drawing.Point(120, 53);
this.buttonTestConnection.Name = "buttonTestConnection";
this.buttonTestConnection.Size = new System.Drawing.Size(175, 23);
this.buttonTestConnection.TabIndex = 20;
this.buttonTestConnection.Text = "Test connection";
this.buttonTestConnection.UseVisualStyleBackColor = true;
this.buttonTestConnection.Click += new System.EventHandler(this.ButtonTestConnection_Click);
//
// buttonCreateSchema
//
this.buttonCreateSchema.Location = new System.Drawing.Point(120, 82);
this.buttonCreateSchema.Name = "buttonCreateSchema";
this.buttonCreateSchema.Size = new System.Drawing.Size(175, 23);
this.buttonCreateSchema.TabIndex = 21;
this.buttonCreateSchema.Text = "Create schema";
this.buttonCreateSchema.UseVisualStyleBackColor = true;
this.buttonCreateSchema.Click += new System.EventHandler(this.ButtonCreateSchema_Click);
//
// saveFileDialog
//
this.saveFileDialog.DefaultExt = "sqlite";
this.saveFileDialog.Filter = "SQLite database|*.sqlite";
//
// SQLiteControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.buttonCreateSchema);
this.Controls.Add(this.buttonTestConnection);
this.Controls.Add(this.buttonUnmaskEncPassword);
this.Controls.Add(this.textBoxEncPassword);
this.Controls.Add(this.textBoxDatabase);
this.Controls.Add(this.labelEncPassword);
this.Controls.Add(this.labelDatabase);
this.Controls.Add(this.buttonSelectDatabase);
this.Name = "SQLiteControl";
this.Size = new System.Drawing.Size(300, 110);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button buttonSelectDatabase;
private System.Windows.Forms.Button buttonUnmaskEncPassword;
public System.Windows.Forms.TextBox textBoxEncPassword;
public System.Windows.Forms.TextBox textBoxDatabase;
private System.Windows.Forms.Label labelEncPassword;
private System.Windows.Forms.Label labelDatabase;
private System.Windows.Forms.Button buttonTestConnection;
private System.Windows.Forms.Button buttonCreateSchema;
private System.Windows.Forms.SaveFileDialog saveFileDialog;
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.IO;
using System.Windows.Forms;
namespace RCM {
public partial class SQLiteControl : UserControl {
private SQLite _sqLite;
public SQLiteControl(SQLite sqLite) {
InitializeComponent();
_sqLite = sqLite;
textBoxDatabase.Text = _sqLite.FileName;
textBoxEncPassword.Text = _sqLite.AESPassword;
}
private void ButtonSelectDatabase_Click(object sender, EventArgs e) {
if (saveFileDialog.ShowDialog() == DialogResult.OK) {
textBoxDatabase.Text = saveFileDialog.FileName;
}
}
private void ButtonTestConnection_Click(object sender, EventArgs e) {
try {
string version = _sqLite.TestConnection(textBoxDatabase.Text);
MessageBox.Show(version, "Test successful", MessageBoxButtons.OK, MessageBoxIcon.Information);
} catch (Exception ex) {
MessageBox.Show(ex.Message, "Test failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ButtonCreateSchema_Click(object sender, EventArgs e) {
if (File.Exists(textBoxDatabase.Text)) {
if (MessageBox.Show("Do you want to delete existing database and create a new one?", "Database already exists", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) != DialogResult.Yes) {
return;
}
File.Delete(textBoxDatabase.Text);
}
_sqLite.CreateSchema(textBoxDatabase.Text);
MessageBox.Show("New database successfully created", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void ButtonUnmaskEncPassword_Click(object sender, EventArgs e) {
textBoxEncPassword.UseSystemPasswordChar = !textBoxEncPassword.UseSystemPasswordChar;
}
}
}

13
RCM/DisplayName.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace RCM {
public class DisplayName : Attribute {
public string Text { get; private set; }
public DisplayName(string text) {
Text = text;
}
}
}

20
RCM/Group.cs Normal file
View File

@ -0,0 +1,20 @@
using Newtonsoft.Json;
using System;
using System.Windows.Forms;
namespace RCM {
[Serializable]
[DisplayName("Group")]
public class Group : IRecord { // TODO: Prvni v poradi new items
public Guid Id { get; set; }
public string Title { get; set; }
public Guid GroupId { get; set; }
[NonSerialized]
private readonly IAction[] _actions = new IAction[0];
public IAction[] GetActions() => _actions;
public UserControl GetControl() => new UserControl();
public void UpdateFromControl() { }
}
}

124
RCM/Group.resx Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Group.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

23
RCM/GroupPath.cs Normal file
View File

@ -0,0 +1,23 @@
using System;
using System.Windows.Forms;
namespace RCM {
public class GroupPath {
public Guid Id { get; private set; }
public TreeNode Node { get; private set; }
public string FullPath { get; private set; }
public GroupPath(TreeNode node) {
Node = node;
if (node == null) {
Id = Guid.Empty;
FullPath = "Root";
} else {
Id = ((IRecord)node.Tag).Id;
FullPath = node.FullPath;
}
}
public override string ToString() => FullPath;
}
}

5
RCM/IAction.cs Normal file
View File

@ -0,0 +1,5 @@
namespace RCM {
public interface IAction {
void Run();
}
}

9
RCM/IConfigSection.cs Normal file
View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
using System.Windows.Forms;
namespace RCM {
public interface IConfigSection {
UserControl GetControl();
void UpdateFromControl();
}
}

19
RCM/IDataSource.cs Normal file
View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace RCM {
public interface IDataSource {
string Title { get; set; }
HashSet<Guid> ExpandedNodes { get; }
Dictionary<Guid, IRecord> SelectAll();
void Insert(IRecord record);
void Update(IRecord record);
void Delete(IRecord record);
UserControl GetControl();
void UpdateFromControl();
string ToString();
}
}

15
RCM/IRecord.cs Normal file
View File

@ -0,0 +1,15 @@
using System;
using System.Windows.Forms;
namespace RCM {
public interface IRecord {
Guid Id { get; set; }
string Title { get; set; }
Guid GroupId { get; set; }
// DateTime LastUpdate { get; set; }
IAction[] GetActions();
UserControl GetControl();
void UpdateFromControl();
}
}

249
RCM/MainForm.Designer.cs generated Normal file
View File

@ -0,0 +1,249 @@
namespace RCM
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.treeView = new System.Windows.Forms.TreeView();
this.splitContainer = new System.Windows.Forms.SplitContainer();
this.buttonConfig = new System.Windows.Forms.Button();
this.comboBoxDatabase = new System.Windows.Forms.ComboBox();
this.panelRecord = new System.Windows.Forms.Panel();
this.buttonSave = new System.Windows.Forms.Button();
this.labelGroup = new System.Windows.Forms.Label();
this.comboBoxGroup = new System.Windows.Forms.ComboBox();
this.textBoxTitle = new System.Windows.Forms.TextBox();
this.labelTitle = new System.Windows.Forms.Label();
this.timerDataSourceRefresh = new System.Windows.Forms.Timer(this.components);
this.statusStrip = new System.Windows.Forms.StatusStrip();
this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.toolStripStatusLastRefresh = new System.Windows.Forms.ToolStripStatusLabel();
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
this.splitContainer.Panel1.SuspendLayout();
this.splitContainer.Panel2.SuspendLayout();
this.splitContainer.SuspendLayout();
this.statusStrip.SuspendLayout();
this.SuspendLayout();
//
// treeView
//
this.treeView.AllowDrop = true;
this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.treeView.Enabled = false;
this.treeView.Indent = 24;
this.treeView.LabelEdit = true;
this.treeView.Location = new System.Drawing.Point(3, 30);
this.treeView.Name = "treeView";
this.treeView.Size = new System.Drawing.Size(244, 440);
this.treeView.TabIndex = 0;
this.treeView.AfterLabelEdit += new System.Windows.Forms.NodeLabelEditEventHandler(this.TreeView_AfterLabelEdit);
this.treeView.AfterCollapse += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterCollapse);
this.treeView.AfterExpand += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterExpand);
this.treeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.TreeView_ItemDrag);
this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeView_AfterSelect);
this.treeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.TreeView_NodeMouseClick);
this.treeView.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.TreeView_NodeMouseDoubleClick);
this.treeView.DragDrop += new System.Windows.Forms.DragEventHandler(this.TreeView_DragDrop);
this.treeView.DragEnter += new System.Windows.Forms.DragEventHandler(this.TreeView_DragEnter);
this.treeView.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TreeView_KeyDown);
this.treeView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TreeView_MouseUp);
//
// splitContainer
//
this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer.Location = new System.Drawing.Point(0, 0);
this.splitContainer.Name = "splitContainer";
//
// splitContainer.Panel1
//
this.splitContainer.Panel1.Controls.Add(this.buttonConfig);
this.splitContainer.Panel1.Controls.Add(this.treeView);
this.splitContainer.Panel1.Controls.Add(this.comboBoxDatabase);
//
// splitContainer.Panel2
//
this.splitContainer.Panel2.Controls.Add(this.panelRecord);
this.splitContainer.Panel2.Controls.Add(this.buttonSave);
this.splitContainer.Panel2.Controls.Add(this.labelGroup);
this.splitContainer.Panel2.Controls.Add(this.comboBoxGroup);
this.splitContainer.Panel2.Controls.Add(this.textBoxTitle);
this.splitContainer.Panel2.Controls.Add(this.labelTitle);
this.splitContainer.Size = new System.Drawing.Size(574, 496);
this.splitContainer.SplitterDistance = 250;
this.splitContainer.TabIndex = 1;
//
// buttonConfig
//
this.buttonConfig.Image = global::RCM.Properties.Resources.ConfigIcon;
this.buttonConfig.Location = new System.Drawing.Point(3, 1);
this.buttonConfig.Name = "buttonConfig";
this.buttonConfig.Size = new System.Drawing.Size(24, 24);
this.buttonConfig.TabIndex = 2;
this.buttonConfig.UseVisualStyleBackColor = true;
this.buttonConfig.Click += new System.EventHandler(this.ButtonConfig_Click);
//
// comboBoxDatabase
//
this.comboBoxDatabase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxDatabase.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxDatabase.Location = new System.Drawing.Point(33, 3);
this.comboBoxDatabase.Name = "comboBoxDatabase";
this.comboBoxDatabase.Size = new System.Drawing.Size(214, 21);
this.comboBoxDatabase.TabIndex = 1;
this.comboBoxDatabase.SelectedIndexChanged += new System.EventHandler(this.ComboBoxDatabase_SelectedIndexChanged);
//
// panelRecord
//
this.panelRecord.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelRecord.Location = new System.Drawing.Point(11, 82);
this.panelRecord.Name = "panelRecord";
this.panelRecord.Size = new System.Drawing.Size(306, 350);
this.panelRecord.TabIndex = 15;
//
// buttonSave
//
this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.buttonSave.Enabled = false;
this.buttonSave.Location = new System.Drawing.Point(131, 438);
this.buttonSave.Name = "buttonSave";
this.buttonSave.Size = new System.Drawing.Size(175, 23);
this.buttonSave.TabIndex = 14;
this.buttonSave.Text = "Save changes";
this.buttonSave.UseVisualStyleBackColor = true;
this.buttonSave.Click += new System.EventHandler(this.ButtonSave_Click);
//
// labelGroup
//
this.labelGroup.AutoSize = true;
this.labelGroup.Location = new System.Drawing.Point(11, 42);
this.labelGroup.Name = "labelGroup";
this.labelGroup.Size = new System.Drawing.Size(39, 13);
this.labelGroup.TabIndex = 13;
this.labelGroup.Text = "Group:";
//
// comboBoxGroup
//
this.comboBoxGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxGroup.Enabled = false;
this.comboBoxGroup.FormattingEnabled = true;
this.comboBoxGroup.Location = new System.Drawing.Point(131, 39);
this.comboBoxGroup.Name = "comboBoxGroup";
this.comboBoxGroup.Size = new System.Drawing.Size(175, 21);
this.comboBoxGroup.TabIndex = 12;
//
// textBoxTitle
//
this.textBoxTitle.Enabled = false;
this.textBoxTitle.Location = new System.Drawing.Point(131, 13);
this.textBoxTitle.Name = "textBoxTitle";
this.textBoxTitle.Size = new System.Drawing.Size(175, 20);
this.textBoxTitle.TabIndex = 1;
//
// labelTitle
//
this.labelTitle.AutoSize = true;
this.labelTitle.Location = new System.Drawing.Point(11, 16);
this.labelTitle.Name = "labelTitle";
this.labelTitle.Size = new System.Drawing.Size(30, 13);
this.labelTitle.TabIndex = 0;
this.labelTitle.Text = "Title:";
//
// timerDataSourceRefresh
//
this.timerDataSourceRefresh.Interval = 30000;
this.timerDataSourceRefresh.Tick += new System.EventHandler(this.TimerDataSourceRefresh_Tick);
//
// statusStrip
//
this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripStatusLabel,
this.toolStripStatusLastRefresh});
this.statusStrip.Location = new System.Drawing.Point(0, 474);
this.statusStrip.Name = "statusStrip";
this.statusStrip.Size = new System.Drawing.Size(574, 22);
this.statusStrip.TabIndex = 0;
this.statusStrip.Text = "statusStrip1";
//
// toolStripStatusLabel
//
this.toolStripStatusLabel.Name = "toolStripStatusLabel";
this.toolStripStatusLabel.Size = new System.Drawing.Size(134, 17);
this.toolStripStatusLabel.Text = "Last data source refresh:";
//
// toolStripStatusLastRefresh
//
this.toolStripStatusLastRefresh.Name = "toolStripStatusLastRefresh";
this.toolStripStatusLastRefresh.Size = new System.Drawing.Size(36, 17);
this.toolStripStatusLastRefresh.Text = "never";
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(574, 496);
this.Controls.Add(this.statusStrip);
this.Controls.Add(this.splitContainer);
this.Name = "MainForm";
this.Text = "Remote Connection Manager";
this.splitContainer.Panel1.ResumeLayout(false);
this.splitContainer.Panel2.ResumeLayout(false);
this.splitContainer.Panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
this.splitContainer.ResumeLayout(false);
this.statusStrip.ResumeLayout(false);
this.statusStrip.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TreeView treeView;
private System.Windows.Forms.SplitContainer splitContainer;
private System.Windows.Forms.ComboBox comboBoxDatabase;
private System.Windows.Forms.TextBox textBoxTitle;
private System.Windows.Forms.Label labelTitle;
private System.Windows.Forms.Label labelGroup;
private System.Windows.Forms.ComboBox comboBoxGroup;
private System.Windows.Forms.Button buttonSave;
private System.Windows.Forms.Panel panelRecord;
private System.Windows.Forms.Timer timerDataSourceRefresh;
private System.Windows.Forms.Button buttonConfig;
private System.Windows.Forms.StatusStrip statusStrip;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLastRefresh;
}
}

463
RCM/MainForm.cs Normal file
View File

@ -0,0 +1,463 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Linq;
using System.Collections.Specialized;
namespace RCM {
public partial class MainForm : Form {
#region Form
protected override CreateParams CreateParams { // Workaround for control flickering reduction
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return cp;
}
}
public MainForm() {
InitializeComponent();
treeView.ImageList = Config.IconList;
treeView.ContextMenuStrip = GetNewRecordContextMenu();
treeView.TreeViewNodeSorter = new RecordNodeSorter();
LoadDataSources(0);
}
private void LoadDataSources(int selectedIndex) {
comboBoxDatabase.Items.Clear();
comboBoxDatabase.Items.AddRange(Config.DataSources.ToArray());
if (comboBoxDatabase.Items.Count == 0) {
treeView.Enabled = false;
treeView.Nodes.Clear();
} else {
treeView.Enabled = true;
comboBoxDatabase.SelectedIndex = selectedIndex != -1 ? selectedIndex : 0;
}
}
private void ButtonConfig_Click(object sender, EventArgs e) {
int selectedIndex = comboBoxDatabase.SelectedIndex;
ConfigForm configForm = new ConfigForm();
configForm.ShowDialog();
configForm.Dispose();
LoadDataSources(selectedIndex);
}
#endregion
#region ComboBox & DataSource
private void RebuildGroupCombobox(IRecord record) {
List<GroupPath> groupPaths = new List<GroupPath>() { new GroupPath(null) };
groupPaths.AddRange(RebuildGroupCombobox(treeView.Nodes));
comboBoxGroup.Items.Clear();
comboBoxGroup.Items.AddRange(groupPaths.ToArray());
comboBoxGroup.SelectedItem = groupPaths.First(gp => gp.Id == record.GroupId);
}
private List<GroupPath> RebuildGroupCombobox(TreeNodeCollection nodes) {
List<GroupPath> groupPaths = new List<GroupPath>();
foreach (TreeNode node in nodes) {
if ((IRecord)node.Tag is Group && !ReferenceEquals(node, treeView.SelectedNode)) {
groupPaths.Add(new GroupPath(node));
groupPaths.AddRange(RebuildGroupCombobox(node.Nodes));
}
}
return groupPaths;
}
private void ReloadDataSource() {
RebuildTreeView();
timerDataSourceRefresh.Stop();
timerDataSourceRefresh.Start();
}
private void ComboBoxDatabase_SelectedIndexChanged(object sender, EventArgs e) => ReloadDataSource();
private void RefreshContextMenuItem_Click(object sender, EventArgs e) => ReloadDataSource();
private void TimerDataSourceRefresh_Tick(object sender, EventArgs e) {
RebuildTreeView();
}
#endregion
#region TreeView display
/* test */
private void RebuildTreeView() {
// Select all records from database
IDataSource dataSource = (IDataSource)comboBoxDatabase.SelectedItem;
Dictionary<Guid, IRecord> dataSourceRecords = dataSource.SelectAll();
Guid selectedGuid = treeView.SelectedNode == null ? Guid.Empty : ((IRecord)treeView.SelectedNode.Tag).Id;
// Flatten tree nodes
List<TreeNode> flattenedTreeList = new List<TreeNode>();
int lastProcessPosition = 0;
flattenedTreeList.AddRange(treeView.Nodes.Cast<TreeNode>());
while (lastProcessPosition < flattenedTreeList.Count) {
flattenedTreeList.AddRange(flattenedTreeList[lastProcessPosition++].Nodes.Cast<TreeNode>());
}
Dictionary<Guid, TreeNode> flattenedTree = flattenedTreeList.ToDictionary(n => ((IRecord)n.Tag).Id, n => n);
// Iterate over existing nodes
foreach (TreeNode node in flattenedTree.Values) {
IRecord record = (IRecord)node.Tag;
// Delete node if record no longer present in database
if (!dataSourceRecords.ContainsKey(record.Id)) {
node.Remove();
continue;
}
// Update existing record if it has changed since the last refresh
IRecord updatedRecord = dataSourceRecords[record.Id];
if (ConfigSerializer.ObjToString(record) != ConfigSerializer.ObjToString(updatedRecord)) {
node.Tag = updatedRecord;
node.Text = updatedRecord.Title;
if (updatedRecord.GroupId != record.GroupId) {
node.Remove();
if (updatedRecord.GroupId == Guid.Empty) {
treeView.Nodes.Add(node);
} else {
flattenedTree[updatedRecord.GroupId].Nodes.Add(node);
}
}
}
// Remove processed record
dataSourceRecords.Remove(record.Id);
}
// Create nodes from unprocessed (new) records
foreach (IRecord record in dataSourceRecords.Values) {
TreeNode node = CreateTreeNodeFromRecord(record);
if (dataSource.ExpandedNodes.Contains(record.Id)) {
node.Expand();
}
flattenedTree.Add(record.Id, node);
}
// Assign nodes to tree structure
foreach (IRecord record in dataSourceRecords.Values) {
TreeNode node = flattenedTree[record.Id];
if (record.GroupId == Guid.Empty) {
treeView.Nodes.Add(node);
} else {
flattenedTree[record.GroupId].Nodes.Add(node);
}
}
TreeNode selectedNode = flattenedTree.ContainsKey(selectedGuid) ? flattenedTree[selectedGuid] : null;
SortTreeAndSelectNode(selectedNode);
toolStripStatusLastRefresh.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
/* test end */
/*private void RebuildTreeView() {
IDataSource dataSource = (IDataSource)comboBoxDatabase.SelectedItem;
Dictionary<Guid, TreeNode> nodes = new Dictionary<Guid, TreeNode>();
try {
foreach (IRecord record in dataSource.SelectAll()) {
nodes.Add(record.Id, CreateTreeNodeFromRecord(record));
}
} catch {
MessageBox.Show("Failed to retieve records from data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
Guid selectedGuid = treeView.SelectedNode == null ? Guid.Empty : ((IRecord)treeView.SelectedNode.Tag).Id;
TreeNode selectedNode = null;
treeView.Nodes.Clear();
foreach (TreeNode node in nodes.Values) {
IRecord record = (IRecord)node.Tag;
if (record.GroupId == Guid.Empty) {
treeView.Nodes.Add(node);
} else {
nodes[record.GroupId].Nodes.Add(node);
}
if (record.Id == selectedGuid) {
selectedNode = node;
}
}
SortTreeAndSelectNode(selectedNode);
foreach (TreeNode node in nodes.Values) {
if (dataSource.ExpandedNodes.Contains(((IRecord)node.Tag).Id)) {
node.Expand();
}
}
toolStripStatusLastRefresh.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}*/
private TreeNode CreateTreeNodeFromRecord(IRecord record) {
string imageKey = record.GetType().FullName;
return new TreeNode(record.Title) { Tag = record, ImageKey = imageKey, SelectedImageKey = imageKey, ContextMenuStrip = GetContextMenu(record) };
}
private void SortTreeAndSelectNode(TreeNode node) {
treeView.Sort();
if (treeView.SelectedNode == node) {
ChangeSelectedRecord();
} else {
treeView.SelectedNode = node;
}
}
#endregion
#region TreeView drag & drop
private void TreeView_ItemDrag(object sender, ItemDragEventArgs e) => DoDragDrop(e.Item, DragDropEffects.Move);
private void TreeView_DragEnter(object sender, DragEventArgs e) => e.Effect = DragDropEffects.Move;
private void TreeView_DragDrop(object sender, DragEventArgs e) {
TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
if (draggedNode == null) {
return;
}
TreeNode targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
bool canDrop = true;
if (targetNode != null) {
TreeNode parentNode = targetNode;
canDrop = !draggedNode.Equals(targetNode) && targetNode != null && (IRecord)targetNode.Tag is Group;
while (canDrop && (parentNode != null)) {
canDrop = !ReferenceEquals(draggedNode, parentNode);
parentNode = parentNode.Parent;
}
}
if (canDrop) {
IRecord record = (IRecord)draggedNode.Tag;
record.GroupId = targetNode == null ? Guid.Empty : ((IRecord)targetNode.Tag).Id;
try {
((IDataSource)comboBoxDatabase.SelectedItem).Update(record);
} catch {
MessageBox.Show("Failed to update record in data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
draggedNode.Remove();
if (targetNode == null) {
treeView.Nodes.Add(draggedNode);
} else {
targetNode.Nodes.Add(draggedNode);
}
SortTreeAndSelectNode(draggedNode);
}
}
#endregion
#region TreeView navigation & selection
private void TreeView_AfterSelect(object sender, TreeViewEventArgs e) => ChangeSelectedRecord();
private void TreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) => treeView.SelectedNode = e.Node;
private void TreeView_MouseUp(object sender, MouseEventArgs e) {
if (treeView.HitTest(e.X, e.Y).Node == null) {
treeView.SelectedNode = null;
ChangeSelectedRecord();
}
}
private void TreeView_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.Delete) {
if (treeView.SelectedNode != null) {
DeleteSelectedNode();
}
} else if (e.KeyCode == Keys.F2) {
if (treeView.SelectedNode != null) {
treeView.SelectedNode.BeginEdit();
}
} else if (e.KeyCode == Keys.F5) {
ReloadDataSource();
}
}
private void TreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) {
IAction[] actions = ((IRecord)e.Node.Tag).GetActions();
if (actions.Length == 0) {
return;
}
actions[0].Run();
}
private void ChangeSelectedRecord() {
bool isSelected = treeView.SelectedNode != null;
textBoxTitle.Enabled = isSelected;
textBoxTitle.Text = "";
comboBoxGroup.Enabled = isSelected;
comboBoxGroup.SelectedItem = null;
buttonSave.Enabled = isSelected;
LoadRecordControl();
}
private void LoadRecordControl() {
foreach (Control control in panelRecord.Controls) {
panelRecord.Controls.Remove(control);
control.Dispose();
}
if (treeView.SelectedNode != null) {
IRecord record = (IRecord)treeView.SelectedNode.Tag;
textBoxTitle.Text = record.Title;
RebuildGroupCombobox(record);
panelRecord.Controls.Add(record.GetControl());
}
}
private void TreeView_AfterExpand(object sender, TreeViewEventArgs e) {
((IDataSource)comboBoxDatabase.SelectedItem).ExpandedNodes.Add(((IRecord)e.Node.Tag).Id);
}
private void TreeView_AfterCollapse(object sender, TreeViewEventArgs e) {
((IDataSource)comboBoxDatabase.SelectedItem).ExpandedNodes.Remove(((IRecord)e.Node.Tag).Id);
}
#endregion
#region Record create/update/delete
public delegate void AfterLabelEditWorkaround(TreeNode node);
private void TreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) {
if (e.Label != null) {
IRecord record = (IRecord)e.Node.Tag;
record.Title = e.Label;
try {
((IDataSource)comboBoxDatabase.SelectedItem).Update(record);
BeginInvoke(new AfterLabelEditWorkaround(SortTreeAndSelectNode), new object[] { e.Node }); // Workaround for treeView.Sort() triggering relabel on every sorted node. See note in https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.listview.afterlabeledit?view=netframework-4.6
} catch {
MessageBox.Show("Failed to update record in data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} else {
e.CancelEdit = true;
}
}
private void ButtonSave_Click(object sender, EventArgs e) {
TreeNode selectedNode = treeView.SelectedNode;
IRecord record = (IRecord)selectedNode.Tag;
record.UpdateFromControl();
record.Title = selectedNode.Text = textBoxTitle.Text;
GroupPath groupPath = (GroupPath)comboBoxGroup.SelectedItem;
bool groupChanged = record.GroupId != groupPath.Id;
record.GroupId = groupPath.Id;
try {
((IDataSource)comboBoxDatabase.SelectedItem).Update(record);
} catch {
MessageBox.Show("Failed to update record in data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (groupChanged) {
selectedNode.Remove();
if (groupPath.Node != null) {
groupPath.Node.Nodes.Add(selectedNode);
} else {
treeView.Nodes.Add(selectedNode);
}
}
SortTreeAndSelectNode(selectedNode);
}
private void NewRecordContextMenuItem_Click(object sender, EventArgs e) {
Type type = (Type)((ToolStripMenuItem)sender).Tag;
TreeNode parentNode = treeView.SelectedNode;
IRecord record = (IRecord)Activator.CreateInstance(type);
record.Id = Guid.NewGuid();
record.Title = string.Format("New {0}", type.GetDisplayName());
record.GroupId = parentNode == null ? Guid.Empty : ((IRecord)parentNode.Tag).Id;
try {
TreeNode node = InsertNewRecord(record, parentNode);
SortTreeAndSelectNode(node);
} catch {
MessageBox.Show("Failed to insert record into data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void DuplicateRecordContextMenuItem_Click(object sender, EventArgs e) {
try {
TreeNode node = DuplicateRecord(treeView.SelectedNode, treeView.SelectedNode.Parent);
SortTreeAndSelectNode(node);
} catch {
MessageBox.Show("Failed to insert record into data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private TreeNode DuplicateRecord(TreeNode sourceNode, TreeNode parentNode) {
IRecord source = (IRecord)sourceNode.Tag;
IRecord record = (IRecord)ConfigSerializer.JsonToObj(ConfigSerializer.ObjToJson(source));
record.Id = Guid.NewGuid();
record.Title = string.Format("{0} - Copy", record.Title);
record.GroupId = parentNode == null ? Guid.Empty : ((IRecord)parentNode.Tag).Id;
TreeNode node = InsertNewRecord(record, parentNode);
if (record is Group) {
foreach (TreeNode sourceChildNode in sourceNode.Nodes) {
DuplicateRecord(sourceChildNode, node);
}
}
return node;
}
private TreeNode InsertNewRecord(IRecord record, TreeNode parentNode) {
((IDataSource)comboBoxDatabase.SelectedItem).Insert(record);
TreeNode node = CreateTreeNodeFromRecord(record);
if (parentNode != null) {
parentNode.Nodes.Add(node);
} else {
treeView.Nodes.Add(node);
}
return node;
}
private void DeleteSelectedNode() {
IRecord record = (IRecord)treeView.SelectedNode.Tag;
string message = string.Format("Do you really want to delete selected item{0}?", record is Group ? " and all its subitems" : "");
if (MessageBox.Show(message, "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
try {
((IDataSource)comboBoxDatabase.SelectedItem).Delete(record);
} catch {
MessageBox.Show("Failed to remove record from data source", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
treeView.SelectedNode.Remove();
SortTreeAndSelectNode(null);
}
}
private void DeleteRecordContextMenuItem_Click(object sender, EventArgs e) => DeleteSelectedNode();
#endregion
#region Record ContextMenu
private ContextMenuStrip GetNewRecordContextMenu() {
ContextMenuStrip menu = new ContextMenuStrip();
ToolStripMenuItem menuItemNew = new ToolStripMenuItem("New item");
foreach (Type type in Config.RecordTypes) {
ToolStripMenuItem item = new ToolStripMenuItem(type.GetDisplayName()) { Tag = type };
item.Click += NewRecordContextMenuItem_Click;
menuItemNew.DropDownItems.Add(item);
}
menu.Items.Add(menuItemNew);
ToolStripMenuItem menuItemRefresh = new ToolStripMenuItem("Refresh data source");
menuItemRefresh.Click += RefreshContextMenuItem_Click;
menu.Items.Add(menuItemRefresh);
return menu;
}
private ContextMenuStrip GetContextMenu(IRecord record) {
ContextMenuStrip menu = record is Group ? GetNewRecordContextMenu() : new ContextMenuStrip();
bool isDefaultAction = true;
foreach (IAction action in record.GetActions()) {
ToolStripMenuItem actionItem = new ToolStripMenuItem(action.GetType().GetDisplayName()) { Tag = action };
if (isDefaultAction) {
actionItem.Font = actionItem.Font = new Font(actionItem.Font, actionItem.Font.Style | FontStyle.Bold);
isDefaultAction = false;
}
actionItem.Click += ActionItem_Click;
menu.Items.Add(actionItem);
}
menu.Items.Add(new ToolStripSeparator());
ToolStripMenuItem duplicateItem = new ToolStripMenuItem("Duplicate item");
duplicateItem.Click += DuplicateRecordContextMenuItem_Click;
menu.Items.Add(duplicateItem);
ToolStripMenuItem deleteItem = new ToolStripMenuItem("Delete item");
deleteItem.Click += DeleteRecordContextMenuItem_Click;
menu.Items.Add(deleteItem);
return menu;
}
private void ActionItem_Click(object sender, EventArgs e) {
((IAction)((ToolStripMenuItem)sender).Tag).Run();
}
#endregion
}
}

129
RCM/MainForm.resx Normal file
View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="timerDataSourceRefresh.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>201, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>39</value>
</metadata>
</root>

21
RCM/Program.cs Normal file
View File

@ -0,0 +1,21 @@
using Newtonsoft.Json.Linq;
using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
namespace RCM {
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Config.LoadModules();
Config.LoadConfiguration();
Application.Run(new MainForm());
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RCM")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RCM")]
[assembly: AssemblyCopyright("Copyright © Disassembler 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f5912ef4-edc9-421d-b474-4cdadb121e55")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

73
RCM/Properties/Resources.Designer.cs generated Normal file
View File

@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace RCM.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RCM.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap ConfigIcon {
get {
object obj = ResourceManager.GetObject("ConfigIcon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="ConfigIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\Resources\Config.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

229
RCM/RCM.csproj Normal file
View File

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F5912EF4-EDC9-421D-B474-4CDADB121E55}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>RCM</RootNamespace>
<AssemblyName>RCM</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="CredentialManagement, Version=1.0.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\CredentialManagement.1.0.2\lib\net35\CredentialManagement.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="MySql.Data, Version=8.0.16.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.8.0.16\lib\net452\MySql.Data.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Data.SQLite, Version=1.0.110.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net46\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConfigFormTab.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="ConfigFormTab.Designer.cs">
<DependentUpon>ConfigFormTab.cs</DependentUpon>
</Compile>
<Compile Include="ConfigSerializer.cs" />
<Compile Include="DataSource\MySQL\MySQL.cs" />
<Compile Include="DataSource\MySQL\MySQLControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="DataSource\MySQL\MySQLControl.Designer.cs">
<DependentUpon>MySQLControl.cs</DependentUpon>
</Compile>
<Compile Include="DataSource\SQLite\SQLite.cs" />
<Compile Include="DataSource\SQLite\SQLiteControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="DataSource\SQLite\SQLiteControl.Designer.cs">
<DependentUpon>SQLiteControl.cs</DependentUpon>
</Compile>
<Compile Include="Record\RDP\RDP.cs" />
<Compile Include="Record\RDP\RDPAction.cs" />
<Compile Include="Record\RDP\SMBAction.cs" />
<Compile Include="Record\SSH\SCPAction.cs" />
<Compile Include="Record\SSH\SSH.cs" />
<Compile Include="Record\SSH\SSHAction.cs" />
<Compile Include="Record\SSH\SSHConfig.cs" />
<Compile Include="Record\SSH\SSHConfigControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Record\SSH\SSHConfigControl.Designer.cs">
<DependentUpon>SSHConfigControl.cs</DependentUpon>
</Compile>
<Compile Include="Record\WebSite\BrowserAction.cs" />
<Compile Include="Record\WebSite\WebSite.cs" />
<Compile Include="Record\WebSite\WebSiteControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Record\WebSite\WebSiteControl.Designer.cs">
<DependentUpon>WebSiteControl.cs</DependentUpon>
</Compile>
<Compile Include="Record\WinBox\WinBox.cs" />
<Compile Include="Record\WinBox\WinBoxAction.cs" />
<Compile Include="Record\WinBox\WinBoxConfig.cs" />
<Compile Include="Record\WinBox\WinBoxConfigControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Record\WinBox\WinBoxConfigControl.Designer.cs">
<DependentUpon>WinBoxConfigControl.cs</DependentUpon>
</Compile>
<Compile Include="TypeExtensions.cs" />
<EmbeddedResource Include="ConfigForm.resx">
<DependentUpon>ConfigForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="ConfigFormTab.resx">
<DependentUpon>ConfigFormTab.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="DataSource\MySQL\MySQL.resx">
<DependentUpon>MySQL.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="DataSource\SQLite\SQLite.resx">
<DependentUpon>SQLite.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Config.resx">
<DependentUpon>Config.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="ConfigForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon>
</Compile>
<Compile Include="DisplayName.cs" />
<Compile Include="GroupPath.cs" />
<Compile Include="Config.cs" />
<Compile Include="IConfigSection.cs" />
<Compile Include="IDataSource.cs" />
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainForm.Designer.cs">
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="RecordNodeSorter.cs" />
<Compile Include="IAction.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SimpleHostControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="SimpleHostControl.Designer.cs">
<DependentUpon>SimpleHostControl.cs</DependentUpon>
</Compile>
<Compile Include="Group.cs" />
<Compile Include="IRecord.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Record\RDP\RDP.resx">
<DependentUpon>RDP.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Record\SSH\SSH.resx">
<DependentUpon>SSH.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Record\WebSite\WebSite.resx">
<DependentUpon>WebSite.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Record\WinBox\WinBox.resx">
<DependentUpon>WinBox.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Record\WinBox\WinBoxConfigControl.resx">
<DependentUpon>WinBoxConfigControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="SimpleHostControl.resx">
<DependentUpon>SimpleHostControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Group.resx">
<DependentUpon>Group.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.110.0\build\net46\System.Data.SQLite.Core.targets'))" />
</Target>
</Project>

49
RCM/Record/RDP/RDP.cs Normal file
View File

@ -0,0 +1,49 @@
using System;
using System.Windows.Forms;
using System.Diagnostics;
using CredentialManagement;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace RCM {
[Serializable]
[DisplayName("Remote Desktop (RDP)")]
public class RDP : IRecord {
public Guid Id { get; set; }
public string Title { get; set; }
public Guid GroupId { get; set; }
public string Host { get; set; }
public string Username { get; set; }
public string Password { get; set; }
[NonSerialized]
private SimpleHostControl _control;
[NonSerialized]
private IAction[] _actions;
public IAction[] GetActions() {
if (_actions == null) {
_actions = new IAction[] { new RDPAction(this), new SMBAction(this) };
}
return _actions;
}
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new SimpleHostControl(Host, Username, Password);
}
return _control;
}
public void UpdateFromControl() {
Host = _control.textBoxHostAddress.Text;
Username = _control.textBoxUsername.Text;
Password = _control.textBoxPassword.Text;
}
public static void Init() {
}
}
}

124
RCM/Record/RDP/RDP.resx Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\..\Resources\RDP.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -0,0 +1,55 @@
using CredentialManagement;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace RCM {
[DisplayName("Start Remote Desktop")]
public class RDPAction : IAction {
private RDP _rdp;
public RDPAction(RDP rdp) {
_rdp = rdp;
}
public async void Run() {
await Task.Run(() => {
if (Environment.OSVersion.Platform == PlatformID.Unix) {
RunUnix();
} else {
RunWin();
}
});
}
private void RunWin() {
string[] host = _rdp.Host.Split(new char[] { ':' });
string[] username = _rdp.Username.Split(new char[] { '\\' });
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/ff393699(v%3dws.10)
string rdpFile = Path.GetTempPath() + Guid.NewGuid().ToString() + ".rdp";
using (StreamWriter sw = new StreamWriter(rdpFile)) {
sw.Write("drivestoredirect:s:*\r\nredirectprinters:i:0\r\nredirectcomports:i:0\r\nredirectsmartcards:i:0\r\nredirectposdevices:i:0\r\nnetworkautodetect:i:0\r\nauthentication level:i:0\r\nprompt for credentials:i:0\r\nnegotiate security layer:i:1\r\npromptcredentialonce:i:0\r\nsmart sizing:i:0\r\nuse multimon:i:0\r\nbandwidthautodetect:i:1\r\n");
sw.Write(string.Format("full address:s:{0}\r\n", _rdp.Host));
sw.Write(string.Format("username:s:{0}\r\n", _rdp.Username));
if (username.Length == 2) {
sw.Write(string.Format("domain:s:{0}\r\n", username[0]));
}
}
Credential credential = new Credential(_rdp.Username, _rdp.Password, string.Format("TERMSRV/{0}", host[0]));
credential.Save();
Process p = Process.Start("mstsc", string.Format("\"{0}\"", rdpFile));
Thread.Sleep(1000);
File.Delete(rdpFile);
}
private void RunUnix() {
// TODO
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RCM {
[DisplayName("Browse via SMB")]
public class SMBAction : IAction {
private RDP _rdp;
public SMBAction(RDP rdp) {
_rdp = rdp;
}
public void Run() {
string[] host = _rdp.Host.Split(new char[] { ':' });
if (Environment.OSVersion.Platform == PlatformID.Unix) {
// TODO
} else {
Process.Start("explorer.exe", string.Format("\\\\{0}", host[0])); // TODO: net use credentials?
}
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
[DisplayName("Browse via SCP")]
public class SCPAction : IAction {
private SSH _ssh;
private SSHConfig _config;
public SCPAction(SSH ssh) {
_ssh = ssh;
_config = (SSHConfig)Config.ConfigSections[typeof(SSHConfig)];
}
public async void Run() {
await Task.Run(() => {
if (Environment.OSVersion.Platform == PlatformID.Unix) {
RunUnix();
} else {
RunWin();
}
});
}
private void RunWin() {
if (string.IsNullOrWhiteSpace(_config.WinSCPPath)) {
MessageBox.Show("WinSCP path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!File.Exists(_config.WinSCPPath)) {
MessageBox.Show("WinSCP path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (string.IsNullOrWhiteSpace(_config.SSHKeys) && string.IsNullOrWhiteSpace(_ssh.Password)) {
MessageBox.Show("No authentication method was set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
if (string.IsNullOrWhiteSpace(_config.PageantPath)) {
MessageBox.Show("Pageant path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!File.Exists(_config.PageantPath)) {
MessageBox.Show("Pageant path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
StringBuilder scpParams = new StringBuilder();
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
Process.Start(_config.PageantPath, _config.SSHKeys);
scpParams.Append(string.Format("/privatekey={0} ", _config.SSHKeys)); //TODO: Zvlada vic klicu?
}
scpParams.AppendFormat("scp://{0}@{1}", _ssh.Username, _ssh.Host); // TODO: password?
Process.Start(_config.WinSCPPath, scpParams.ToString());
}
private void RunUnix() {
// TOOD
}
}
}

44
RCM/Record/SSH/SSH.cs Normal file
View File

@ -0,0 +1,44 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
[Serializable]
public class SSH : IRecord {
public Guid Id { get; set; }
public string Title { get; set; }
public Guid GroupId { get; set; }
public string Host { get; set; }
public string Username { get; set; }
public string Password { get; set; }
[NonSerialized]
private SimpleHostControl _control;
[NonSerialized]
private IAction[] _actions;
public IAction[] GetActions() {
if (_actions == null) {
_actions = new IAction[] { new SSHAction(this), new SCPAction(this) };
}
return _actions;
}
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new SimpleHostControl(Host, Username, Password);
}
return _control;
}
public void UpdateFromControl() {
Host = _control.textBoxHostAddress.Text;
Username = _control.textBoxUsername.Text;
Password = _control.textBoxPassword.Text;
}
}
}

124
RCM/Record/SSH/SSH.resx Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\..\Resources\SSH.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -0,0 +1,72 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
[DisplayName("Start SSH")]
public class SSHAction : IAction {
private SSH _ssh;
private SSHConfig _config;
public SSHAction(SSH ssh) {
_ssh = ssh;
_config = (SSHConfig)Config.ConfigSections[typeof(SSHConfig)];
}
public async void Run() {
await Task.Run(() => {
if (Environment.OSVersion.Platform == PlatformID.Unix) {
RunUnix();
} else {
RunWin();
}
});
}
private void RunWin() {
if (string.IsNullOrWhiteSpace(_config.PuttyPath)) {
MessageBox.Show("PuTTY path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!File.Exists(_config.PuttyPath)) {
MessageBox.Show("PuTTY path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (string.IsNullOrWhiteSpace(_config.SSHKeys) && string.IsNullOrWhiteSpace(_ssh.Password)) {
MessageBox.Show("No authentication method was set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
if (string.IsNullOrWhiteSpace(_config.PageantPath)) {
MessageBox.Show("Pageant path is not set", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!File.Exists(_config.PageantPath)) {
MessageBox.Show("Pageant path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
string[] host = _ssh.Host.Split(new char[] { ':' });
StringBuilder puttyParams = new StringBuilder();
if (!string.IsNullOrWhiteSpace(_config.SSHKeys)) {
Process.Start(_config.PageantPath, _config.SSHKeys);
puttyParams.AppendFormat("-i {0} ", _config.SSHKeys);
} else {
puttyParams.AppendFormat("-pw {0} ", _ssh.Password);
}
if (host.Length > 1) {
puttyParams.AppendFormat("-P {0} ", host[1]);
}
puttyParams.AppendFormat("{0}@{1}", _ssh.Username, host[0]);
Process.Start(_config.PuttyPath, puttyParams.ToString());
}
private void RunUnix() {
// TODO
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
[DisplayName("SSH")]
[Serializable]
public class SSHConfig : IConfigSection {
public string SSHKeys { get; set; }
public string PuttyPath { get; set; }
public string PageantPath { get; set; }
public string WinSCPPath { get; set; }
[NonSerialized]
private SSHConfigControl _control;
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new SSHConfigControl(this);
}
return _control;
}
public void UpdateFromControl() {
SSHKeys = _control.textBoxSshKeys.Text;
PuttyPath = _control.textBoxPutty.Text;
PageantPath = _control.textBoxPageant.Text;
WinSCPPath = _control.textBoxWinSCP.Text;
}
}
}

View File

@ -0,0 +1,222 @@
namespace RCM {
partial class SSHConfigControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.labelPutty = new System.Windows.Forms.Label();
this.groupBoxWindows = new System.Windows.Forms.GroupBox();
this.buttonSelectPageant = new System.Windows.Forms.Button();
this.buttonSelectPutty = new System.Windows.Forms.Button();
this.textBoxPageant = new System.Windows.Forms.TextBox();
this.textBoxPutty = new System.Windows.Forms.TextBox();
this.labelPageant = new System.Windows.Forms.Label();
this.labelSshKey = new System.Windows.Forms.Label();
this.textBoxSshKeys = new System.Windows.Forms.TextBox();
this.buttonSelectSshKeys = new System.Windows.Forms.Button();
this.openFileDialogSshKeys = new System.Windows.Forms.OpenFileDialog();
this.openFileDialogPutty = new System.Windows.Forms.OpenFileDialog();
this.openFileDialogPageant = new System.Windows.Forms.OpenFileDialog();
this.buttonSelectWinSCP = new System.Windows.Forms.Button();
this.textBoxWinSCP = new System.Windows.Forms.TextBox();
this.labelWinSCP = new System.Windows.Forms.Label();
this.openFileDialogWinSCP = new System.Windows.Forms.OpenFileDialog();
this.groupBoxWindows.SuspendLayout();
this.SuspendLayout();
//
// labelPutty
//
this.labelPutty.AutoSize = true;
this.labelPutty.Location = new System.Drawing.Point(19, 22);
this.labelPutty.Name = "labelPutty";
this.labelPutty.Size = new System.Drawing.Size(101, 13);
this.labelPutty.TabIndex = 0;
this.labelPutty.Text = "Path to PuTTY.exe:";
//
// groupBoxWindows
//
this.groupBoxWindows.Controls.Add(this.buttonSelectWinSCP);
this.groupBoxWindows.Controls.Add(this.textBoxWinSCP);
this.groupBoxWindows.Controls.Add(this.labelWinSCP);
this.groupBoxWindows.Controls.Add(this.buttonSelectPageant);
this.groupBoxWindows.Controls.Add(this.buttonSelectPutty);
this.groupBoxWindows.Controls.Add(this.textBoxPageant);
this.groupBoxWindows.Controls.Add(this.textBoxPutty);
this.groupBoxWindows.Controls.Add(this.labelPageant);
this.groupBoxWindows.Controls.Add(this.labelPutty);
this.groupBoxWindows.Location = new System.Drawing.Point(3, 39);
this.groupBoxWindows.Name = "groupBoxWindows";
this.groupBoxWindows.Size = new System.Drawing.Size(487, 108);
this.groupBoxWindows.TabIndex = 1;
this.groupBoxWindows.TabStop = false;
this.groupBoxWindows.Text = "Windows";
//
// buttonSelectPageant
//
this.buttonSelectPageant.Location = new System.Drawing.Point(452, 44);
this.buttonSelectPageant.Name = "buttonSelectPageant";
this.buttonSelectPageant.Size = new System.Drawing.Size(22, 22);
this.buttonSelectPageant.TabIndex = 6;
this.buttonSelectPageant.Text = "⋯";
this.buttonSelectPageant.UseVisualStyleBackColor = true;
this.buttonSelectPageant.Click += new System.EventHandler(this.ButtonSelectPageant_Click);
//
// buttonSelectPutty
//
this.buttonSelectPutty.Location = new System.Drawing.Point(452, 18);
this.buttonSelectPutty.Name = "buttonSelectPutty";
this.buttonSelectPutty.Size = new System.Drawing.Size(22, 22);
this.buttonSelectPutty.TabIndex = 6;
this.buttonSelectPutty.Text = "⋯";
this.buttonSelectPutty.UseVisualStyleBackColor = true;
this.buttonSelectPutty.Click += new System.EventHandler(this.ButtonSelectPutty_Click);
//
// textBoxPageant
//
this.textBoxPageant.Location = new System.Drawing.Point(132, 45);
this.textBoxPageant.Name = "textBoxPageant";
this.textBoxPageant.Size = new System.Drawing.Size(314, 20);
this.textBoxPageant.TabIndex = 3;
//
// textBoxPutty
//
this.textBoxPutty.Location = new System.Drawing.Point(132, 19);
this.textBoxPutty.Name = "textBoxPutty";
this.textBoxPutty.Size = new System.Drawing.Size(314, 20);
this.textBoxPutty.TabIndex = 2;
//
// labelPageant
//
this.labelPageant.AutoSize = true;
this.labelPageant.Location = new System.Drawing.Point(19, 48);
this.labelPageant.Name = "labelPageant";
this.labelPageant.Size = new System.Drawing.Size(107, 13);
this.labelPageant.TabIndex = 1;
this.labelPageant.Text = "Path to Pageant.exe:";
//
// labelSshKey
//
this.labelSshKey.AutoSize = true;
this.labelSshKey.Location = new System.Drawing.Point(8, 10);
this.labelSshKey.Name = "labelSshKey";
this.labelSshKey.Size = new System.Drawing.Size(99, 13);
this.labelSshKey.TabIndex = 4;
this.labelSshKey.Text = "Paths to SSH keys:";
//
// textBoxSshKeys
//
this.textBoxSshKeys.Location = new System.Drawing.Point(135, 7);
this.textBoxSshKeys.Name = "textBoxSshKeys";
this.textBoxSshKeys.Size = new System.Drawing.Size(314, 20);
this.textBoxSshKeys.TabIndex = 4;
//
// buttonSelectSshKeys
//
this.buttonSelectSshKeys.Location = new System.Drawing.Point(455, 6);
this.buttonSelectSshKeys.Name = "buttonSelectSshKeys";
this.buttonSelectSshKeys.Size = new System.Drawing.Size(22, 22);
this.buttonSelectSshKeys.TabIndex = 5;
this.buttonSelectSshKeys.Text = "⋯";
this.buttonSelectSshKeys.UseVisualStyleBackColor = true;
this.buttonSelectSshKeys.Click += new System.EventHandler(this.ButtonSelectSshKeys_Click);
//
// openFileDialogSshKeys
//
this.openFileDialogSshKeys.Filter = "OpenSSH keys (*.pub)|*.pub|PuTTY keys (*.ppk)|*.ppk|All files (*.*)|*.*";
this.openFileDialogSshKeys.Multiselect = true;
//
// openFileDialogPutty
//
this.openFileDialogPutty.Filter = "PuTTY.exe|PuTTY.exe";
//
// openFileDialogPageant
//
this.openFileDialogPageant.Filter = "Pageant.exe|Pageant.exe";
//
// buttonSelectWinSCP
//
this.buttonSelectWinSCP.Location = new System.Drawing.Point(452, 70);
this.buttonSelectWinSCP.Name = "buttonSelectWinSCP";
this.buttonSelectWinSCP.Size = new System.Drawing.Size(22, 22);
this.buttonSelectWinSCP.TabIndex = 9;
this.buttonSelectWinSCP.Text = "⋯";
this.buttonSelectWinSCP.UseVisualStyleBackColor = true;
this.buttonSelectWinSCP.Click += new System.EventHandler(this.ButtonSelectWinSCP_Click);
//
// textBoxWinSCP
//
this.textBoxWinSCP.Location = new System.Drawing.Point(132, 71);
this.textBoxWinSCP.Name = "textBoxWinSCP";
this.textBoxWinSCP.Size = new System.Drawing.Size(314, 20);
this.textBoxWinSCP.TabIndex = 8;
//
// labelWinSCP
//
this.labelWinSCP.AutoSize = true;
this.labelWinSCP.Location = new System.Drawing.Point(19, 74);
this.labelWinSCP.Name = "labelWinSCP";
this.labelWinSCP.Size = new System.Drawing.Size(107, 13);
this.labelWinSCP.TabIndex = 7;
this.labelWinSCP.Text = "Path to WinSCP.exe:";
//
// openFileDialogWinSCP
//
this.openFileDialogWinSCP.Filter = "WinSCP.exe|WinSCP.exe";
//
// SSHConfigControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.buttonSelectSshKeys);
this.Controls.Add(this.textBoxSshKeys);
this.Controls.Add(this.labelSshKey);
this.Controls.Add(this.groupBoxWindows);
this.Name = "SSHConfigControl";
this.Size = new System.Drawing.Size(499, 172);
this.groupBoxWindows.ResumeLayout(false);
this.groupBoxWindows.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label labelPutty;
private System.Windows.Forms.GroupBox groupBoxWindows;
public System.Windows.Forms.TextBox textBoxPageant;
public System.Windows.Forms.TextBox textBoxPutty;
private System.Windows.Forms.Label labelPageant;
private System.Windows.Forms.Label labelSshKey;
public System.Windows.Forms.TextBox textBoxSshKeys;
private System.Windows.Forms.Button buttonSelectSshKeys;
private System.Windows.Forms.Button buttonSelectPageant;
private System.Windows.Forms.Button buttonSelectPutty;
private System.Windows.Forms.OpenFileDialog openFileDialogSshKeys;
private System.Windows.Forms.OpenFileDialog openFileDialogPutty;
private System.Windows.Forms.OpenFileDialog openFileDialogPageant;
private System.Windows.Forms.Button buttonSelectWinSCP;
public System.Windows.Forms.TextBox textBoxWinSCP;
private System.Windows.Forms.Label labelWinSCP;
private System.Windows.Forms.OpenFileDialog openFileDialogWinSCP;
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Windows.Forms;
namespace RCM {
public partial class SSHConfigControl : UserControl {
public SSHConfigControl(SSHConfig sshConfig) {
InitializeComponent();
textBoxPutty.Text = sshConfig.PuttyPath;
textBoxPageant.Text = sshConfig.PageantPath;
textBoxSshKeys.Text = sshConfig.SSHKeys;
}
private void ButtonSelectSshKeys_Click(object sender, EventArgs e) {
if (openFileDialogSshKeys.ShowDialog() == DialogResult.OK) {
textBoxSshKeys.Text = string.Join(",", openFileDialogSshKeys.FileNames);
}
}
private void ButtonSelectPutty_Click(object sender, EventArgs e) {
if (openFileDialogPutty.ShowDialog() == DialogResult.OK) {
textBoxPutty.Text = openFileDialogPutty.FileName;
}
}
private void ButtonSelectPageant_Click(object sender, EventArgs e) {
if (openFileDialogPageant.ShowDialog() == DialogResult.OK) {
textBoxPageant.Text = openFileDialogPageant.FileName;
}
}
private void ButtonSelectWinSCP_Click(object sender, EventArgs e) {
if (openFileDialogWinSCP.ShowDialog() == DialogResult.OK) {
textBoxWinSCP.Text = openFileDialogWinSCP.FileName;
}
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RCM {
[DisplayName("Open in browser")]
public class BrowserAction : IAction {
private WebSite _website;
public BrowserAction(WebSite website) {
_website = website;
}
public async void Run() {
await Task.Run(() => {
if (_website.URL.StartsWith("http://") || _website.URL.StartsWith("https://"))
Process.Start(_website.URL); // TODO: vyplnovat hesla
});
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace RCM {
[Serializable]
public class WebSite : IRecord {
public Guid Id { get; set; }
public string Title { get; set; }
public Guid GroupId { get; set; }
public string URL { get; set; }
public string Username { get; set; }
public string Password { get; set; }
[NonSerialized]
private WebSiteControl _control;
[NonSerialized]
private IAction[] _actions;
public IAction[] GetActions() {
if (_actions == null) {
_actions = new IAction[] { new BrowserAction(this) };
}
return _actions;
}
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new WebSiteControl(this);
}
return _control;
}
public void UpdateFromControl() {
URL = _control.textBoxURL.Text;
Username = _control.textBoxUsername.Text;
Password = _control.textBoxPassword.Text;
}
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\..\Resources\Website.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -0,0 +1,122 @@
namespace RCM {
partial class WebSiteControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.buttonUnmaskPassword = new System.Windows.Forms.Button();
this.textBoxPassword = new System.Windows.Forms.TextBox();
this.textBoxUsername = new System.Windows.Forms.TextBox();
this.textBoxURL = new System.Windows.Forms.TextBox();
this.labelPassword = new System.Windows.Forms.Label();
this.labelUsername = new System.Windows.Forms.Label();
this.labelURL = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// buttonUnmaskPassword
//
this.buttonUnmaskPassword.Location = new System.Drawing.Point(273, 51);
this.buttonUnmaskPassword.Name = "buttonUnmaskPassword";
this.buttonUnmaskPassword.Size = new System.Drawing.Size(22, 22);
this.buttonUnmaskPassword.TabIndex = 4;
this.buttonUnmaskPassword.Text = "●";
this.buttonUnmaskPassword.UseVisualStyleBackColor = true;
this.buttonUnmaskPassword.Click += new System.EventHandler(this.ButtonUnmaskPassword_Click);
//
// textBoxPassword
//
this.textBoxPassword.Location = new System.Drawing.Point(120, 52);
this.textBoxPassword.Name = "textBoxPassword";
this.textBoxPassword.Size = new System.Drawing.Size(147, 20);
this.textBoxPassword.TabIndex = 3;
this.textBoxPassword.UseSystemPasswordChar = true;
//
// textBoxUsername
//
this.textBoxUsername.Location = new System.Drawing.Point(120, 26);
this.textBoxUsername.Name = "textBoxUsername";
this.textBoxUsername.Size = new System.Drawing.Size(175, 20);
this.textBoxUsername.TabIndex = 2;
//
// textBoxURL
//
this.textBoxURL.Location = new System.Drawing.Point(120, 0);
this.textBoxURL.Name = "textBoxURL";
this.textBoxURL.Size = new System.Drawing.Size(175, 20);
this.textBoxURL.TabIndex = 1;
//
// labelPassword
//
this.labelPassword.AutoSize = true;
this.labelPassword.Location = new System.Drawing.Point(0, 55);
this.labelPassword.Name = "labelPassword";
this.labelPassword.Size = new System.Drawing.Size(56, 13);
this.labelPassword.TabIndex = 14;
this.labelPassword.Text = "Password:";
//
// labelUsername
//
this.labelUsername.AutoSize = true;
this.labelUsername.Location = new System.Drawing.Point(0, 29);
this.labelUsername.Name = "labelUsername";
this.labelUsername.Size = new System.Drawing.Size(58, 13);
this.labelUsername.TabIndex = 13;
this.labelUsername.Text = "Username:";
//
// labelURL
//
this.labelURL.AutoSize = true;
this.labelURL.Location = new System.Drawing.Point(0, 3);
this.labelURL.Name = "labelURL";
this.labelURL.Size = new System.Drawing.Size(32, 13);
this.labelURL.TabIndex = 12;
this.labelURL.Text = "URL:";
//
// WebsiteControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.buttonUnmaskPassword);
this.Controls.Add(this.textBoxPassword);
this.Controls.Add(this.textBoxUsername);
this.Controls.Add(this.textBoxURL);
this.Controls.Add(this.labelPassword);
this.Controls.Add(this.labelUsername);
this.Controls.Add(this.labelURL);
this.Name = "WebsiteControl";
this.Size = new System.Drawing.Size(300, 77);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button buttonUnmaskPassword;
private System.Windows.Forms.Label labelPassword;
private System.Windows.Forms.Label labelUsername;
private System.Windows.Forms.Label labelURL;
public System.Windows.Forms.TextBox textBoxURL;
public System.Windows.Forms.TextBox textBoxUsername;
public System.Windows.Forms.TextBox textBoxPassword;
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Windows.Forms;
namespace RCM {
public partial class WebSiteControl : UserControl {
public WebSiteControl(WebSite website) {
InitializeComponent();
textBoxURL.Text = website.URL;
textBoxUsername.Text = website.Username;
textBoxPassword.Text = website.Password;
}
private void ButtonUnmaskPassword_Click(object sender, EventArgs e) {
textBoxPassword.UseSystemPasswordChar = !textBoxPassword.UseSystemPasswordChar;
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
[Serializable]
public class WinBox : IRecord {
public Guid Id { get; set; }
public string Title { get; set; }
public Guid GroupId { get; set; }
public string Host { get; set; }
public string Username { get; set; }
public string Password { get; set; }
[NonSerialized]
private SimpleHostControl _control;
[NonSerialized]
private IAction[] _actions;
public IAction[] GetActions() {
if (_actions == null) {
_actions = new IAction[] { new WinBoxAction(this) };
}
return _actions;
}
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new SimpleHostControl(Host, Username, Password);
}
return _control;
}
public void UpdateFromControl() {
Host = _control.textBoxHostAddress.Text;
Username = _control.textBoxUsername.Text;
Password = _control.textBoxPassword.Text;
}
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\..\Resources\WinBox.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RCM {
[DisplayName("Start WinBox")]
public class WinBoxAction : IAction {
private WinBox _winbox;
private WinBoxConfig _config;
public WinBoxAction(WinBox winbox) {
_winbox = winbox;
_config = (WinBoxConfig)Config.ConfigSections[typeof(WinBoxConfig)];
}
public async void Run() {
await Task.Run(() => {
if (Environment.OSVersion.Platform == PlatformID.Unix) {
RunUnix();
} else {
RunWin();
}
});
}
private void RunWin() {
string winboxParam = string.Format("{0} \"{1}\" \"{2}\"", _winbox.Host, _winbox.Username, _winbox.Password);
Process.Start(_config.WinBoxPath, winboxParam);
}
private void RunUnix() {
string winboxParam = string.Format("{0} {1} \"{2}\" \"{3}\"", _config.WinBoxPath, _winbox.Host, _winbox.Username, _winbox.Password);
Process.Start("wine", winboxParam);
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RCM {
[DisplayName("WinBox")]
[Serializable]
public class WinBoxConfig : IConfigSection {
public string WinBoxPath { get; set; }
[NonSerialized]
private WinBoxConfigControl _control;
public UserControl GetControl() {
if (_control == null || _control.IsDisposed) {
_control = new WinBoxConfigControl(this);
}
return _control;
}
public void UpdateFromControl() {
WinBoxPath = _control.textBoxWinBox.Text;
}
}
}

View File

@ -0,0 +1,83 @@
namespace RCM {
partial class WinBoxConfigControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.buttonSelectWinBox = new System.Windows.Forms.Button();
this.textBoxWinBox = new System.Windows.Forms.TextBox();
this.labelWinBox = new System.Windows.Forms.Label();
this.openFileDialogWinBox = new System.Windows.Forms.OpenFileDialog();
this.SuspendLayout();
//
// buttonSelectWinBox
//
this.buttonSelectWinBox.Location = new System.Drawing.Point(455, 6);
this.buttonSelectWinBox.Name = "buttonSelectWinBox";
this.buttonSelectWinBox.Size = new System.Drawing.Size(22, 22);
this.buttonSelectWinBox.TabIndex = 9;
this.buttonSelectWinBox.Text = "⋯";
this.buttonSelectWinBox.UseVisualStyleBackColor = true;
this.buttonSelectWinBox.Click += new System.EventHandler(this.ButtonSelectWinBox_Click);
//
// textBoxWinBox
//
this.textBoxWinBox.Location = new System.Drawing.Point(135, 7);
this.textBoxWinBox.Name = "textBoxWinBox";
this.textBoxWinBox.Size = new System.Drawing.Size(314, 20);
this.textBoxWinBox.TabIndex = 8;
//
// labelWinBox
//
this.labelWinBox.AutoSize = true;
this.labelWinBox.Location = new System.Drawing.Point(6, 10);
this.labelWinBox.Name = "labelWinBox";
this.labelWinBox.Size = new System.Drawing.Size(104, 13);
this.labelWinBox.TabIndex = 7;
this.labelWinBox.Text = "Path to WinBox.exe:";
//
// openFileDialogWinBox
//
this.openFileDialogWinBox.Filter = "WinBox.exe|WinBox.exe";
//
// WinBoxConfigControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.buttonSelectWinBox);
this.Controls.Add(this.textBoxWinBox);
this.Controls.Add(this.labelWinBox);
this.Name = "WinBoxConfigControl";
this.Size = new System.Drawing.Size(494, 116);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button buttonSelectWinBox;
public System.Windows.Forms.TextBox textBoxWinBox;
private System.Windows.Forms.Label labelWinBox;
private System.Windows.Forms.OpenFileDialog openFileDialogWinBox;
}
}

View File

@ -0,0 +1,16 @@
using System.Windows.Forms;
namespace RCM {
public partial class WinBoxConfigControl : UserControl {
public WinBoxConfigControl(WinBoxConfig winBoxConfig) {
InitializeComponent();
textBoxWinBox.Text = winBoxConfig.WinBoxPath;
}
private void ButtonSelectWinBox_Click(object sender, System.EventArgs e) {
if (openFileDialogWinBox.ShowDialog() == DialogResult.OK) {
textBoxWinBox.Text = openFileDialogWinBox.FileName;
}
}
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="openFileDialogWinBox.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 21</value>
</metadata>
</root>

32
RCM/RecordNodeSorter.cs Normal file
View File

@ -0,0 +1,32 @@
using System.Collections;
using System.Windows.Forms;
namespace RCM {
public class RecordNodeSorter : IComparer {
public int Compare(object o1, object o2) {
IRecord record1 = (IRecord)((TreeNode)o1).Tag;
IRecord record2 = (IRecord)((TreeNode)o2).Tag;
int result = 0;
if (record1 is Group) {
if (record2 is Group) {
result = string.Compare(record1.Title, record2.Title);
if (result == 0) {
result = string.Compare(record1.Id.ToString(), record2.Id.ToString());
}
} else {
result = -1;
}
} else if (record2 is Group) {
result = 1;
} else {
result = string.Compare(record1.Title, record2.Title);
if (result == 0) {
result = string.Compare(record1.Id.ToString(), record2.Id.ToString());
}
}
return result;
}
}
}

122
RCM/SimpleHostControl.Designer.cs generated Normal file
View File

@ -0,0 +1,122 @@
namespace RCM {
partial class SimpleHostControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.buttonUnmaskPassword = new System.Windows.Forms.Button();
this.textBoxPassword = new System.Windows.Forms.TextBox();
this.textBoxUsername = new System.Windows.Forms.TextBox();
this.textBoxHostAddress = new System.Windows.Forms.TextBox();
this.labelPassword = new System.Windows.Forms.Label();
this.labelUsername = new System.Windows.Forms.Label();
this.labelHostAddress = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// buttonUnmaskPassword
//
this.buttonUnmaskPassword.Location = new System.Drawing.Point(273, 51);
this.buttonUnmaskPassword.Name = "buttonUnmaskPassword";
this.buttonUnmaskPassword.Size = new System.Drawing.Size(22, 22);
this.buttonUnmaskPassword.TabIndex = 4;
this.buttonUnmaskPassword.Text = "●";
this.buttonUnmaskPassword.UseVisualStyleBackColor = true;
this.buttonUnmaskPassword.Click += new System.EventHandler(this.ButtonUnmaskPassword_Click);
//
// textBoxPassword
//
this.textBoxPassword.Location = new System.Drawing.Point(120, 52);
this.textBoxPassword.Name = "textBoxPassword";
this.textBoxPassword.Size = new System.Drawing.Size(147, 20);
this.textBoxPassword.TabIndex = 3;
this.textBoxPassword.UseSystemPasswordChar = true;
//
// textBoxUsername
//
this.textBoxUsername.Location = new System.Drawing.Point(120, 26);
this.textBoxUsername.Name = "textBoxUsername";
this.textBoxUsername.Size = new System.Drawing.Size(175, 20);
this.textBoxUsername.TabIndex = 2;
//
// textBoxHostAddress
//
this.textBoxHostAddress.Location = new System.Drawing.Point(120, 0);
this.textBoxHostAddress.Name = "textBoxHostAddress";
this.textBoxHostAddress.Size = new System.Drawing.Size(175, 20);
this.textBoxHostAddress.TabIndex = 1;
//
// labelPassword
//
this.labelPassword.AutoSize = true;
this.labelPassword.Location = new System.Drawing.Point(0, 55);
this.labelPassword.Name = "labelPassword";
this.labelPassword.Size = new System.Drawing.Size(56, 13);
this.labelPassword.TabIndex = 14;
this.labelPassword.Text = "Password:";
//
// labelUsername
//
this.labelUsername.AutoSize = true;
this.labelUsername.Location = new System.Drawing.Point(0, 29);
this.labelUsername.Name = "labelUsername";
this.labelUsername.Size = new System.Drawing.Size(58, 13);
this.labelUsername.TabIndex = 13;
this.labelUsername.Text = "Username:";
//
// labelHostAddress
//
this.labelHostAddress.AutoSize = true;
this.labelHostAddress.Location = new System.Drawing.Point(0, 3);
this.labelHostAddress.Name = "labelHostAddress";
this.labelHostAddress.Size = new System.Drawing.Size(72, 13);
this.labelHostAddress.TabIndex = 12;
this.labelHostAddress.Text = "Host address:";
//
// SimpleHostControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.buttonUnmaskPassword);
this.Controls.Add(this.textBoxPassword);
this.Controls.Add(this.textBoxUsername);
this.Controls.Add(this.textBoxHostAddress);
this.Controls.Add(this.labelPassword);
this.Controls.Add(this.labelUsername);
this.Controls.Add(this.labelHostAddress);
this.Name = "SimpleHostControl";
this.Size = new System.Drawing.Size(300, 75);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button buttonUnmaskPassword;
private System.Windows.Forms.Label labelPassword;
private System.Windows.Forms.Label labelUsername;
private System.Windows.Forms.Label labelHostAddress;
public System.Windows.Forms.TextBox textBoxHostAddress;
public System.Windows.Forms.TextBox textBoxUsername;
public System.Windows.Forms.TextBox textBoxPassword;
}
}

17
RCM/SimpleHostControl.cs Normal file
View File

@ -0,0 +1,17 @@
using System;
using System.Windows.Forms;
namespace RCM {
public partial class SimpleHostControl : UserControl {
public SimpleHostControl(string host, string username, string password) {
InitializeComponent();
textBoxHostAddress.Text = host;
textBoxUsername.Text = username;
textBoxPassword.Text = password;
}
private void ButtonUnmaskPassword_Click(object sender, EventArgs e) {
textBoxPassword.UseSystemPasswordChar = !textBoxPassword.UseSystemPasswordChar;
}
}
}

120
RCM/SimpleHostControl.resx Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

14
RCM/TypeExtensions.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RCM {
public static class TypeExtensions {
public static string GetDisplayName(this Type type) {
DisplayName displayName = (DisplayName)type.GetCustomAttributes(typeof(DisplayName), false).FirstOrDefault();
return displayName != null ? displayName.Text : type.Name;
}
}
}

9
RCM/packages.config Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.8.3.1" targetFramework="net461" />
<package id="CredentialManagement" version="1.0.2" targetFramework="net461" />
<package id="Google.Protobuf" version="3.6.1" targetFramework="net461" />
<package id="MySql.Data" version="8.0.16" targetFramework="net461" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net461" />
<package id="System.Data.SQLite.Core" version="1.0.110.0" targetFramework="net461" />
</packages>

BIN
Resources/Config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

BIN
Resources/Empty.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Resources/Group.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Resources/MySQL.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Resources/RDP.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Resources/SQLite.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Resources/SSH.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Resources/Website.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
Resources/WinBox.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB