diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ab3ba544 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,30 @@ +# editorconfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 + +# Xml project files +[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] +indent_size = 2 + +# Xml files +[*.{xml,stylecop,resx,ruleset}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +# Shell scripts +[*.sh] +end_of_line = lf +[*.{cmd, bat}] +end_of_line = crlf \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..8079ae23 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..bee4418f --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +build/ +*.suo +*.user +_ReSharper* +*.csproj.user +*.resharper.user +*.userprefs +*.suo +*.cache +*.trx +*.pidb +Thumbs.db +[Bb]in +[Dd]ebug +[Oo]bj +[Rr]elease +[Tt]est[Rr]esult* +_UpgradeReport_Files +*[Pp]ublish.xml +*.project +*.metadata +logs +*.generated.cs +T4MVC.cs +/SharpSnmpLib/sharpsnmplib.snk +/packages +/lib/sharpsnmplib.snk +/gendarme +/lib/Activelock3.6.NET/ActiveLock3_6NET.xml +/lib/Activelock3.6.NET/SharpSnmpLib.Optional.dll +/lib/NBug.dll + +/SharpSnmpLib.Mib/Help + +/NDependOut diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..2997131b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/ExpandCollapsePanel"] + path = lib/ExpandCollapsePanel + url = git@github.com:lextm/ExpandCollapsePanel.git +[submodule "mwa"] + path = mwa + url = git@github.com:jexuswebserver/Microsoft.Web.Administration.git diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe new file mode 100644 index 00000000..a9c681c8 Binary files /dev/null and b/.nuget/NuGet.exe differ diff --git a/CertificateInstaller/App.config b/CertificateInstaller/App.config new file mode 100644 index 00000000..d1428ad7 --- /dev/null +++ b/CertificateInstaller/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/CertificateInstaller/CertificateInstaller.csproj b/CertificateInstaller/CertificateInstaller.csproj new file mode 100644 index 00000000..234fe5b5 --- /dev/null +++ b/CertificateInstaller/CertificateInstaller.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {54BEC09A-B32F-4A66-A871-749E0621D822} + Exe + Properties + CertificateInstaller + CertificateInstaller + v4.5 + 512 + + false + ..\JexusManager\JexusManager.snk + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + ..\bin\ + TRACE + prompt + 4 + false + + + app.manifest + + + + ..\packages\Mono.Options.4.4.0.0\lib\net4-client\Mono.Options.dll + True + + + + + + + + + + + + + NativeMethods.cs + + + + + + + + Designer + + + + + + \ No newline at end of file diff --git a/CertificateInstaller/Program.cs b/CertificateInstaller/Program.cs new file mode 100644 index 00000000..1ad3c859 --- /dev/null +++ b/CertificateInstaller/Program.cs @@ -0,0 +1,146 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace CertificateInstaller +{ + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Security.Cryptography.X509Certificates; + + using Microsoft.Web.Administration; + + using Mono.Options; + + internal class Program + { + private static int Main(string[] args) + { + string p12File = null; + string p12Pwd = null; + string friendlyName = null; + string store = null; + string hash = null; + string address = null; + string port = null; + string id = null; + string host = null; + + OptionSet p = + new OptionSet().Add("f:", "File name", delegate (string v) { if (v != null) p12File = v; }) + .Add("p:", "Password", delegate (string v) { if (v != null) p12Pwd = v; }) + .Add("n:", "Friendly name", delegate (string v) { if (v != null) friendlyName = v; }) + .Add("s:", "Store name", delegate (string v) { if (v != null) store = v; }) + .Add("h:", "Certificate hash (not required when adding certificates)", delegate (string v) { if (v != null) hash = v; }) + .Add("a:", "IP address", delegate (string v) { if (v != null) address = v; }) + .Add("o:", "Port number", delegate (string v) { if (v != null) port = v; }) + .Add("i:", "Application ID", delegate (string v) { if (v != null) id = v; }) + .Add("x:", "SNI host name (not required when managing IP based bindings)", delegate (string v) { if (v != null) host = v; }); + + if (args.Length == 0) + { + ShowHelp(p); + return -1; + } + + List extra; + try + { + extra = p.Parse(args); + } + catch (OptionException ex) + { + Console.WriteLine(ex.Message); + return -1; + } + + if (extra.Count > 0) + { + ShowHelp(p); + return -1; + } + + try + { + X509Store personal = new X509Store(store, StoreLocation.LocalMachine); + personal.Open(OpenFlags.ReadWrite); + if (hash == null) + { + // add certificate + // http://paulstovell.com/blog/x509certificate2 + var x509 = new X509Certificate2( + p12File, + p12Pwd, + X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet + | X509KeyStorageFlags.MachineKeySet); + x509.FriendlyName = friendlyName; + personal.Add(x509); + personal.Close(); + return 0; + } + + var selectedItem = personal.Certificates.Find(X509FindType.FindByThumbprint, hash, false); + if (address == null) + { + if (host == null) + { + // remove certificate and mapping + var mappings = NativeMethods.QuerySslCertificateInfo(); + foreach (var mapping in mappings) + { + if (mapping.Hash.SequenceEqual(selectedItem[0].GetCertHash())) + { + NativeMethods.DeleteCertificateBinding(mapping.IpPort); + } + } + + personal.Remove(selectedItem[0]); + } + else + { + var mappings = NativeMethods.QuerySslSniInfo(); + foreach (var mapping in mappings) + { + if (mapping.Hash.SequenceEqual(selectedItem[0].GetCertHash())) + { + NativeMethods.DeleteSniBinding(new Tuple(mapping.Host, mapping.Port)); + } + } + } + + personal.Close(); + return 0; + } + + if (host == null) + { + // register mapping + var endpoint = new IPEndPoint(IPAddress.Parse(address), int.Parse(port)); + NativeMethods.BindCertificate(endpoint, selectedItem[0].GetCertHash(), store, Guid.Parse(id)); + } + else + { + NativeMethods.BindSni(new Tuple(host, int.Parse(port)), selectedItem[0].GetCertHash(), store, Guid.Parse(id)); + } + + return 0; + } + catch (Exception ex) + { + Console.WriteLine(ex); + return -1; + } + } + + private static void ShowHelp(OptionSet optionSet) + { + Console.WriteLine("Jexus Manager is available at https://jexus.codeplex.com"); + Console.WriteLine("CertificateInstaller.exe [Options]"); + Console.WriteLine("Options:"); + optionSet.WriteOptionDescriptions(Console.Out); + } + } +} diff --git a/CertificateInstaller/Properties/AssemblyInfo.cs b/CertificateInstaller/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a4e98522 --- /dev/null +++ b/CertificateInstaller/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("CertificateInstaller")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CertificateInstaller")] +[assembly: AssemblyCopyright("Copyright \u00A9 2015")] +[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("54bec09a-b32f-4a66-a871-749e0621d822")] + +// 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")] diff --git a/CertificateInstaller/app.manifest b/CertificateInstaller/app.manifest new file mode 100644 index 00000000..46701591 --- /dev/null +++ b/CertificateInstaller/app.manifest @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CertificateInstaller/packages.config b/CertificateInstaller/packages.config new file mode 100644 index 00000000..8a52a680 --- /dev/null +++ b/CertificateInstaller/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/JexusManager.Features.Access/AccessFeature.cs b/JexusManager.Features.Access/AccessFeature.cs new file mode 100644 index 00000000..5d741ccd --- /dev/null +++ b/JexusManager.Features.Access/AccessFeature.cs @@ -0,0 +1,103 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Access +{ + using System; + using System.Diagnostics; + using System.Resources; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal class AccessFeature + { + public AccessFeature(Module module, ServerManager server, Application application) + { + this.Module = module; + this.Server = server; + this.Application = application; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (this.Module as IServiceProvider).GetService(type); + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/access"); + this.SslFlags = (long)section["sslFlags"]; + + this.OnAccessSettingsSaved(); + } + + public string Directory { get; set; } + + protected void OnAccessSettingsSaved() + { + this.AccessSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210534"); + return false; + } + + public AccessSettingsSavedEventHandler AccessSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + public ServerManager Server { get; set; } + public Application Application { get; set; } + + public string Name + { + get { return "SSL Settings"; } + } + + + public long SslFlags { get; set; } + + public void CancelChanges() + { + this.Load(); + } + + public bool ApplyChanges() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/access"); + section["sslFlags"] = this.SslFlags; + service.ServerManager.CommitChanges(); + return true; + } + } +} diff --git a/JexusManager.Features.Access/AccessModule.cs b/JexusManager.Features.Access/AccessModule.cs new file mode 100644 index 00000000..58a959ea --- /dev/null +++ b/JexusManager.Features.Access/AccessModule.cs @@ -0,0 +1,24 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Access +{ + using System; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class AccessModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(AccessPage), "SSL Settings", + "Specify requirements for SSL and client certificates.", + Resources.access_36, Resources.access_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Access/AccessModuleProvider.cs b/JexusManager.Features.Access/AccessModuleProvider.cs new file mode 100644 index 00000000..7a422c22 --- /dev/null +++ b/JexusManager.Features.Access/AccessModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Access +{ + using System; + + using Microsoft.Web.Management.Server; + + public class AccessModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(AccessModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return scope == ManagementScope.Site || scope == ManagementScope.Application; + } + } +} diff --git a/JexusManager.Features.Access/AccessPage.Designer.cs b/JexusManager.Features.Access/AccessPage.Designer.cs new file mode 100644 index 00000000..8fb9afb0 --- /dev/null +++ b/JexusManager.Features.Access/AccessPage.Designer.cs @@ -0,0 +1,283 @@ +namespace JexusManager.Features.Access +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AccessPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.rbRequire = new System.Windows.Forms.RadioButton(); + this.rbAccept = new System.Windows.Forms.RadioButton(); + this.rbIgnore = new System.Windows.Forms.RadioButton(); + this.label4 = new System.Windows.Forms.Label(); + this.cbSSL = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 5; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.rbRequire); + this.panel2.Controls.Add(this.rbAccept); + this.panel2.Controls.Add(this.rbIgnore); + this.panel2.Controls.Add(this.label4); + this.panel2.Controls.Add(this.cbSSL); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 11; + // + // rbRequire + // + this.rbRequire.AutoSize = true; + this.rbRequire.Enabled = false; + this.rbRequire.Location = new System.Drawing.Point(29, 130); + this.rbRequire.Name = "rbRequire"; + this.rbRequire.Size = new System.Drawing.Size(62, 17); + this.rbRequire.TabIndex = 8; + this.rbRequire.TabStop = true; + this.rbRequire.Text = "Require"; + this.rbRequire.UseVisualStyleBackColor = true; + this.rbRequire.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // rbAccept + // + this.rbAccept.AutoSize = true; + this.rbAccept.Enabled = false; + this.rbAccept.Location = new System.Drawing.Point(29, 107); + this.rbAccept.Name = "rbAccept"; + this.rbAccept.Size = new System.Drawing.Size(59, 17); + this.rbAccept.TabIndex = 7; + this.rbAccept.TabStop = true; + this.rbAccept.Text = "Accept"; + this.rbAccept.UseVisualStyleBackColor = true; + this.rbAccept.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // rbIgnore + // + this.rbIgnore.AutoSize = true; + this.rbIgnore.Enabled = false; + this.rbIgnore.Location = new System.Drawing.Point(29, 84); + this.rbIgnore.Name = "rbIgnore"; + this.rbIgnore.Size = new System.Drawing.Size(55, 17); + this.rbIgnore.TabIndex = 6; + this.rbIgnore.TabStop = true; + this.rbIgnore.Text = "Ignore"; + this.rbIgnore.UseVisualStyleBackColor = true; + this.rbIgnore.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(13, 56); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(90, 13); + this.label4.TabIndex = 5; + this.label4.Text = "Client certificates:"; + // + // cbSSL + // + this.cbSSL.AutoSize = true; + this.cbSSL.Enabled = false; + this.cbSSL.Location = new System.Drawing.Point(16, 21); + this.cbSSL.Name = "cbSSL"; + this.cbSSL.Size = new System.Drawing.Size(86, 17); + this.cbSSL.TabIndex = 4; + this.cbSSL.Text = "Require SSL"; + this.cbSSL.UseVisualStyleBackColor = true; + this.cbSSL.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(407, 13); + this.label2.TabIndex = 1; + this.label2.Text = "This page lets you modify the SSL settings for the content of a website or applic" + + "ation."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(136, 25); + this.label3.TabIndex = 2; + this.label3.Text = "SSL Settings"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // AccessPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "AccessPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private CheckBox cbSSL; + private ContextMenuStrip cmsActionPanel; + private RadioButton rbRequire; + private RadioButton rbAccept; + private RadioButton rbIgnore; + private Label label4; + } +} diff --git a/JexusManager.Features.Access/AccessPage.cs b/JexusManager.Features.Access/AccessPage.cs new file mode 100644 index 00000000..4e182400 --- /dev/null +++ b/JexusManager.Features.Access/AccessPage.cs @@ -0,0 +1,198 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Access +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + using Microsoft.Web.Management.Server; + + using Binding = Microsoft.Web.Administration.Binding; + + internal partial class AccessPage : ModuleDialogPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly AccessPage _owner; + + public PageTaskList(AccessPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private AccessFeature _feature; + private bool _hasChanges; + private bool _initialized; + private TaskList _taskList; + + public AccessPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new AccessFeature(this.Module, null, null); + _feature.AccessSettingsUpdated = this.Refresh; + _feature.Load(); + base.Initialize(navigationData); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override bool ApplyChanges() + { + long result = 0; + if (cbSSL.Checked) + { + result |= 8; + } + + if (rbAccept.Checked) + { + result |= 32; + } + + if (rbRequire.Checked) + { + result |= 64; + } + + _feature.SslFlags = result; + if (!_feature.ApplyChanges()) + { + return false; + } + + this.ClearChanges(); + return true; + } + + protected override void CancelChanges() + { + _feature.CancelChanges(); + this.ClearChanges(); + } + + protected override bool HasChanges + { + get { return _hasChanges; } + } + + protected override bool CanApplyChanges + { + get { return true; } + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + this.Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + this.Refresh(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + protected override void OnRefresh() + { + if (!_hasChanges) + { + cbSSL.Checked = (_feature.SslFlags & 8) == 8; + rbAccept.Checked = (_feature.SslFlags & 32) == 32; + rbRequire.Checked = (_feature.SslFlags & 64) == 64; + rbIgnore.Checked = !rbAccept.Checked && !rbRequire.Checked; + + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + if (service.Scope == ManagementScope.Site) + { + var result = false; + foreach (Binding binding in service.Site.Bindings) + { + if (binding.Protocol == "https") + { + result = true; + break; + } + } + + cbSSL.Enabled = rbAccept.Enabled = rbIgnore.Enabled = rbRequire.Enabled = result; + } + else if (service.Scope == ManagementScope.Application) + { + var result = false; + foreach (Binding binding in service.Application.GetSite().Bindings) + { + if (binding.Protocol == "https") + { + result = true; + break; + } + } + + cbSSL.Enabled = rbAccept.Enabled = rbIgnore.Enabled = rbRequire.Enabled = result; + } + + _initialized = true; + } + + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + } + + private void CbSslCheckedChanged(object sender, EventArgs e) + { + InformChanges(); + } + } +} diff --git a/JexusManager.Features.Access/AccessPage.resx b/JexusManager.Features.Access/AccessPage.resx new file mode 100644 index 00000000..950be5d2 --- /dev/null +++ b/JexusManager.Features.Access/AccessPage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Access/AccessSettingsSavedEventHandler.cs b/JexusManager.Features.Access/AccessSettingsSavedEventHandler.cs new file mode 100644 index 00000000..2ddba5e9 --- /dev/null +++ b/JexusManager.Features.Access/AccessSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Access +{ + public delegate void AccessSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Access/JexusManager.Features.Access.csproj b/JexusManager.Features.Access/JexusManager.Features.Access.csproj new file mode 100644 index 00000000..9e20662e --- /dev/null +++ b/JexusManager.Features.Access/JexusManager.Features.Access.csproj @@ -0,0 +1,103 @@ + + + + + Debug + AnyCPU + {B756B3A0-CFC8-4E71-A95A-0E9DC2A8D0C9} + Library + Properties + JexusManager.Features.Access + JexusManager.Features.Access + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + + + + + + + + + + + + + UserControl + + + AccessPage.cs + + + + + True + True + Resources.resx + + + + + AccessPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Access/JexusManager.snk b/JexusManager.Features.Access/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Access/JexusManager.snk differ diff --git a/JexusManager.Features.Access/Properties/AssemblyInfo.cs b/JexusManager.Features.Access/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a17ac568 --- /dev/null +++ b/JexusManager.Features.Access/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Access")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Access")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("b756b3a0-cfc8-4e71-a95a-0e9dc2a8d0c9")] + +// 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")] diff --git a/JexusManager.Features.Access/Properties/Resources.Designer.cs b/JexusManager.Features.Access/Properties/Resources.Designer.cs new file mode 100644 index 00000000..e8f2e853 --- /dev/null +++ b/JexusManager.Features.Access/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Access.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Access.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap access_36 { + get { + object obj = ResourceManager.GetObject("access_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Access/Properties/Resources.resx b/JexusManager.Features.Access/Properties/Resources.resx new file mode 100644 index 00000000..19e92a4a --- /dev/null +++ b/JexusManager.Features.Access/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\access_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Access/Resources/access_36.png b/JexusManager.Features.Access/Resources/access_36.png new file mode 100644 index 00000000..4d27421d Binary files /dev/null and b/JexusManager.Features.Access/Resources/access_36.png differ diff --git a/JexusManager.Features.Authentication/AnonymousAuthenticationFeature.cs b/JexusManager.Features.Authentication/AnonymousAuthenticationFeature.cs new file mode 100644 index 00000000..ac123244 --- /dev/null +++ b/JexusManager.Features.Authentication/AnonymousAuthenticationFeature.cs @@ -0,0 +1,145 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class AnonymousAuthenticationFeature : AuthenticationFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly AnonymousAuthenticationFeature _owner; + + public FeatureTaskList(AnonymousAuthenticationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + } + + private FeatureTaskList _taskList; + + public AnonymousAuthenticationFeature(Module module) : base(module) + { + } + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/authentication/anonymousAuthentication"); + var anonymousEnabled = (bool)section["enabled"]; + SetEnabled(anonymousEnabled); + } + + private void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var anonymousSection = service.GetSection("system.webServer/security/authentication/anonymousAuthentication"); + anonymousSection["enabled"] = true; + service.ServerManager.CommitChanges(); + SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var anonymousSection = service.GetSection("system.webServer/security/authentication/anonymousAuthentication"); + anonymousSection["enabled"] = false; + service.ServerManager.CommitChanges(); + SetEnabled(false); + } + + private void Edit() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var anonymousSection = service.GetSection("system.webServer/security/authentication/anonymousAuthentication"); + var dialog = new AnonymousEditDialog(this.Module, new AnonymousItem(anonymousSection)); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + OnAuthenticationSettingsSaved(); + } + + public override Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Anonymous"); + return true; + } + + public override bool IsFeatureEnabled + { + get { return true; } + } + + public override AuthenticationType AuthenticationType + { + get { return AuthenticationType.Other; } + } + + public override string Name + { + get { return "Anonymous Authentication"; } + } + } +} diff --git a/JexusManager.Features.Authentication/AnonymousEditDialog.Designer.cs b/JexusManager.Features.Authentication/AnonymousEditDialog.Designer.cs new file mode 100644 index 00000000..6db95fde --- /dev/null +++ b/JexusManager.Features.Authentication/AnonymousEditDialog.Designer.cs @@ -0,0 +1,144 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AnonymousEditDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.rbSpecific = new System.Windows.Forms.RadioButton(); + this.rbPool = new System.Windows.Forms.RadioButton(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnSet = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(7, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(124, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Anonymous user identity:"; + // + // rbSpecific + // + this.rbSpecific.AutoSize = true; + this.rbSpecific.Location = new System.Drawing.Point(10, 42); + this.rbSpecific.Name = "rbSpecific"; + this.rbSpecific.Size = new System.Drawing.Size(89, 17); + this.rbSpecific.TabIndex = 1; + this.rbSpecific.TabStop = true; + this.rbSpecific.Text = "Specific user:"; + this.rbSpecific.UseVisualStyleBackColor = true; + // + // rbPool + // + this.rbPool.AutoSize = true; + this.rbPool.Location = new System.Drawing.Point(10, 111); + this.rbPool.Name = "rbPool"; + this.rbPool.Size = new System.Drawing.Size(136, 17); + this.rbPool.TabIndex = 2; + this.rbPool.TabStop = true; + this.rbPool.Text = "Application pool identity"; + this.rbPool.UseVisualStyleBackColor = true; + // + // txtName + // + this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtName.Location = new System.Drawing.Point(29, 74); + this.txtName.Name = "txtName"; + this.txtName.ReadOnly = true; + this.txtName.Size = new System.Drawing.Size(275, 20); + this.txtName.TabIndex = 3; + // + // btnSet + // + this.btnSet.Location = new System.Drawing.Point(310, 72); + this.btnSet.Name = "btnSet"; + this.btnSet.Size = new System.Drawing.Size(95, 23); + this.btnSet.TabIndex = 4; + this.btnSet.Text = "Set..."; + this.btnSet.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(313, 146); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(92, 23); + this.btnCancel.TabIndex = 5; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(217, 146); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // AnonymousEditDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(414, 181); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnSet); + this.Controls.Add(this.txtName); + this.Controls.Add(this.rbPool); + this.Controls.Add(this.rbSpecific); + this.Controls.Add(this.label1); + this.Name = "AnonymousEditDialog"; + this.Text = "Edit Anonymous Authentication Credentials"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AnonymousEditDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private RadioButton rbSpecific; + private RadioButton rbPool; + private TextBox txtName; + private Button btnSet; + private Button btnCancel; + private Button btnOK; + } +} diff --git a/JexusManager.Features.Authentication/AnonymousEditDialog.cs b/JexusManager.Features.Authentication/AnonymousEditDialog.cs new file mode 100644 index 00000000..6220a769 --- /dev/null +++ b/JexusManager.Features.Authentication/AnonymousEditDialog.cs @@ -0,0 +1,98 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using System.Reactive.Linq; + using System.Reactive.Disposables; + public partial class AnonymousEditDialog : DialogForm + { + public AnonymousEditDialog(IServiceProvider serviceProvider, AnonymousItem existing) + : base(serviceProvider) + { + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + rbPool.Checked = txtName.Text.Length == 0; + })); + + container.Add( + Observable.FromEventPattern(btnSet, "Click") + .Subscribe(evt => + { + var dialog = new CredentialsDialog(ServiceProvider, existing.Name); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + txtName.Text = dialog.UserName; + existing.Name = dialog.UserName; + existing.Password = dialog.Password; + SetButton(); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + DialogResult = DialogResult.OK; + if (rbPool.Checked) + { + existing.Name = string.Empty; + // TODO: reset password. + existing.Password = null; + } + + existing.Apply(); + })); + + container.Add( + Observable.FromEventPattern(rbPool, "CheckedChanged") + .Merge(Observable.FromEventPattern(rbSpecific, "CheckedChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnSet.Enabled = !rbPool.Checked; + SetButton(); + var toElevate = !rbPool.Checked; + if (toElevate) + { + NativeMethods.TryAddShieldToButton(btnOK); + } + else + { + NativeMethods.RemoveShieldFromButton(btnOK); + } + })); + + txtName.Text = "test"; // IMPORTANT: trigger a change event. + txtName.Text = existing.Name; + } + + private void SetButton() + { + // TODO: disable if not elevated. Need to find an in-place elevation approach. + btnOK.Enabled = rbPool.Checked || (txtName.Text.Length != 0 && NativeMethods.IsProcessElevated); + } + + private void AnonymousEditDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Anonymous"); + } + } +} diff --git a/JexusManager.Features.Authentication/AnonymousEditDialog.resx b/JexusManager.Features.Authentication/AnonymousEditDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authentication/AnonymousEditDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/AnonymousItem.cs b/JexusManager.Features.Authentication/AnonymousItem.cs new file mode 100644 index 00000000..61a66ae2 --- /dev/null +++ b/JexusManager.Features.Authentication/AnonymousItem.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using Microsoft.Web.Administration; + + public class AnonymousItem + { + public ConfigurationElement Element { get; set; } + + public AnonymousItem(ConfigurationElement element) + { + this.Element = element; + Name = (string)element["userName"]; + Password = (string)element["password"]; + } + + public string Name { get; set; } + public string Password { get; set; } + + public void Apply() + { + Element["userName"] = this.Name; + Element["password"] = this.Password; + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Authentication/AuthenticationModule.cs b/JexusManager.Features.Authentication/AuthenticationModule.cs new file mode 100644 index 00000000..d61d8be7 --- /dev/null +++ b/JexusManager.Features.Authentication/AuthenticationModule.cs @@ -0,0 +1,22 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class AuthenticationModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(AuthenticationPage), "Authentication", "Configure authentication settings for sites and applications", Resources.authentication_36, Resources.authentication_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Authentication/AuthenticationModuleProvider.cs b/JexusManager.Features.Authentication/AuthenticationModuleProvider.cs new file mode 100644 index 00000000..24a324b2 --- /dev/null +++ b/JexusManager.Features.Authentication/AuthenticationModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + + using Microsoft.Web.Management.Server; + + public class AuthenticationModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(AuthenticationModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Authentication/AuthenticationPage.Designer.cs b/JexusManager.Features.Authentication/AuthenticationPage.Designer.cs new file mode 100644 index 00000000..94144cbd --- /dev/null +++ b/JexusManager.Features.Authentication/AuthenticationPage.Designer.cs @@ -0,0 +1,286 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AuthenticationPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chResponse = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + this.btnHelp = new System.Windows.Forms.ToolStripButton(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.tsActionPanel.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 3; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitContainer1_SplitterMoved); + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 9; + this.pictureBox1.TabStop = false; + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 8; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chStatus, + this.chResponse}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 25); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 515); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 200; + // + // chStatus + // + this.chStatus.Text = "Status"; + this.chStatus.Width = 100; + // + // chResponse + // + this.chResponse.Text = "Response Type"; + this.chResponse.Width = 150; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel3, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 0); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel3 + // + this.toolStripLabel3.Name = "toolStripLabel3"; + this.toolStripLabel3.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel3.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Category", + "Area"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(149, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Authentication"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.btnHelp}); + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // btnHelp + // + this.btnHelp.Name = "btnHelp"; + this.btnHelp.Size = new System.Drawing.Size(214, 4); + // + // AuthenticationPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "AuthenticationPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.tsActionPanel.ResumeLayout(false); + this.tsActionPanel.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chName; + private ColumnHeader chStatus; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private ToolStripButton btnHelp; + private Label label3; + private PictureBox pictureBox1; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel3; + private ToolStripComboBox cbGroup; + private ColumnHeader chResponse; + private ContextMenuStrip cmsActionPanel; + } +} diff --git a/JexusManager.Features.Authentication/AuthenticationPage.cs b/JexusManager.Features.Authentication/AuthenticationPage.cs new file mode 100644 index 00000000..ada0ae90 --- /dev/null +++ b/JexusManager.Features.Authentication/AuthenticationPage.cs @@ -0,0 +1,177 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class AuthenticationPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly AuthenticationPage _owner; + + public PageTaskList(AuthenticationPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class AuthenticationListViewItem : ListViewItem + { + private readonly AuthenticationPage _page; + private readonly ListViewSubItem _status; + + public AuthenticationListViewItem(AuthenticationFeature feature, AuthenticationPage page) + : base(feature.Name) + { + _page = page; + Feature = feature; + _status = new ListViewSubItem(this, feature.IsEnabled ? "Enabled" : "Disabled"); + SubItems.Add(_status); + SubItems.Add(new ListViewSubItem(this, GetString(feature.AuthenticationType))); + feature.AuthenticationSettingsUpdated = AuthenticationSettingsUpdated; + } + + private void AuthenticationSettingsUpdated() + { + var service = _page.GetService(typeof(IConfigurationService)) as IConfigurationService; + service?.ServerManager.CommitChanges(); + + _status.Text = Feature.IsEnabled ? "Enabled" : "Disabled"; + _page.ListView1SelectedIndexChanged(ListView, EventArgs.Empty); + } + + private string GetString(AuthenticationType authenticationType) + { + switch (authenticationType) + { + case AuthenticationType.ChallengeBase: + case AuthenticationType.ClientCertificateBased: + return "HTTP 401 Challenge"; + case AuthenticationType.LoginRedirectBased: + return "HTTP 302 Login/Redirect"; + case AuthenticationType.Other: + return string.Empty; + default: + throw new ArgumentOutOfRangeException(nameof(authenticationType)); + } + } + + public AuthenticationFeature Feature { get; } + } + + private TaskList _taskList; + + public AuthenticationPage() + { + InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + var certificateFeature = new ClientCertificateAuthenticationFeature(Module); + listView1.Items.Add(new AuthenticationListViewItem(certificateFeature, this)); + + var anonymousFeature = new AnonymousAuthenticationFeature(Module); + listView1.Items.Add(new AuthenticationListViewItem(anonymousFeature, this)); + + var impersonationFeature = new ImpersonationFeature(Module); + listView1.Items.Add(new AuthenticationListViewItem(impersonationFeature, this)); + + var basicFeature = new BasicAuthenticationFeature(Module); + listView1.Items.Add(new AuthenticationListViewItem(basicFeature, this)); + + var digestFeature = new DigestAuthenticationFeature(Module); + listView1.Items.Add(new AuthenticationListViewItem(digestFeature, this)); + + var formsFeature = new FormsAuthenticationFeature(Module); + listView1.Items.Add(new AuthenticationListViewItem(formsFeature, this)); + + var windowsFeature = new WindowsAuthenticationFeature(Module); + listView1.Items.Add(new AuthenticationListViewItem(windowsFeature, this)); + + certificateFeature.Load(); + anonymousFeature.Load(); + impersonationFeature.Load(); + basicFeature.Load(); + digestFeature.Load(); + formsFeature.Load(); + windowsFeature.Load(); + + InitializeListPage(); + } + + protected override void InitializeListPage() + { + TaskList extra = null; + if (listView1.SelectedItems.Count != 0) + { + var item = (AuthenticationListViewItem)listView1.SelectedItems[0]; + extra = item.Feature.GetTaskList(); + } + + Tasks.Fill(tsActionPanel, cmsActionPanel, extra); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + InitializeListPage(); + } + + protected override bool ShowHelp() + { + if (listView1.SelectedItems.Count == 0) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461"); + return true; + } + + return ((AuthenticationListViewItem)listView1.SelectedItems[0]).Feature.ShowHelp(); + } + + private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.Authentication/AuthenticationPage.resx b/JexusManager.Features.Authentication/AuthenticationPage.resx new file mode 100644 index 00000000..3046fb13 --- /dev/null +++ b/JexusManager.Features.Authentication/AuthenticationPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 807, 17 + + + 702, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/BasicAuthenticationFeature.cs b/JexusManager.Features.Authentication/BasicAuthenticationFeature.cs new file mode 100644 index 00000000..7cf619d7 --- /dev/null +++ b/JexusManager.Features.Authentication/BasicAuthenticationFeature.cs @@ -0,0 +1,145 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class BasicAuthenticationFeature : AuthenticationFeature + { + private FeatureTaskList _taskList; + + private sealed class FeatureTaskList : TaskList + { + private readonly BasicAuthenticationFeature _owner; + + public FeatureTaskList(BasicAuthenticationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + } + + public BasicAuthenticationFeature(Module module) : base(module) + { + } + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/authentication/basicAuthentication"); + var basicEnabled = (bool)section["enabled"]; + SetEnabled(basicEnabled); + } + + private void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var basicSection = service.GetSection("system.webServer/security/authentication/basicAuthentication"); + basicSection["enabled"] = true; + service.ServerManager.CommitChanges(); + SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var basicSection = service.GetSection("system.webServer/security/authentication/basicAuthentication"); + basicSection["enabled"] = false; + service.ServerManager.CommitChanges(); + SetEnabled(false); + } + + private void Edit() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var basicSection = service.GetSection("system.webServer/security/authentication/basicAuthentication"); + var dialog = new BasicEditDialog(this.Module, new BasicItem(basicSection)); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + OnAuthenticationSettingsSaved(); + } + + public override Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Basic"); + return true; + } + + public override bool IsFeatureEnabled + { + get { return true; } + } + + public override AuthenticationType AuthenticationType + { + get { return AuthenticationType.ChallengeBase; } + } + + public override string Name + { + get { return "Basic Authentication"; } + } + } +} diff --git a/JexusManager.Features.Authentication/BasicEditDialog.Designer.cs b/JexusManager.Features.Authentication/BasicEditDialog.Designer.cs new file mode 100644 index 00000000..2ce880bd --- /dev/null +++ b/JexusManager.Features.Authentication/BasicEditDialog.Designer.cs @@ -0,0 +1,123 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class BasicEditDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.txtDomain = new System.Windows.Forms.TextBox(); + this.txtRealm = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(257, 131); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(161, 131); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(81, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Default domain:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 64); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(40, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Realm:"; + // + // txtDomain + // + this.txtDomain.Location = new System.Drawing.Point(16, 29); + this.txtDomain.Name = "txtDomain"; + this.txtDomain.Size = new System.Drawing.Size(336, 20); + this.txtDomain.TabIndex = 4; + // + // txtRealm + // + this.txtRealm.Location = new System.Drawing.Point(16, 80); + this.txtRealm.Name = "txtRealm"; + this.txtRealm.Size = new System.Drawing.Size(336, 20); + this.txtRealm.TabIndex = 5; + // + // BasicEditDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(364, 166); + this.Controls.Add(this.txtRealm); + this.Controls.Add(this.txtDomain); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "BasicEditDialog"; + this.Text = "Edit Basic Authentication Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.BasicEditDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private Label label2; + private TextBox txtDomain; + private TextBox txtRealm; + } +} diff --git a/JexusManager.Features.Authentication/BasicEditDialog.cs b/JexusManager.Features.Authentication/BasicEditDialog.cs new file mode 100644 index 00000000..a8de342c --- /dev/null +++ b/JexusManager.Features.Authentication/BasicEditDialog.cs @@ -0,0 +1,43 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using System.Reactive.Linq; + using System.Reactive.Disposables; + public partial class BasicEditDialog : DialogForm + { + public BasicEditDialog(IServiceProvider serviceProvider, BasicItem existing) + : base(serviceProvider) + { + InitializeComponent(); + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + txtDomain.Text = existing.Domain; + txtRealm.Text = existing.Realm; + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + existing.Domain = txtDomain.Text; + existing.Realm = txtRealm.Text; + existing.Apply(); + DialogResult = DialogResult.OK; + })); + } + + private void BasicEditDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Basic"); + } + } +} diff --git a/JexusManager.Features.Authentication/BasicEditDialog.resx b/JexusManager.Features.Authentication/BasicEditDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authentication/BasicEditDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/BasicItem.cs b/JexusManager.Features.Authentication/BasicItem.cs new file mode 100644 index 00000000..0da02a0f --- /dev/null +++ b/JexusManager.Features.Authentication/BasicItem.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using Microsoft.Web.Administration; + + public class BasicItem + { + public ConfigurationElement Element { get; set; } + + public BasicItem(ConfigurationElement element) + { + this.Element = element; + Domain = (string)element["defaultLogonDomain"]; + Realm = (string)element["realm"]; + } + + public string Domain { get; set; } + public string Realm { get; set; } + + public void Apply() + { + Element["defaultLogonDomain"] = Domain; + Element["realm"] = Realm; + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Authentication/ClientCertificateAuthenticationFeature.cs b/JexusManager.Features.Authentication/ClientCertificateAuthenticationFeature.cs new file mode 100644 index 00000000..c7baf548 --- /dev/null +++ b/JexusManager.Features.Authentication/ClientCertificateAuthenticationFeature.cs @@ -0,0 +1,123 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class ClientCertificateAuthenticationFeature : AuthenticationFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly ClientCertificateAuthenticationFeature _owner; + + public FeatureTaskList(ClientCertificateAuthenticationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + } + + private FeatureTaskList _taskList; + + public ClientCertificateAuthenticationFeature(Module module) : base(module) + { + } + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var anonymousSection = service.GetSection("system.webServer/security/authentication/clientCertificateMappingAuthentication"); + var anonymousEnabled = (bool)anonymousSection["enabled"]; + SetEnabled(anonymousEnabled); + } + + private void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var anonymousSection = service.GetSection("system.webServer/security/authentication/clientCertificateMappingAuthentication"); + anonymousSection["enabled"] = true; + service.ServerManager.CommitChanges(); + SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var anonymousSection = service.GetSection("system.webServer/security/authentication/clientCertificateMappingAuthentication"); + anonymousSection["enabled"] = false; + service.ServerManager.CommitChanges(); + SetEnabled(false); + } + + public override Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Active_Directory"); + return true; + } + + public override bool IsFeatureEnabled + { + get { return true; } + } + + public override AuthenticationType AuthenticationType + { + get { return AuthenticationType.ClientCertificateBased; } + } + + public override string Name + { + get { return "Active Directory Client Certificate Authentication"; } + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Authentication/CredentialsDialog.Designer.cs b/JexusManager.Features.Authentication/CredentialsDialog.Designer.cs new file mode 100644 index 00000000..85db7cbd --- /dev/null +++ b/JexusManager.Features.Authentication/CredentialsDialog.Designer.cs @@ -0,0 +1,147 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class CredentialsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtPassword = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtConfirm = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(207, 176); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(111, 176); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(61, 13); + this.label1.TabIndex = 2; + this.label1.Text = "User name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(16, 29); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(286, 20); + this.txtName.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 67); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(56, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Password:"; + // + // txtPassword + // + this.txtPassword.Location = new System.Drawing.Point(16, 83); + this.txtPassword.Name = "txtPassword"; + this.txtPassword.PasswordChar = '*'; + this.txtPassword.Size = new System.Drawing.Size(286, 20); + this.txtPassword.TabIndex = 5; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(13, 122); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(93, 13); + this.label3.TabIndex = 6; + this.label3.Text = "Confirm password:"; + // + // txtConfirm + // + this.txtConfirm.Location = new System.Drawing.Point(16, 138); + this.txtConfirm.Name = "txtConfirm"; + this.txtConfirm.PasswordChar = '*'; + this.txtConfirm.Size = new System.Drawing.Size(286, 20); + this.txtConfirm.TabIndex = 7; + // + // CredentialsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(314, 211); + this.Controls.Add(this.txtConfirm); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtPassword); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "CredentialsDialog"; + this.Text = "Set Credentials"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.CredentialsDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private TextBox txtName; + private Label label2; + private TextBox txtPassword; + private Label label3; + private TextBox txtConfirm; + } +} diff --git a/JexusManager.Features.Authentication/CredentialsDialog.cs b/JexusManager.Features.Authentication/CredentialsDialog.cs new file mode 100644 index 00000000..be215090 --- /dev/null +++ b/JexusManager.Features.Authentication/CredentialsDialog.cs @@ -0,0 +1,60 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using System.Reactive.Linq; + using System.Reactive.Disposables; + public partial class CredentialsDialog : DialogForm + { + public CredentialsDialog(IServiceProvider serviceProvider, string name) + : base(serviceProvider) + { + InitializeComponent(); + btnOK.Enabled = false; + txtName.Text = name; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtPassword, "TextChanged")) + .Merge(Observable.FromEventPattern(txtConfirm, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtPassword.Text) + && txtConfirm.Text == txtPassword.Text; + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + UserName = txtName.Text; + Password = txtPassword.Text; + // TODO: verify user + // DialogResult = DialogResult.Cancel; + DialogResult = DialogResult.OK; + })); + } + + private void CredentialsDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Anonymous"); + } + + public string Password { get; set; } + + public string UserName { get; set; } + } +} diff --git a/JexusManager.Features.Authentication/CredentialsDialog.resx b/JexusManager.Features.Authentication/CredentialsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authentication/CredentialsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/DigestAuthenticationFeature.cs b/JexusManager.Features.Authentication/DigestAuthenticationFeature.cs new file mode 100644 index 00000000..0a2d4425 --- /dev/null +++ b/JexusManager.Features.Authentication/DigestAuthenticationFeature.cs @@ -0,0 +1,145 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class DigestAuthenticationFeature : AuthenticationFeature + { + private FeatureTaskList _taskList; + + private sealed class FeatureTaskList : TaskList + { + private readonly DigestAuthenticationFeature _owner; + + public FeatureTaskList(DigestAuthenticationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + } + + public DigestAuthenticationFeature(Module module) : base(module) + { + } + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var digestSection = service.GetSection("system.webServer/security/authentication/digestAuthentication"); + var digestEnabled = (bool)digestSection["enabled"]; + SetEnabled(digestEnabled); + } + + private void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var digestSection = service.GetSection("system.webServer/security/authentication/digestAuthentication"); + digestSection["enabled"] = true; + service.ServerManager.CommitChanges(); + SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var digestSection = service.GetSection("system.webServer/security/authentication/digestAuthentication"); + digestSection["enabled"] = false; + service.ServerManager.CommitChanges(); + SetEnabled(false); + } + + private void Edit() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var digestSection = service.GetSection("system.webServer/security/authentication/digestAuthentication"); + var dialog = new DigestEditDialog(this.Module, new DigestItem(digestSection)); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + OnAuthenticationSettingsSaved(); + } + + public override Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Digest"); + return true; + } + + public override bool IsFeatureEnabled + { + get { return true; } + } + + public override AuthenticationType AuthenticationType + { + get { return AuthenticationType.ChallengeBase; } + } + + public override string Name + { + get { return "Digest Authentication"; } + } + } +} diff --git a/JexusManager.Features.Authentication/DigestEditDialog.Designer.cs b/JexusManager.Features.Authentication/DigestEditDialog.Designer.cs new file mode 100644 index 00000000..82333c42 --- /dev/null +++ b/JexusManager.Features.Authentication/DigestEditDialog.Designer.cs @@ -0,0 +1,101 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class DigestEditDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.txtRealm = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(257, 79); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(161, 79); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 12); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(40, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Realm:"; + // + // txtRealm + // + this.txtRealm.Location = new System.Drawing.Point(16, 28); + this.txtRealm.Name = "txtRealm"; + this.txtRealm.Size = new System.Drawing.Size(336, 20); + this.txtRealm.TabIndex = 5; + // + // DigestEditDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(364, 116); + this.Controls.Add(this.txtRealm); + this.Controls.Add(this.label2); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "DigestEditDialog"; + this.Text = "Edit Digest Authentication Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.BasicEditDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label2; + private TextBox txtRealm; + } +} diff --git a/JexusManager.Features.Authentication/DigestEditDialog.cs b/JexusManager.Features.Authentication/DigestEditDialog.cs new file mode 100644 index 00000000..e4324f0f --- /dev/null +++ b/JexusManager.Features.Authentication/DigestEditDialog.cs @@ -0,0 +1,42 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using System.Reactive.Disposables; + public partial class DigestEditDialog : DialogForm + { + public DigestEditDialog(IServiceProvider serviceProvider, DigestItem item) + : base(serviceProvider) + { + InitializeComponent(); + txtRealm.Text = item.Realm; + txtRealm.SelectAll(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + item.Realm = txtRealm.Text; + item.Apply(); + DialogResult = DialogResult.OK; + })); + } + + private void BasicEditDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Digest"); + } + } +} diff --git a/JexusManager.Features.Authentication/DigestEditDialog.resx b/JexusManager.Features.Authentication/DigestEditDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authentication/DigestEditDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/DigestItem.cs b/JexusManager.Features.Authentication/DigestItem.cs new file mode 100644 index 00000000..75738d58 --- /dev/null +++ b/JexusManager.Features.Authentication/DigestItem.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using Microsoft.Web.Administration; + + public class DigestItem + { + public ConfigurationElement Element { get; set; } + + public DigestItem(ConfigurationElement element) + { + this.Element = element; + Realm = (string)element["realm"]; + } + + public string Realm { get; set; } + + public void Apply() + { + Element["realm"] = Realm; + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Authentication/FormsAuthenticationFeature.cs b/JexusManager.Features.Authentication/FormsAuthenticationFeature.cs new file mode 100644 index 00000000..94349ee0 --- /dev/null +++ b/JexusManager.Features.Authentication/FormsAuthenticationFeature.cs @@ -0,0 +1,146 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class FormsAuthenticationFeature : AuthenticationFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly FormsAuthenticationFeature _owner; + + public FeatureTaskList(FormsAuthenticationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + } + + private FeatureTaskList _taskList; + + public FormsAuthenticationFeature(Module module) + : base(module) + { + } + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/authentication"); + var enabled = 3L == (long)section["mode"]; + this.SetEnabled(enabled); + } + + private void Enable() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/authentication"); + section["mode"] = "Forms"; + service.ServerManager.CommitChanges(); + this.SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/authentication"); + section["mode"] = "Windows"; + service.ServerManager.CommitChanges(); + this.SetEnabled(false); + } + + private void Edit() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/authentication"); + var dialog = new FormsEditDialog(this.Module, new FormsItem(section.GetChildElement("forms"))); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + this.OnAuthenticationSettingsSaved(); + } + + public override Version MinimumFrameworkVersion + { + get { return FxVersion20; } + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Forms"); + return true; + } + + public override bool IsFeatureEnabled + { + get { return true; } + } + + public override AuthenticationType AuthenticationType + { + get { return AuthenticationType.LoginRedirectBased; } + } + + public override string Name + { + get { return "Forms Authentication"; } + } + } +} diff --git a/JexusManager.Features.Authentication/FormsEditDialog.Designer.cs b/JexusManager.Features.Authentication/FormsEditDialog.Designer.cs new file mode 100644 index 00000000..69629ff8 --- /dev/null +++ b/JexusManager.Features.Authentication/FormsEditDialog.Designer.cs @@ -0,0 +1,245 @@ +namespace JexusManager.Features.Authentication +{ + using System.Windows.Forms; + + partial class FormsEditDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtURL = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtTimeout = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.cbExpire = new System.Windows.Forms.CheckBox(); + this.cbSSL = new System.Windows.Forms.CheckBox(); + this.cbProtectedMode = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.cbMode = new System.Windows.Forms.ComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(287, 396); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(191, 396); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 18); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(61, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Login URL:"; + // + // txtURL + // + this.txtURL.Location = new System.Drawing.Point(12, 34); + this.txtURL.Name = "txtURL"; + this.txtURL.Size = new System.Drawing.Size(209, 20); + this.txtURL.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 70); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(209, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Authentication cookie time-out (in minutes):"; + // + // txtTimeout + // + this.txtTimeout.Location = new System.Drawing.Point(12, 86); + this.txtTimeout.Name = "txtTimeout"; + this.txtTimeout.Size = new System.Drawing.Size(92, 20); + this.txtTimeout.TabIndex = 5; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.cbExpire); + this.groupBox1.Controls.Add(this.cbSSL); + this.groupBox1.Controls.Add(this.cbProtectedMode); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.txtName); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.cbMode); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Location = new System.Drawing.Point(12, 112); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(370, 264); + this.groupBox1.TabIndex = 6; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Cookie settings"; + // + // cbExpire + // + this.cbExpire.AutoSize = true; + this.cbExpire.Location = new System.Drawing.Point(19, 224); + this.cbExpire.Name = "cbExpire"; + this.cbExpire.Size = new System.Drawing.Size(224, 17); + this.cbExpire.TabIndex = 7; + this.cbExpire.Text = "Extend cookie expiration on every request"; + this.cbExpire.UseVisualStyleBackColor = true; + // + // cbSSL + // + this.cbSSL.AutoSize = true; + this.cbSSL.Location = new System.Drawing.Point(19, 190); + this.cbSSL.Name = "cbSSL"; + this.cbSSL.Size = new System.Drawing.Size(86, 17); + this.cbSSL.TabIndex = 6; + this.cbSSL.Text = "Require SSL"; + this.cbSSL.UseVisualStyleBackColor = true; + // + // cbProtectedMode + // + this.cbProtectedMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbProtectedMode.FormattingEnabled = true; + this.cbProtectedMode.Items.AddRange(new object[] { + "Encryption and validation", + "None", + "Encryption", + "Validation"}); + this.cbProtectedMode.Location = new System.Drawing.Point(19, 153); + this.cbProtectedMode.Name = "cbProtectedMode"; + this.cbProtectedMode.Size = new System.Drawing.Size(176, 21); + this.cbProtectedMode.TabIndex = 5; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(16, 137); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(85, 13); + this.label5.TabIndex = 4; + this.label5.Text = "Protected mode:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(19, 97); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(176, 20); + this.txtName.TabIndex = 3; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(16, 81); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(38, 13); + this.label4.TabIndex = 2; + this.label4.Text = "Name:"; + // + // cbMode + // + this.cbMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbMode.FormattingEnabled = true; + this.cbMode.Items.AddRange(new object[] { + "Do not use cookies", + "Use cookies", + "Auto Detect", + "Use device profile"}); + this.cbMode.Location = new System.Drawing.Point(19, 43); + this.cbMode.Name = "cbMode"; + this.cbMode.Size = new System.Drawing.Size(176, 21); + this.cbMode.TabIndex = 1; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(16, 27); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(37, 13); + this.label3.TabIndex = 0; + this.label3.Text = "Mode:"; + // + // FormsEditDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(394, 431); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.txtTimeout); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtURL); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "FormsEditDialog"; + this.Text = "Edit Forms Authentication Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.FormsEditDialogHelpButtonClicked); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtURL; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txtTimeout; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.CheckBox cbExpire; + private System.Windows.Forms.CheckBox cbSSL; + private System.Windows.Forms.ComboBox cbProtectedMode; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox txtName; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox cbMode; + private System.Windows.Forms.Label label3; + } +} diff --git a/JexusManager.Features.Authentication/FormsEditDialog.cs b/JexusManager.Features.Authentication/FormsEditDialog.cs new file mode 100644 index 00000000..dbf90d3b --- /dev/null +++ b/JexusManager.Features.Authentication/FormsEditDialog.cs @@ -0,0 +1,98 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.ComponentModel; + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Diagnostics; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using System.Reactive.Disposables; + public partial class FormsEditDialog : DialogForm + { + public FormsEditDialog(IServiceProvider serviceProvider, FormsItem existing) + : base(serviceProvider) + { + InitializeComponent(); + + txtURL.Text = existing.LoginUrl; + txtTimeout.Text = TimeSpanExtensions.GetTotalMinutes(existing.Timeout).ToString(); + + cbMode.SelectedIndex = (int)existing.Mode; + txtName.Text = existing.Name; + cbProtectedMode.SelectedIndex = (int)existing.ProtectedMode; + cbSSL.Checked = existing.RequireSsl; + cbExpire.Checked = existing.SlidinngExpiration; + btnOK.Enabled = false; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (string.IsNullOrWhiteSpace(txtURL.Text)) + { + ShowMessage( + "You must specify a login page URL.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + int minutes; + if (!int.TryParse(txtTimeout.Text, out minutes)) + { + ShowMessage( + "You must specify a valid, positive integer for the authentication time-out.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + if (string.IsNullOrWhiteSpace(txtName.Text)) + { + ShowMessage( + "You must specify a cookie name.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + existing.LoginUrl = txtURL.Text; + existing.Timeout = new TimeSpan(0, 0, minutes, 0); + existing.Mode = cbMode.SelectedIndex; + existing.Name = txtName.Text; + existing.ProtectedMode = cbProtectedMode.SelectedIndex; + existing.RequireSsl = cbSSL.Checked; + existing.SlidinngExpiration = cbExpire.Checked; + existing.Apply(); + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(cbSSL, "CheckedChanged") + .Merge(Observable.FromEventPattern(txtURL, "TextChanged")) + .Merge(Observable.FromEventPattern(cbMode, "SelectedIndexChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = true; + })); + } + + private void FormsEditDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Forms"); + } + } +} diff --git a/JexusManager.Features.Authentication/FormsEditDialog.resx b/JexusManager.Features.Authentication/FormsEditDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authentication/FormsEditDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/FormsItem.cs b/JexusManager.Features.Authentication/FormsItem.cs new file mode 100644 index 00000000..70f01e0a --- /dev/null +++ b/JexusManager.Features.Authentication/FormsItem.cs @@ -0,0 +1,53 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + + using Microsoft.Web.Administration; + + + public class FormsItem + { + public ConfigurationElement Element { get; set; } + + public FormsItem(ConfigurationElement element) + { + this.Element = element; + LoginUrl = (string)element["loginUrl"]; + Timeout = (TimeSpan)element["timeout"]; + Mode = (long)element["cookieless"]; + Name = (string)element["name"]; + ProtectedMode = (long)element["protection"]; + RequireSsl = (bool)element["requireSSL"]; + SlidinngExpiration = (bool)element["slidingExpiration"]; + } + + public long Mode { get; set; } + + public long ProtectedMode { get; set; } + + public bool RequireSsl { get; set; } + + public bool SlidinngExpiration { get; set; } + + public string Name { get; set; } + + public TimeSpan Timeout { get; set; } + + public string LoginUrl { get; set; } + + public void Apply() + { + Element["loginUrl"] = LoginUrl; + Element["timeout"] = Timeout; + Element["cookieless"] = Mode; + Element["name"] = Name; + Element["protection"] = ProtectedMode; + Element["requireSSL"] = RequireSsl; + Element["slidingExpiration"] = SlidinngExpiration; + } + } +} diff --git a/JexusManager.Features.Authentication/ImpersonationEditDialog.Designer.cs b/JexusManager.Features.Authentication/ImpersonationEditDialog.Designer.cs new file mode 100644 index 00000000..36c3314f --- /dev/null +++ b/JexusManager.Features.Authentication/ImpersonationEditDialog.Designer.cs @@ -0,0 +1,144 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class ImpersonationEditDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.rbSpecific = new System.Windows.Forms.RadioButton(); + this.rbPool = new System.Windows.Forms.RadioButton(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnSet = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(7, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(116, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Identity to impersonate:"; + // + // rbSpecific + // + this.rbSpecific.AutoSize = true; + this.rbSpecific.Location = new System.Drawing.Point(10, 42); + this.rbSpecific.Name = "rbSpecific"; + this.rbSpecific.Size = new System.Drawing.Size(89, 17); + this.rbSpecific.TabIndex = 1; + this.rbSpecific.TabStop = true; + this.rbSpecific.Text = "Specific user:"; + this.rbSpecific.UseVisualStyleBackColor = true; + // + // rbPool + // + this.rbPool.AutoSize = true; + this.rbPool.Location = new System.Drawing.Point(10, 111); + this.rbPool.Name = "rbPool"; + this.rbPool.Size = new System.Drawing.Size(114, 17); + this.rbPool.TabIndex = 2; + this.rbPool.TabStop = true; + this.rbPool.Text = "Authenticated user"; + this.rbPool.UseVisualStyleBackColor = true; + // + // txtName + // + this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtName.Location = new System.Drawing.Point(29, 74); + this.txtName.Name = "txtName"; + this.txtName.ReadOnly = true; + this.txtName.Size = new System.Drawing.Size(275, 20); + this.txtName.TabIndex = 3; + // + // btnSet + // + this.btnSet.Location = new System.Drawing.Point(310, 72); + this.btnSet.Name = "btnSet"; + this.btnSet.Size = new System.Drawing.Size(95, 23); + this.btnSet.TabIndex = 4; + this.btnSet.Text = "Set..."; + this.btnSet.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(313, 146); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(92, 23); + this.btnCancel.TabIndex = 5; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(217, 146); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // ImpersonationEditDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(414, 181); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnSet); + this.Controls.Add(this.txtName); + this.Controls.Add(this.rbPool); + this.Controls.Add(this.rbSpecific); + this.Controls.Add(this.label1); + this.Name = "ImpersonationEditDialog"; + this.Text = "Edit ASP.NET Impersonation Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AnonymousEditDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private RadioButton rbSpecific; + private RadioButton rbPool; + private TextBox txtName; + private Button btnSet; + private Button btnCancel; + private Button btnOK; + } +} diff --git a/JexusManager.Features.Authentication/ImpersonationEditDialog.cs b/JexusManager.Features.Authentication/ImpersonationEditDialog.cs new file mode 100644 index 00000000..2db3ee20 --- /dev/null +++ b/JexusManager.Features.Authentication/ImpersonationEditDialog.cs @@ -0,0 +1,99 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using System.Reactive.Disposables; + + public partial class ImpersonationEditDialog : DialogForm + { + public ImpersonationEditDialog(IServiceProvider serviceProvider, ImpersonationItem existing) + : base(serviceProvider) + { + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + rbPool.Checked = txtName.Text.Length == 0; + })); + + container.Add( + Observable.FromEventPattern(btnSet, "Click") + .Subscribe(evt => + { + var dialog = new CredentialsDialog(ServiceProvider, existing.Name); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + txtName.Text = dialog.UserName; + existing.Name = dialog.UserName; + existing.Password = dialog.Password; + SetButton(); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + DialogResult = DialogResult.OK; + if (rbPool.Checked) + { + existing.Name = string.Empty; + // TODO: reset password. + existing.Password = null; + } + + existing.Apply(); + })); + + container.Add( + Observable.FromEventPattern(rbPool, "CheckedChanged") + .Merge(Observable.FromEventPattern(rbSpecific, "CheckedChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnSet.Enabled = !rbPool.Checked; + SetButton(); + var toElevate = !rbPool.Checked; + if (toElevate) + { + NativeMethods.TryAddShieldToButton(btnOK); + } + else + { + NativeMethods.RemoveShieldFromButton(btnOK); + } + })); + + txtName.Text = "test"; // IMPORTANT: trigger a change event. + txtName.Text = existing.Name; + } + + private void SetButton() + { + // TODO: disable if not elevated. Need to find an in-place elevation approach. + btnOK.Enabled = rbPool.Checked || (txtName.Text.Length != 0 && NativeMethods.IsProcessElevated); + } + + private void AnonymousEditDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Impersonation"); + } + } +} diff --git a/JexusManager.Features.Authentication/ImpersonationEditDialog.resx b/JexusManager.Features.Authentication/ImpersonationEditDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authentication/ImpersonationEditDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/ImpersonationFeature.cs b/JexusManager.Features.Authentication/ImpersonationFeature.cs new file mode 100644 index 00000000..a1a3775d --- /dev/null +++ b/JexusManager.Features.Authentication/ImpersonationFeature.cs @@ -0,0 +1,145 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class ImpersonationFeature : AuthenticationFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly ImpersonationFeature _owner; + + public FeatureTaskList(ImpersonationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + } + + private FeatureTaskList _taskList; + + public ImpersonationFeature(Module module) : base(module) + { + } + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/identity"); + var enabled = (bool)section["impersonate"]; + SetEnabled(enabled); + } + + private void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/identity"); + section["impersonate"] = true; + service.ServerManager.CommitChanges(); + SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/identity"); + section["impersonate"] = false; + service.ServerManager.CommitChanges(); + SetEnabled(false); + } + + private void Edit() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.web/identity"); + var dialog = new ImpersonationEditDialog(this.Module, new ImpersonationItem(section)); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + OnAuthenticationSettingsSaved(); + } + + public override Version MinimumFrameworkVersion + { + get { return FxVersion20; } + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Impersonation"); + return true; + } + + public override bool IsFeatureEnabled + { + get { return true; } + } + + public override AuthenticationType AuthenticationType + { + get { return AuthenticationType.Other; } + } + + public override string Name + { + get { return "ASP.NET Impersonation"; } + } + } +} diff --git a/JexusManager.Features.Authentication/ImpersonationItem.cs b/JexusManager.Features.Authentication/ImpersonationItem.cs new file mode 100644 index 00000000..4a3dad2e --- /dev/null +++ b/JexusManager.Features.Authentication/ImpersonationItem.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using Microsoft.Web.Administration; + + public class ImpersonationItem + { + public ConfigurationElement Element { get; set; } + + public ImpersonationItem(ConfigurationElement element) + { + this.Element = element; + Name = (string)element["userName"]; + Password = (string)element["password"]; + } + + public string Name { get; set; } + public string Password { get; set; } + + public void Apply() + { + Element["userName"] = this.Name; + Element["password"] = this.Password; + } + } +} diff --git a/JexusManager.Features.Authentication/JexusManager.Features.Authentication.csproj b/JexusManager.Features.Authentication/JexusManager.Features.Authentication.csproj new file mode 100644 index 00000000..6bde76f9 --- /dev/null +++ b/JexusManager.Features.Authentication/JexusManager.Features.Authentication.csproj @@ -0,0 +1,204 @@ + + + + + Debug + AnyCPU + {AA7CCFC5-B695-4773-BA79-77CF6C36908F} + Library + Properties + JexusManager.Features.Authentication + JexusManager.Features.Authentication + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + form + + + AnonymousEditDialog.cs + + + + + + UserControl + + + AuthenticationPage.cs + + + + Form + + + BasicEditDialog.cs + + + + + form + + + CredentialsDialog.cs + + + + Form + + + DigestEditDialog.cs + + + + + Form + + + FormsEditDialog.cs + + + + form + + + ImpersonationEditDialog.cs + + + + + + True + True + Resources.resx + + + + Form + + + ProvidersDialog.cs + + + Form + + + WindowsAdvancedDialog.cs + + + + + + + AnonymousEditDialog.cs + + + AuthenticationPage.cs + + + BasicEditDialog.cs + + + CredentialsDialog.cs + + + DigestEditDialog.cs + + + FormsEditDialog.cs + + + ImpersonationEditDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + ProvidersDialog.cs + + + WindowsAdvancedDialog.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/JexusManager.snk b/JexusManager.Features.Authentication/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Authentication/JexusManager.snk differ diff --git a/JexusManager.Features.Authentication/Properties/AssemblyInfo.cs b/JexusManager.Features.Authentication/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..89c86fd1 --- /dev/null +++ b/JexusManager.Features.Authentication/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Authentication")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Authentication")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("aa7ccfc5-b695-4773-ba79-77cf6c36908f")] + +// 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")] diff --git a/JexusManager.Features.Authentication/Properties/Resources.Designer.cs b/JexusManager.Features.Authentication/Properties/Resources.Designer.cs new file mode 100644 index 00000000..74533dfa --- /dev/null +++ b/JexusManager.Features.Authentication/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Authentication.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Authentication.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap authentication_36 { + get { + object obj = ResourceManager.GetObject("authentication_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Authentication/Properties/Resources.resx b/JexusManager.Features.Authentication/Properties/Resources.resx new file mode 100644 index 00000000..d37ef7c2 --- /dev/null +++ b/JexusManager.Features.Authentication/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\authentication_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/ProviderItem.cs b/JexusManager.Features.Authentication/ProviderItem.cs new file mode 100644 index 00000000..3c9211b0 --- /dev/null +++ b/JexusManager.Features.Authentication/ProviderItem.cs @@ -0,0 +1,31 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using Microsoft.Web.Administration; + + public class ProviderItem + { + public ConfigurationElement Element { get; set; } + + public ProviderItem(ConfigurationElement element) + { + this.Element = element; + if (element == null) + { + return; + } + + this.Value = (string)element["value"]; + } + + public string Value { get; set; } + + public void Apply() + { + Element["value"] = Value; + } + } +} diff --git a/JexusManager.Features.Authentication/ProvidersDialog.Designer.cs b/JexusManager.Features.Authentication/ProvidersDialog.Designer.cs new file mode 100644 index 00000000..8d78cffc --- /dev/null +++ b/JexusManager.Features.Authentication/ProvidersDialog.Designer.cs @@ -0,0 +1,186 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class ProvidersDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.lbProviders = new System.Windows.Forms.ListBox(); + this.btnUp = new System.Windows.Forms.Button(); + this.btnDown = new System.Windows.Forms.Button(); + this.btnRemove = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.cbAvailable = new System.Windows.Forms.ComboBox(); + this.btnAdd = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(14, 18); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(96, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Enabled Providers:"; + // + // lbProviders + // + this.lbProviders.FormattingEnabled = true; + this.lbProviders.Location = new System.Drawing.Point(17, 34); + this.lbProviders.Name = "lbProviders"; + this.lbProviders.Size = new System.Drawing.Size(290, 108); + this.lbProviders.TabIndex = 1; + // + // btnUp + // + this.btnUp.Location = new System.Drawing.Point(316, 34); + this.btnUp.Name = "btnUp"; + this.btnUp.Size = new System.Drawing.Size(85, 23); + this.btnUp.TabIndex = 2; + this.btnUp.Text = "Move Up"; + this.btnUp.UseVisualStyleBackColor = true; + // + // btnDown + // + this.btnDown.Location = new System.Drawing.Point(316, 64); + this.btnDown.Name = "btnDown"; + this.btnDown.Size = new System.Drawing.Size(85, 23); + this.btnDown.TabIndex = 3; + this.btnDown.Text = "Move Down"; + this.btnDown.UseVisualStyleBackColor = true; + // + // btnRemove + // + this.btnRemove.Location = new System.Drawing.Point(316, 94); + this.btnRemove.Name = "btnRemove"; + this.btnRemove.Size = new System.Drawing.Size(85, 23); + this.btnRemove.TabIndex = 4; + this.btnRemove.Text = "Remove"; + this.btnRemove.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(14, 154); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(314, 26); + this.label2.TabIndex = 5; + this.label2.Text = "Select a provider from the list of available providers and click Add\r\nto add it t" + + "o the enabled providers."; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(14, 198); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(100, 13); + this.label3.TabIndex = 6; + this.label3.Text = "Available Providers:"; + // + // cbAvailable + // + this.cbAvailable.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbAvailable.FormattingEnabled = true; + this.cbAvailable.Location = new System.Drawing.Point(17, 214); + this.cbAvailable.Name = "cbAvailable"; + this.cbAvailable.Size = new System.Drawing.Size(290, 21); + this.cbAvailable.TabIndex = 7; + // + // btnAdd + // + this.btnAdd.Location = new System.Drawing.Point(316, 212); + this.btnAdd.Name = "btnAdd"; + this.btnAdd.Size = new System.Drawing.Size(85, 23); + this.btnAdd.TabIndex = 8; + this.btnAdd.Text = "Add"; + this.btnAdd.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(312, 281); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 9; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(216, 281); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 10; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // ProvidersDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(419, 316); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnAdd); + this.Controls.Add(this.cbAvailable); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.btnRemove); + this.Controls.Add(this.btnDown); + this.Controls.Add(this.btnUp); + this.Controls.Add(this.lbProviders); + this.Controls.Add(this.label1); + this.Name = "ProvidersDialog"; + this.Text = "Providers"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private ListBox lbProviders; + private Button btnUp; + private Button btnDown; + private Button btnRemove; + private Label label2; + private Label label3; + private ComboBox cbAvailable; + private Button btnAdd; + private Button btnCancel; + private Button btnOK; + } +} diff --git a/JexusManager.Features.Authentication/ProvidersDialog.cs b/JexusManager.Features.Authentication/ProvidersDialog.cs new file mode 100644 index 00000000..881172fa --- /dev/null +++ b/JexusManager.Features.Authentication/ProvidersDialog.cs @@ -0,0 +1,123 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using System.Reactive.Disposables; + public partial class ProvidersDialog : DialogForm + { + private readonly string[] _allProviders = { "Negotiate", "NTLM", "Negotiate:Kerberos" }; + + public ProvidersDialog(IServiceProvider serviceProvider, WindowsItem item) + : base(serviceProvider) + { + InitializeComponent(); + foreach (var provider in item.Providers) + { + lbProviders.Items.Add(provider.Value); + } + + foreach (var each in _allProviders) + { + if (!lbProviders.Items.Contains(each)) + { + cbAvailable.Items.Add(each); + } + } + + btnOK.Enabled = false; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnRemove, "Click") + .Subscribe(evt => + { + cbAvailable.Items.Add(lbProviders.SelectedItem); + lbProviders.Items.Remove(lbProviders.SelectedItem); + cbAvailable.Enabled = true; + btnOK.Enabled = true; + })); + + var add = Observable.FromEventPattern(btnAdd, "Click"); + var load = Observable.FromEventPattern(this, "Load"); + container.Add( + add.Subscribe(evt => + { + var selected = cbAvailable.Text; + lbProviders.Items.Add(selected); + cbAvailable.Items.RemoveAt(cbAvailable.SelectedIndex); + cbAvailable.SelectedIndex = -1; + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(cbAvailable, "SelectedIndexChanged") + .Merge(load) + .Merge(add) + .Subscribe(evt => + { + btnAdd.Enabled = cbAvailable.SelectedIndex > -1; + if (cbAvailable.Items.Count == 0) + { + cbAvailable.Enabled = false; + } + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + item.Providers.Clear(); + foreach (string provider in lbProviders.Items) + { + item.Providers.Add(new ProviderItem(null) { Value = provider }); + } + + item.Apply(); + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(lbProviders, "SelectedIndexChanged") + .Merge(load) + .Subscribe(evt => + { + btnRemove.Enabled = lbProviders.SelectedIndex > -1; + btnUp.Enabled = lbProviders.SelectedIndex > 0; + btnDown.Enabled = lbProviders.SelectedIndex > -1 && lbProviders.SelectedIndex < lbProviders.Items.Count - 1; + })); + + container.Add( + Observable.FromEventPattern(btnUp, "Click") + .Subscribe(evt => + { + var current = lbProviders.SelectedItem; + var up = lbProviders.SelectedIndex - 1; + lbProviders.Items.RemoveAt(lbProviders.SelectedIndex); + lbProviders.Items.Insert(up, current); + lbProviders.SelectedIndex = up; + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(btnDown, "Click") + .Subscribe(evt => + { + var current = lbProviders.SelectedItem; + var down = lbProviders.SelectedIndex + 1; + lbProviders.Items.RemoveAt(lbProviders.SelectedIndex); + lbProviders.Items.Insert(down, current); + lbProviders.SelectedIndex = down; + btnOK.Enabled = true; + })); + } + } +} diff --git a/JexusManager.Features.Authentication/ProvidersDialog.resx b/JexusManager.Features.Authentication/ProvidersDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authentication/ProvidersDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/Resources/authentication_36.png b/JexusManager.Features.Authentication/Resources/authentication_36.png new file mode 100644 index 00000000..93d30564 Binary files /dev/null and b/JexusManager.Features.Authentication/Resources/authentication_36.png differ diff --git a/JexusManager.Features.Authentication/WindowsAdvancedDialog.Designer.cs b/JexusManager.Features.Authentication/WindowsAdvancedDialog.Designer.cs new file mode 100644 index 00000000..b616a482 --- /dev/null +++ b/JexusManager.Features.Authentication/WindowsAdvancedDialog.Designer.cs @@ -0,0 +1,137 @@ +namespace JexusManager.Features.Authentication +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class WindowsAdvancedDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WindowsAdvancedDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.cbExtended = new System.Windows.Forms.ComboBox(); + this.cbKernelMode = new System.Windows.Forms.CheckBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(106, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Extended Protection:"; + // + // cbExtended + // + this.cbExtended.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbExtended.FormattingEnabled = true; + this.cbExtended.Items.AddRange(new object[] { + "Off", + "Accept", + "Required"}); + this.cbExtended.Location = new System.Drawing.Point(16, 39); + this.cbExtended.Name = "cbExtended"; + this.cbExtended.Size = new System.Drawing.Size(121, 21); + this.cbExtended.TabIndex = 1; + // + // cbKernelMode + // + this.cbKernelMode.AutoSize = true; + this.cbKernelMode.Checked = true; + this.cbKernelMode.CheckState = System.Windows.Forms.CheckState.Checked; + this.cbKernelMode.Location = new System.Drawing.Point(16, 89); + this.cbKernelMode.Name = "cbKernelMode"; + this.cbKernelMode.Size = new System.Drawing.Size(191, 17); + this.cbKernelMode.TabIndex = 2; + this.cbKernelMode.Text = "Enable Kernel-mode authentication"; + this.cbKernelMode.UseVisualStyleBackColor = true; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(16, 112); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.textBox1.Size = new System.Drawing.Size(461, 158); + this.textBox1.TabIndex = 3; + this.textBox1.Text = resources.GetString("textBox1.Text"); + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(286, 301); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 4; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(382, 301); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 5; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // WindowsAdvancedDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(489, 336); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.cbKernelMode); + this.Controls.Add(this.cbExtended); + this.Controls.Add(this.label1); + this.Name = "WindowsAdvancedDialog"; + this.Text = "Advanced Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.WindowsAdvancedDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private ComboBox cbExtended; + private CheckBox cbKernelMode; + private TextBox textBox1; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.Authentication/WindowsAdvancedDialog.cs b/JexusManager.Features.Authentication/WindowsAdvancedDialog.cs new file mode 100644 index 00000000..32385002 --- /dev/null +++ b/JexusManager.Features.Authentication/WindowsAdvancedDialog.cs @@ -0,0 +1,58 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using Microsoft.Web.Management.Client.Win32; + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + public partial class WindowsAdvancedDialog : DialogForm + { + public WindowsAdvancedDialog(IServiceProvider serviceProvider, WindowsItem item) + : base(serviceProvider) + { + InitializeComponent(); + btnOK.Enabled = false; + cbExtended.SelectedIndex = item.TokenChecking; + cbKernelMode.Checked = item.UseKernelMode; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(cbExtended, "SelectedIndexChanged") + .Subscribe(evt => + { + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(cbKernelMode, "CheckedChanged") + .Subscribe(evt => + { + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + item.TokenChecking = cbExtended.SelectedIndex; + item.UseKernelMode = cbKernelMode.Checked; + item.Apply(); + DialogResult = DialogResult.OK; + })); + } + + private void WindowsAdvancedDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Advanced_Windows"); + } + } +} diff --git a/JexusManager.Features.Authentication/WindowsAdvancedDialog.resx b/JexusManager.Features.Authentication/WindowsAdvancedDialog.resx new file mode 100644 index 00000000..cab860cb --- /dev/null +++ b/JexusManager.Features.Authentication/WindowsAdvancedDialog.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + By default, IIS enables kernel-mode authentication, which may improve authentication performance and prevent authentication problems with application pools configured to use a custom identity. As a best practice, do not disable this setting if Kerberos authentication is used in your environment and the application pool is configured to use a custom identity. + + \ No newline at end of file diff --git a/JexusManager.Features.Authentication/WindowsAuthenticationFeature.cs b/JexusManager.Features.Authentication/WindowsAuthenticationFeature.cs new file mode 100644 index 00000000..4757ff34 --- /dev/null +++ b/JexusManager.Features.Authentication/WindowsAuthenticationFeature.cs @@ -0,0 +1,167 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Extensions; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class WindowsAuthenticationFeature : AuthenticationFeature + { + private FeatureTaskList _taskList; + + private sealed class FeatureTaskList : TaskList + { + private readonly WindowsAuthenticationFeature _owner; + + public FeatureTaskList(WindowsAuthenticationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Advanced Settings...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Providers", "Providers...", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Providers() + { + _owner.Providers(); + } + } + + public WindowsAuthenticationFeature(Module module) : base(module) + { + } + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var windowsSection = service.GetSection("system.webServer/security/authentication/windowsAuthentication"); + var windowsEnabled = (bool)windowsSection["enabled"]; + SetEnabled(windowsEnabled); + } + + private void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var windowsSection = service.GetSection("system.webServer/security/authentication/windowsAuthentication"); + windowsSection["enabled"] = true; + service.ServerManager.CommitChanges(); + SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var windowsSection = service.GetSection("system.webServer/security/authentication/windowsAuthentication"); + windowsSection["enabled"] = false; + service.ServerManager.CommitChanges(); + SetEnabled(false); + } + + private void Edit() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var windowsSection = service.GetSection("system.webServer/security/authentication/windowsAuthentication"); + var dialog = new WindowsAdvancedDialog(Module, new WindowsItem(windowsSection)); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + OnAuthenticationSettingsSaved(); + } + + private void Providers() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var windowsSection = service.GetSection("system.webServer/security/authentication/windowsAuthentication"); + var dialog = new ProvidersDialog(Module, new WindowsItem(windowsSection)); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + OnAuthenticationSettingsSaved(); + } + + public override Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210461#Windows"); + return true; + } + + public override bool IsFeatureEnabled + { + get { return true; } + } + + public override AuthenticationType AuthenticationType + { + get { return AuthenticationType.ChallengeBase; } + } + + public override string Name + { + get { return "Windows Authentication"; } + } + } +} diff --git a/JexusManager.Features.Authentication/WindowsItem.cs b/JexusManager.Features.Authentication/WindowsItem.cs new file mode 100644 index 00000000..6fb2a624 --- /dev/null +++ b/JexusManager.Features.Authentication/WindowsItem.cs @@ -0,0 +1,56 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authentication +{ + using System; + using System.Collections.Generic; + + using Microsoft.Web.Administration; + + public class WindowsItem + { + public ConfigurationElement Element { get; set; } + + public WindowsItem(ConfigurationElement element) + { + this.Element = element; + var extended = element.ChildElements["extendedProtection"]; + this.TokenChecking = Convert.ToInt32((long)extended["tokenChecking"]); + this.UseKernelMode = (bool)element["useKernelMode"]; + + var providers = element.GetCollection("providers"); + Providers = new List(providers.Count); + foreach (ConfigurationElement provider in providers) + { + Providers.Add(new ProviderItem(provider)); + } + } + + public List Providers { get; set; } + + public int TokenChecking { get; set; } + public bool UseKernelMode { get; set; } + + public void Apply() + { + this.Element["useKernelMode"] = this.UseKernelMode; + var extended = this.Element.ChildElements["extendedProtection"]; + extended["tokenChecking"] = (long)this.TokenChecking; + + var providers = Element.GetCollection("providers"); + providers.Clear(); + foreach (var item in Providers) + { + if (item.Element == null) + { + item.Element = providers.CreateElement(); + item.Apply(); + } + + providers.Add(item.Element); + } + } + } +} diff --git a/JexusManager.Features.Authentication/packages.config b/JexusManager.Features.Authentication/packages.config new file mode 100644 index 00000000..bdc4e5c2 --- /dev/null +++ b/JexusManager.Features.Authentication/packages.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/JexusManager.Features.Authorization/AuthorizationFeature.cs b/JexusManager.Features.Authorization/AuthorizationFeature.cs new file mode 100644 index 00000000..1b42df3c --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationFeature.cs @@ -0,0 +1,183 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.Authorization +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Authorization feature. + /// + internal class AuthorizationFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly AuthorizationFeature _owner; + + public FeatureTaskList(AuthorizationFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("AddAllow", "Add Allow Rule...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("AddDeny", "Add Deny Rule...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Edit...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddAllow() + { + _owner.AddAllow(); + } + + [Obfuscation(Exclude = true)] + public void AddDeny() + { + _owner.AddDeny(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Edit(); + } + } + + public AuthorizationFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + var section = service.GetSection("system.webServer/security/authorization"); + return section.GetCollection(); + } + + public void Load() + { + LoadItems(); + } + + public void AddAllow() + { + var dialog = new NewRuleDialog(this.Module, null, true, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddDeny() + { + var dialog = new NewRuleDialog(this.Module, null, false, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected authorization rule?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + this.RemoveItem(); + } + + public void Edit() + { + var dialog = new NewRuleDialog(this.Module, this.SelectedItem, false, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + protected override void OnSettingsSaved() + { + this.AuthorizationSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210462"); + return false; + } + + public AuthorizationSettingsSavedEventHandler AuthorizationSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "Authorization Rules"; } + } + } +} diff --git a/JexusManager.Features.Authorization/AuthorizationModule.cs b/JexusManager.Features.Authorization/AuthorizationModule.cs new file mode 100644 index 00000000..5ccd65dc --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationModule.cs @@ -0,0 +1,24 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authorization +{ + using System; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class AuthorizationModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(AuthorizationPage), "Authorization Rules", + "Configure rules for authorizing users to access websites and applications", Resources.authorization_36, + Resources.authorization_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Authorization/AuthorizationModuleProvider.cs b/JexusManager.Features.Authorization/AuthorizationModuleProvider.cs new file mode 100644 index 00000000..558e8650 --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authorization +{ + using System; + + using Microsoft.Web.Management.Server; + + public class AuthorizationModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(AuthorizationModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Authorization/AuthorizationPage.Designer.cs b/JexusManager.Features.Authorization/AuthorizationPage.Designer.cs new file mode 100644 index 00000000..80e50877 --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationPage.Designer.cs @@ -0,0 +1,266 @@ +namespace JexusManager.Features.Authorization +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AuthorizationPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chMode = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chUsers = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chRoles = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chVerbs = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chMode, + this.chUsers, + this.chRoles, + this.chVerbs, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 13); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 527); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // chMode + // + this.chMode.Text = "Mode"; + this.chMode.Width = 80; + // + // chUsers + // + this.chUsers.Text = "Users"; + this.chUsers.Width = 120; + // + // chRoles + // + this.chRoles.Text = "Roles"; + this.chRoles.Width = 120; + // + // chVerbs + // + this.chVerbs.Text = "Verbs"; + this.chVerbs.Width = 120; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(427, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to specify rules for authorizing users to access websites and ap" + + "plications."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(199, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Authorization Rules"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // AuthorizationPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "AuthorizationPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chMode; + private ColumnHeader chUsers; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chRoles; + private ColumnHeader chVerbs; + } +} diff --git a/JexusManager.Features.Authorization/AuthorizationPage.cs b/JexusManager.Features.Authorization/AuthorizationPage.cs new file mode 100644 index 00000000..3c31ba49 --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationPage.cs @@ -0,0 +1,140 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authorization +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class AuthorizationPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly AuthorizationPage _owner; + + public PageTaskList(AuthorizationPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class AuthorizationListViewItem : ListViewItem + { + public AuthorizationRule Item { get; } + + private readonly AuthorizationPage _page; + + public AuthorizationListViewItem(AuthorizationRule item, AuthorizationPage page) + : base(item.AccessType == 0L ? "Allow" : "Deny") + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.UsersString)); + this.SubItems.Add(new ListViewSubItem(this, item.Roles)); + this.SubItems.Add(new ListViewSubItem(this, item.Verbs)); + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private AuthorizationFeature _feature; + private PageTaskList _taskList; + + public AuthorizationPage() + { + this.InitializeComponent(); + } + + public override ModuleListPageViewModes ViewModes + { + get + { + return ModuleListPageViewModes.Details; + } + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new AuthorizationFeature(Module); + _feature.AuthorizationSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new AuthorizationListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (AuthorizationListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((AuthorizationListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.Authorization/AuthorizationPage.resx b/JexusManager.Features.Authorization/AuthorizationPage.resx new file mode 100644 index 00000000..80c56e6e --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationPage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Authorization/AuthorizationRule.cs b/JexusManager.Features.Authorization/AuthorizationRule.cs new file mode 100644 index 00000000..5931960b --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationRule.cs @@ -0,0 +1,67 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authorization +{ + using Microsoft.Web.Administration; + + internal class AuthorizationRule : IItem + { + public AuthorizationRule(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inhertied"; + if (element == null) + { + Users = Roles = Verbs = string.Empty; + return; + } + + this.AccessType = (long)element["accessType"]; + this.Users = (string)element["users"]; + this.Roles = (string)element["roles"]; + this.Verbs = (string)element["verbs"]; + } + + public string Verbs { get; set; } + + public string Roles { get; set; } + + public string Users { get; set; } + + public long AccessType { get; set; } + + public ConfigurationElement Element { get; set; } + + public string Flag { get; set; } + + public string UsersString + { + get + { + return Users == "*" ? "All Users" : Users == "?" ? "Anonymous Users" : Users; + } + } + + public bool Equals(AuthorizationRule other) + { + // all properties + return Match(other) && other.AccessType == AccessType; + } + + public void Apply() + { + Element["accessType"] = AccessType; + Element["users"] = Users; + Element["roles"] = Roles; + Element["verbs"] = Verbs; + } + + public bool Match(AuthorizationRule other) + { + // match combined keys. + return other != null && other.Verbs == this.Verbs && other.Roles == this.Roles && other.Users == this.Users; + } + } +} diff --git a/JexusManager.Features.Authorization/AuthorizationSettingsSavedEventHandler.cs b/JexusManager.Features.Authorization/AuthorizationSettingsSavedEventHandler.cs new file mode 100644 index 00000000..e042e1d0 --- /dev/null +++ b/JexusManager.Features.Authorization/AuthorizationSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authorization +{ + public delegate void AuthorizationSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Authorization/JexusManager.Features.Authorization.csproj b/JexusManager.Features.Authorization/JexusManager.Features.Authorization.csproj new file mode 100644 index 00000000..b115621f --- /dev/null +++ b/JexusManager.Features.Authorization/JexusManager.Features.Authorization.csproj @@ -0,0 +1,130 @@ + + + + + Debug + AnyCPU + {FEFD62BE-AA83-42B9-A79D-2DF0150F4F81} + Library + Properties + JexusManager.Features.Authorization + JexusManager.Features.Authorization + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + UserControl + + + AuthorizationPage.cs + + + + + Form + + + NewRuleDialog.cs + + + + True + True + Resources.resx + + + + + AuthorizationPage.cs + + + NewRuleDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Authorization/JexusManager.snk b/JexusManager.Features.Authorization/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Authorization/JexusManager.snk differ diff --git a/JexusManager.Features.Authorization/NewRuleDialog.Designer.cs b/JexusManager.Features.Authorization/NewRuleDialog.Designer.cs new file mode 100644 index 00000000..8e0efb85 --- /dev/null +++ b/JexusManager.Features.Authorization/NewRuleDialog.Designer.cs @@ -0,0 +1,229 @@ +namespace JexusManager.Features.Authorization +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewRuleDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.rbAll = new System.Windows.Forms.RadioButton(); + this.rbRoles = new System.Windows.Forms.RadioButton(); + this.txtRoles = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtUsers = new System.Windows.Forms.TextBox(); + this.rbAnonymous = new System.Windows.Forms.RadioButton(); + this.rbUsers = new System.Windows.Forms.RadioButton(); + this.label1 = new System.Windows.Forms.Label(); + this.cbVerbs = new System.Windows.Forms.CheckBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtVerbs = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(342, 386); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(241, 386); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 9); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(180, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Allow access to this Web content to:"; + // + // rbAll + // + this.rbAll.AutoSize = true; + this.rbAll.Checked = true; + this.rbAll.Location = new System.Drawing.Point(15, 34); + this.rbAll.Name = "rbAll"; + this.rbAll.Size = new System.Drawing.Size(64, 17); + this.rbAll.TabIndex = 3; + this.rbAll.TabStop = true; + this.rbAll.Text = "All users"; + this.rbAll.UseVisualStyleBackColor = true; + // + // rbRoles + // + this.rbRoles.AutoSize = true; + this.rbRoles.Location = new System.Drawing.Point(15, 102); + this.rbRoles.Name = "rbRoles"; + this.rbRoles.Size = new System.Drawing.Size(167, 17); + this.rbRoles.TabIndex = 4; + this.rbRoles.Text = "Specified roles or user groups:"; + this.rbRoles.UseVisualStyleBackColor = true; + // + // txtRoles + // + this.txtRoles.Enabled = false; + this.txtRoles.Location = new System.Drawing.Point(33, 125); + this.txtRoles.Name = "txtRoles"; + this.txtRoles.Size = new System.Drawing.Size(404, 20); + this.txtRoles.TabIndex = 6; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(30, 148); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(118, 13); + this.label2.TabIndex = 7; + this.label2.Text = "Example: Administrators"; + // + // txtUsers + // + this.txtUsers.Enabled = false; + this.txtUsers.Location = new System.Drawing.Point(33, 196); + this.txtUsers.Name = "txtUsers"; + this.txtUsers.Size = new System.Drawing.Size(404, 20); + this.txtUsers.TabIndex = 8; + // + // rbAnonymous + // + this.rbAnonymous.AutoSize = true; + this.rbAnonymous.Location = new System.Drawing.Point(15, 69); + this.rbAnonymous.Name = "rbAnonymous"; + this.rbAnonymous.Size = new System.Drawing.Size(121, 17); + this.rbAnonymous.TabIndex = 9; + this.rbAnonymous.TabStop = true; + this.rbAnonymous.Text = "All anonymous users"; + this.rbAnonymous.UseVisualStyleBackColor = true; + // + // rbUsers + // + this.rbUsers.AutoSize = true; + this.rbUsers.Location = new System.Drawing.Point(15, 173); + this.rbUsers.Name = "rbUsers"; + this.rbUsers.Size = new System.Drawing.Size(100, 17); + this.rbUsers.TabIndex = 10; + this.rbUsers.TabStop = true; + this.rbUsers.Text = "Specified users:"; + this.rbUsers.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(30, 219); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(115, 13); + this.label1.TabIndex = 11; + this.label1.Text = "Example: User1, User2"; + // + // cbVerbs + // + this.cbVerbs.AutoSize = true; + this.cbVerbs.Location = new System.Drawing.Point(15, 253); + this.cbVerbs.Name = "cbVerbs"; + this.cbVerbs.Size = new System.Drawing.Size(174, 17); + this.cbVerbs.TabIndex = 12; + this.cbVerbs.Text = "Apply this rule to specific verbs:"; + this.cbVerbs.UseVisualStyleBackColor = true; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(30, 299); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(110, 13); + this.label3.TabIndex = 13; + this.label3.Text = "Example: GET, POST"; + // + // txtVerbs + // + this.txtVerbs.Enabled = false; + this.txtVerbs.Location = new System.Drawing.Point(33, 276); + this.txtVerbs.Name = "txtVerbs"; + this.txtVerbs.Size = new System.Drawing.Size(404, 20); + this.txtVerbs.TabIndex = 14; + // + // NewRuleDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(449, 421); + this.Controls.Add(this.txtVerbs); + this.Controls.Add(this.label3); + this.Controls.Add(this.cbVerbs); + this.Controls.Add(this.label1); + this.Controls.Add(this.rbUsers); + this.Controls.Add(this.rbAnonymous); + this.Controls.Add(this.txtUsers); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtRoles); + this.Controls.Add(this.rbRoles); + this.Controls.Add(this.rbAll); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewRuleDialog"; + this.Text = "Add Allow Restriction Rule"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private RadioButton rbAll; + private RadioButton rbRoles; + private TextBox txtRoles; + private Label label2; + private TextBox txtUsers; + private RadioButton rbAnonymous; + private RadioButton rbUsers; + private Label label1; + private CheckBox cbVerbs; + private Label label3; + private TextBox txtVerbs; + } +} diff --git a/JexusManager.Features.Authorization/NewRuleDialog.cs b/JexusManager.Features.Authorization/NewRuleDialog.cs new file mode 100644 index 00000000..f400ad2a --- /dev/null +++ b/JexusManager.Features.Authorization/NewRuleDialog.cs @@ -0,0 +1,134 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Authorization +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Linq; + using System.Reactive.Disposables; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewRuleDialog : DialogForm + { + private readonly bool _allowed; + private readonly AuthorizationFeature _feature; + + public NewRuleDialog(IServiceProvider serviceProvider, AuthorizationRule existing, bool allowed, AuthorizationFeature feature) + : base(serviceProvider) + { + this.InitializeComponent(); + _allowed = existing == null ? allowed : existing.AccessType == 0L; + Text = string.Format("{0} {1} Authorization Rule", existing == null ? "Add" : "Edit", _allowed ? "Allow" : "Deny"); + txtDescription.Text = _allowed + ? "Allow access to this Web content to:" + : "Deny access to this Web content to:"; + _feature = feature; + Item = existing ?? new AuthorizationRule(null); + if (existing != null) + { + txtRoles.Text = Item.Roles; + txtVerbs.Text = Item.Verbs; + if (Item.Users == "*") + { + rbAll.Checked = true; + } + else if (Item.Users == "?") + { + rbAnonymous.Checked = true; + } + else + { + rbUsers.Checked = true; + txtUsers.Text = Item.Users; + } + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.AccessType = _allowed ? 0L : 1L; + Item.Roles = string.Empty; + if (rbAll.Checked) + { + Item.Users = "*"; + } + else if (rbAnonymous.Checked) + { + Item.Users = "?"; + } + else if (rbUsers.Checked) + { + Item.Users = txtUsers.Text; + } + + if (rbRoles.Checked) + { + Item.Roles = txtRoles.Text; + Item.Users = string.Empty; + } + + if (cbVerbs.Checked) + { + Item.Verbs = txtVerbs.Text; + } + + if (_feature.Items.Any(item => item.Match(this.Item))) + { + ShowMessage( + "This authorization rule already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(rbAll, "CheckedChanged") + .Merge(Observable.FromEventPattern(rbRoles, "CheckedChanged")) + .Merge(Observable.FromEventPattern(rbAnonymous, "CheckedChanged")) + .Merge(Observable.FromEventPattern(rbUsers, "CheckedChanged")) + .Merge(Observable.FromEventPattern(cbVerbs, "CheckedChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + txtVerbs.Enabled = cbVerbs.Checked; + txtRoles.Enabled = rbRoles.Checked; + txtUsers.Enabled = rbUsers.Checked; + btnOK.Enabled = (rbRoles.Checked && !string.IsNullOrWhiteSpace(txtRoles.Text)) + || (rbUsers.Checked && !string.IsNullOrWhiteSpace(txtUsers.Text)) + || rbAll.Checked + || rbAnonymous.Checked; + })); + + container.Add( + Observable.FromEventPattern(txtRoles, "TextChanged") + .Merge(Observable.FromEventPattern(txtUsers, "TextChanged")) + .Merge(Observable.FromEventPattern(txtVerbs, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = true; + })); + } + + public AuthorizationRule Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210462"); + } + } +} diff --git a/JexusManager.Features.Authorization/NewRuleDialog.resx b/JexusManager.Features.Authorization/NewRuleDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Authorization/NewRuleDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Authorization/Properties/AssemblyInfo.cs b/JexusManager.Features.Authorization/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..535aefbc --- /dev/null +++ b/JexusManager.Features.Authorization/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Authorization")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Authorization")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("fefd62be-aa83-42b9-a79d-2df0150f4f81")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.Authorization/Properties/Resources.Designer.cs b/JexusManager.Features.Authorization/Properties/Resources.Designer.cs new file mode 100644 index 00000000..bbde6b1c --- /dev/null +++ b/JexusManager.Features.Authorization/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Authorization.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Authorization.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap authorization_36 { + get { + object obj = ResourceManager.GetObject("authorization_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Authorization/Properties/Resources.resx b/JexusManager.Features.Authorization/Properties/Resources.resx new file mode 100644 index 00000000..f80f7e7a --- /dev/null +++ b/JexusManager.Features.Authorization/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\authorization_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Authorization/Resources/authorization_36.png b/JexusManager.Features.Authorization/Resources/authorization_36.png new file mode 100644 index 00000000..b1025f09 Binary files /dev/null and b/JexusManager.Features.Authorization/Resources/authorization_36.png differ diff --git a/JexusManager.Features.Authorization/packages.config b/JexusManager.Features.Authorization/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.Authorization/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Caching/CachingAdvancedDialog.Designer.cs b/JexusManager.Features.Caching/CachingAdvancedDialog.Designer.cs new file mode 100644 index 00000000..4878f1c1 --- /dev/null +++ b/JexusManager.Features.Caching/CachingAdvancedDialog.Designer.cs @@ -0,0 +1,177 @@ +namespace JexusManager.Features.Caching +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class CachingAdvancedDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.txtHeaders = new System.Windows.Forms.TextBox(); + this.txtString = new System.Windows.Forms.TextBox(); + this.cbHeaders = new System.Windows.Forms.CheckBox(); + this.cbString = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(347, 256); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(251, 256); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.txtHeaders); + this.groupBox1.Controls.Add(this.txtString); + this.groupBox1.Controls.Add(this.cbHeaders); + this.groupBox1.Controls.Add(this.cbString); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(430, 238); + this.groupBox1.TabIndex = 2; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Multiple File Versions"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(37, 183); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(217, 13); + this.label3.TabIndex = 6; + this.label3.Text = "Example: Accept-Language, Accept-Charset"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(37, 102); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(124, 13); + this.label2.TabIndex = 5; + this.label2.Text = "Example: Locale, Culture"; + // + // txtHeaders + // + this.txtHeaders.Enabled = false; + this.txtHeaders.Location = new System.Drawing.Point(40, 160); + this.txtHeaders.Name = "txtHeaders"; + this.txtHeaders.Size = new System.Drawing.Size(321, 20); + this.txtHeaders.TabIndex = 4; + // + // txtString + // + this.txtString.Enabled = false; + this.txtString.Location = new System.Drawing.Point(40, 79); + this.txtString.Name = "txtString"; + this.txtString.Size = new System.Drawing.Size(321, 20); + this.txtString.TabIndex = 3; + // + // cbHeaders + // + this.cbHeaders.AutoSize = true; + this.cbHeaders.Location = new System.Drawing.Point(21, 137); + this.cbHeaders.Name = "cbHeaders"; + this.cbHeaders.Size = new System.Drawing.Size(69, 17); + this.cbHeaders.TabIndex = 2; + this.cbHeaders.Text = "Headers:"; + this.cbHeaders.UseVisualStyleBackColor = true; + // + // cbString + // + this.cbString.AutoSize = true; + this.cbString.Location = new System.Drawing.Point(21, 56); + this.cbString.Name = "cbString"; + this.cbString.Size = new System.Drawing.Size(136, 17); + this.cbString.TabIndex = 1; + this.cbString.Text = "Query string variable(s):"; + this.cbString.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(18, 31); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(208, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Cache different versions of a file based on:"; + // + // CachingAdvancedDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(454, 291); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "CachingAdvancedDialog"; + this.Text = "Advanced Output Cache Rule Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.CachingAdvancedDialogHelpButtonClicked); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private GroupBox groupBox1; + private Label label3; + private Label label2; + private TextBox txtHeaders; + private TextBox txtString; + private CheckBox cbHeaders; + private CheckBox cbString; + private Label label1; + } +} diff --git a/JexusManager.Features.Caching/CachingAdvancedDialog.cs b/JexusManager.Features.Caching/CachingAdvancedDialog.cs new file mode 100644 index 00000000..07736998 --- /dev/null +++ b/JexusManager.Features.Caching/CachingAdvancedDialog.cs @@ -0,0 +1,68 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class CachingAdvancedDialog : DialogForm + { + public CachingAdvancedDialog(IServiceProvider serviceProvider, CachingItem item) + : base(serviceProvider) + { + InitializeComponent(); + cbString.Checked = !string.IsNullOrWhiteSpace(item.VaryByQueryString); + txtString.Text = item.VaryByQueryString; + + cbHeaders.Checked = !string.IsNullOrWhiteSpace(item.VaryByHeaders); + txtHeaders.Text = item.VaryByHeaders; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + item.VaryByHeaders = txtHeaders.Text; + item.VaryByQueryString = txtString.Text; + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(cbString, "CheckedChanged") + .Subscribe(evt => + { + txtString.Enabled = cbString.Checked; + if (!cbString.Checked) + { + txtString.Text = string.Empty; + } + })); + + container.Add( + Observable.FromEventPattern(cbHeaders, "CheckedChanged") + .Subscribe(evt => + { + txtHeaders.Enabled = cbHeaders.Checked; + if (!cbHeaders.Checked) + { + txtHeaders.Text = string.Empty; + } + })); + } + + private void CachingAdvancedDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210522"); + } + } +} diff --git a/JexusManager.Features.Caching/CachingAdvancedDialog.resx b/JexusManager.Features.Caching/CachingAdvancedDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Caching/CachingAdvancedDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Caching/CachingFeature.cs b/JexusManager.Features.Caching/CachingFeature.cs new file mode 100644 index 00000000..0c766b43 --- /dev/null +++ b/JexusManager.Features.Caching/CachingFeature.cs @@ -0,0 +1,187 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.Caching +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class CachingFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly CachingFeature _owner; + + public FeatureTaskList(CachingFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Edit Feature Settings...", string.Empty).SetUsage()); + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + } + + public CachingFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + LoadItems(); + } + + public void Add() + { + var dialog = new NewCachingDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Set() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/caching"); + var dialog = new CachingSettingsDialog(this.Module, section); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + this.OnSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected restriction?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + var dialog = new NewCachingDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + var section = service.GetSection("system.webServer/caching"); + return section.GetCollection("profiles"); + } + + protected override void OnSettingsSaved() + { + this.CachingSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210522"); + return false; + } + + public CachingSettingsSavedEventHandler CachingSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "Output Caching"; } + } + } +} diff --git a/JexusManager.Features.Caching/CachingItem.cs b/JexusManager.Features.Caching/CachingItem.cs new file mode 100644 index 00000000..344d13f1 --- /dev/null +++ b/JexusManager.Features.Caching/CachingItem.cs @@ -0,0 +1,69 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + using System; + + using Microsoft.Web.Administration; + + internal class CachingItem : IItem + { + public CachingItem(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inhertied"; + if (element == null) + { + Extension = VaryByQueryString = VaryByHeaders = string.Empty; + return; + } + + this.Extension = (string)element["extension"]; + this.Policy = (long)element["policy"]; + this.KernelCachePolicy = (long)element["kernelCachePolicy"]; + this.Duration = (TimeSpan)element["duration"]; + this.VaryByQueryString = (string)element["varyByQueryString"]; + this.VaryByHeaders = (string)element["varyByHeaders"]; + } + + public string VaryByHeaders { get; set; } + + public string VaryByQueryString { get; set; } + + public TimeSpan Duration { get; set; } + + public long KernelCachePolicy { get; set; } + + public long Policy { get; set; } + + public string Extension { get; set; } + + public ConfigurationElement Element { get; set; } + + public string Flag { get; set; } + + public bool Equals(CachingItem other) + { + // all properties + return this.Match(other); + } + + public void Apply() + { + Element["extension"] = Extension; + Element["policy"] = Policy; + Element["kernelCachePolicy"] = KernelCachePolicy; + Element["duration"] = Duration; + Element["varyByQueryString"] = VaryByQueryString; + Element["varyByHeaders"] = VaryByHeaders; + } + + public bool Match(CachingItem other) + { + // match combined keys. + return other != null && other.Extension == this.Extension; + } + } +} diff --git a/JexusManager.Features.Caching/CachingModule.cs b/JexusManager.Features.Caching/CachingModule.cs new file mode 100644 index 00000000..2a81c33e --- /dev/null +++ b/JexusManager.Features.Caching/CachingModule.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + using System; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class CachingModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo( + this, + typeof(CachingPage), + "Output Caching", + "Specify rules for caching served content in the output cache", + Resources.caching_36, + Resources.caching_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Caching/CachingModuleProvider.cs b/JexusManager.Features.Caching/CachingModuleProvider.cs new file mode 100644 index 00000000..3099c263 --- /dev/null +++ b/JexusManager.Features.Caching/CachingModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + using System; + + using Microsoft.Web.Management.Server; + + public class CachingModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(CachingModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Caching/CachingPage.Designer.cs b/JexusManager.Features.Caching/CachingPage.Designer.cs new file mode 100644 index 00000000..b65dc050 --- /dev/null +++ b/JexusManager.Features.Caching/CachingPage.Designer.cs @@ -0,0 +1,298 @@ +namespace JexusManager.Features.Caching +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class CachingPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chExtension = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chUser = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chKernel = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chExtension, + this.chUser, + this.chKernel, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + // + // chExtension + // + this.chExtension.Text = "Extension"; + this.chExtension.Width = 120; + // + // chUser + // + this.chUser.Text = "User-Mode Policy"; + this.chUser.Width = 120; + // + // chKernel + // + this.chKernel.Text = "Kernel-Mode Policy"; + this.chKernel.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(567, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure output cache settings and to specify rules for cach" + + "ing served content in the output cache."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(161, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Output Caching"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // CachingPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "CachingPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chExtension; + private ColumnHeader chUser; + private ColumnHeader chKernel; + private ColumnHeader chType; + } +} diff --git a/JexusManager.Features.Caching/CachingPage.cs b/JexusManager.Features.Caching/CachingPage.cs new file mode 100644 index 00000000..b3dbe30f --- /dev/null +++ b/JexusManager.Features.Caching/CachingPage.cs @@ -0,0 +1,148 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class CachingPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly CachingPage _owner; + + public PageTaskList(CachingPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class CachingListViewItem : ListViewItem + { + public CachingItem Item { get; } + + private readonly CachingPage _page; + + public CachingListViewItem(CachingItem item, CachingPage page) + : base(item.Extension) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, ToString(item.Policy))); + this.SubItems.Add(new ListViewSubItem(this, ToString(item.KernelCachePolicy))); + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + + private static string ToString(long policy) + { + switch (policy) + { + case 0L: + return "Do not cache"; + case 1L: + return "Cache until change"; + case 2: + return "Cache for time period"; + case 3: + return "Prevent all caching"; + } + + return string.Empty; + } + } + + private CachingFeature _feature; + private PageTaskList _taskList; + + public CachingPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new CachingFeature(this.Module); + _feature.CachingSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new CachingListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (CachingListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((CachingListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.Caching/CachingPage.resx b/JexusManager.Features.Caching/CachingPage.resx new file mode 100644 index 00000000..7a2db0ac --- /dev/null +++ b/JexusManager.Features.Caching/CachingPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 497, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Caching/CachingSettingsDialog.Designer.cs b/JexusManager.Features.Caching/CachingSettingsDialog.Designer.cs new file mode 100644 index 00000000..76729b98 --- /dev/null +++ b/JexusManager.Features.Caching/CachingSettingsDialog.Designer.cs @@ -0,0 +1,152 @@ +namespace JexusManager.Features.Caching +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class CachingSettingsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cbUser = new System.Windows.Forms.CheckBox(); + this.cbKernel = new System.Windows.Forms.CheckBox(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtSize = new System.Windows.Forms.TextBox(); + this.cbLimit = new System.Windows.Forms.CheckBox(); + this.txtLimit = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // cbUser + // + this.cbUser.AutoSize = true; + this.cbUser.Location = new System.Drawing.Point(12, 12); + this.cbUser.Name = "cbUser"; + this.cbUser.Size = new System.Drawing.Size(92, 17); + this.cbUser.TabIndex = 1; + this.cbUser.Text = "Enable cache"; + this.cbUser.UseVisualStyleBackColor = true; + // + // cbKernel + // + this.cbKernel.AutoSize = true; + this.cbKernel.Location = new System.Drawing.Point(12, 49); + this.cbKernel.Name = "cbKernel"; + this.cbKernel.Size = new System.Drawing.Size(124, 17); + this.cbKernel.TabIndex = 2; + this.cbKernel.Text = "Enable kernel cache"; + this.cbKernel.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(227, 191); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(131, 191); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 83); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(205, 13); + this.label1.TabIndex = 6; + this.label1.Text = "Maximum cached response size (in bytes):"; + // + // txtSize + // + this.txtSize.Enabled = false; + this.txtSize.Location = new System.Drawing.Point(12, 99); + this.txtSize.Name = "txtSize"; + this.txtSize.Size = new System.Drawing.Size(249, 20); + this.txtSize.TabIndex = 7; + // + // cbLimit + // + this.cbLimit.AutoSize = true; + this.cbLimit.Enabled = false; + this.cbLimit.Location = new System.Drawing.Point(12, 125); + this.cbLimit.Name = "cbLimit"; + this.cbLimit.Size = new System.Drawing.Size(137, 17); + this.cbLimit.TabIndex = 8; + this.cbLimit.Text = "Cache size limit (in MB):"; + this.cbLimit.UseVisualStyleBackColor = true; + // + // txtLimit + // + this.txtLimit.Enabled = false; + this.txtLimit.Location = new System.Drawing.Point(30, 148); + this.txtLimit.Name = "txtLimit"; + this.txtLimit.Size = new System.Drawing.Size(245, 20); + this.txtLimit.TabIndex = 9; + // + // CachingSettingsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(334, 226); + this.Controls.Add(this.txtLimit); + this.Controls.Add(this.cbLimit); + this.Controls.Add(this.txtSize); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.cbKernel); + this.Controls.Add(this.cbUser); + this.Name = "CachingSettingsDialog"; + this.Text = "Edit Output Cache Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.PermissionsDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private CheckBox cbUser; + private CheckBox cbKernel; + private Button btnCancel; + private Button btnOK; + private Label label1; + private TextBox txtSize; + private CheckBox cbLimit; + private TextBox txtLimit; + } +} diff --git a/JexusManager.Features.Caching/CachingSettingsDialog.cs b/JexusManager.Features.Caching/CachingSettingsDialog.cs new file mode 100644 index 00000000..95c786b8 --- /dev/null +++ b/JexusManager.Features.Caching/CachingSettingsDialog.cs @@ -0,0 +1,61 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + internal partial class CachingSettingsDialog : DialogForm + { + public CachingSettingsDialog(IServiceProvider serviceProvider, ConfigurationElement element) + : base(serviceProvider) + { + InitializeComponent(); + cbUser.Checked = (bool)element["enabled"]; + cbKernel.Checked = (bool)element["enableKernelCache"]; + txtSize.Text = element["maxResponseSize"].ToString(); + var limit = (uint)element["maxCacheSize"]; + cbLimit.Checked = limit != 0; + if (cbLimit.Checked) + { + txtLimit.Text = limit.ToString(); + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + element["enableKernelCache"] = cbKernel.Checked; + element["enabled"] = cbUser.Checked; + element["maxResponseSize"] = uint.Parse(txtSize.Text); + if (!cbLimit.Checked) + { + element["maxCacheSize"] = 0U; + } + else + { + element["maxCacheSize"] = uint.Parse(txtLimit.Text); + } + + DialogResult = DialogResult.OK; + })); + } + + private void PermissionsDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210522"); + } + } +} diff --git a/JexusManager.Features.Caching/CachingSettingsDialog.resx b/JexusManager.Features.Caching/CachingSettingsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Caching/CachingSettingsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Caching/CachingSettingsSavedEventHandler.cs b/JexusManager.Features.Caching/CachingSettingsSavedEventHandler.cs new file mode 100644 index 00000000..05cf21cd --- /dev/null +++ b/JexusManager.Features.Caching/CachingSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + public delegate void CachingSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Caching/JexusManager.Features.Caching.csproj b/JexusManager.Features.Caching/JexusManager.Features.Caching.csproj new file mode 100644 index 00000000..51a23afc --- /dev/null +++ b/JexusManager.Features.Caching/JexusManager.Features.Caching.csproj @@ -0,0 +1,148 @@ + + + + + Debug + AnyCPU + {30A30352-CA83-446E-8AF8-598EA7397988} + Library + Properties + JexusManager.Features.Caching + JexusManager.Features.Caching + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + Form + + + CachingAdvancedDialog.cs + + + + + + + UserControl + + + CachingPage.cs + + + Form + + + CachingSettingsDialog.cs + + + + Form + + + NewCachingDialog.cs + + + + True + True + Resources.resx + + + + + CachingAdvancedDialog.cs + + + CachingPage.cs + + + CachingSettingsDialog.cs + + + NewCachingDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Caching/JexusManager.snk b/JexusManager.Features.Caching/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Caching/JexusManager.snk differ diff --git a/JexusManager.Features.Caching/NewCachingDialog.Designer.cs b/JexusManager.Features.Caching/NewCachingDialog.Designer.cs new file mode 100644 index 00000000..c82327c7 --- /dev/null +++ b/JexusManager.Features.Caching/NewCachingDialog.Designer.cs @@ -0,0 +1,286 @@ +namespace JexusManager.Features.Caching +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewCachingDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtExtension = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbUser = new System.Windows.Forms.CheckBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.btnAdvanced = new System.Windows.Forms.Button(); + this.txtUserTime = new System.Windows.Forms.TextBox(); + this.rbUserNo = new System.Windows.Forms.RadioButton(); + this.rbUserTime = new System.Windows.Forms.RadioButton(); + this.rbUserFile = new System.Windows.Forms.RadioButton(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.txtKernelTime = new System.Windows.Forms.TextBox(); + this.rbKernelNo = new System.Windows.Forms.RadioButton(); + this.rbKernelTime = new System.Windows.Forms.RadioButton(); + this.rbKernelFile = new System.Windows.Forms.RadioButton(); + this.cbKernel = new System.Windows.Forms.CheckBox(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(377, 506); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(276, 506); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(9, 48); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(113, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Example: .aspx or .axd"; + // + // txtExtension + // + this.txtExtension.Location = new System.Drawing.Point(12, 25); + this.txtExtension.Name = "txtExtension"; + this.txtExtension.Size = new System.Drawing.Size(338, 20); + this.txtExtension.TabIndex = 8; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(103, 13); + this.label3.TabIndex = 11; + this.label3.Text = "File name extension:"; + // + // cbUser + // + this.cbUser.AutoSize = true; + this.cbUser.Location = new System.Drawing.Point(12, 89); + this.cbUser.Name = "cbUser"; + this.cbUser.Size = new System.Drawing.Size(118, 17); + this.cbUser.TabIndex = 16; + this.cbUser.Text = "User-mode caching"; + this.cbUser.UseVisualStyleBackColor = true; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.btnAdvanced); + this.groupBox1.Controls.Add(this.txtUserTime); + this.groupBox1.Controls.Add(this.rbUserNo); + this.groupBox1.Controls.Add(this.rbUserTime); + this.groupBox1.Controls.Add(this.rbUserFile); + this.groupBox1.Location = new System.Drawing.Point(15, 112); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(438, 192); + this.groupBox1.TabIndex = 17; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "File Cache Monitoring"; + // + // btnAdvanced + // + this.btnAdvanced.Location = new System.Drawing.Point(280, 163); + this.btnAdvanced.Name = "btnAdvanced"; + this.btnAdvanced.Size = new System.Drawing.Size(152, 23); + this.btnAdvanced.TabIndex = 8; + this.btnAdvanced.Text = "Advanced..."; + this.btnAdvanced.UseVisualStyleBackColor = true; + // + // txtUserTime + // + this.txtUserTime.Location = new System.Drawing.Point(35, 76); + this.txtUserTime.Name = "txtUserTime"; + this.txtUserTime.Size = new System.Drawing.Size(190, 20); + this.txtUserTime.TabIndex = 7; + // + // rbUserNo + // + this.rbUserNo.AutoSize = true; + this.rbUserNo.Location = new System.Drawing.Point(15, 125); + this.rbUserNo.Name = "rbUserNo"; + this.rbUserNo.Size = new System.Drawing.Size(116, 17); + this.rbUserNo.TabIndex = 6; + this.rbUserNo.TabStop = true; + this.rbUserNo.Text = "Prevent all caching"; + this.rbUserNo.UseVisualStyleBackColor = true; + // + // rbUserTime + // + this.rbUserTime.AutoSize = true; + this.rbUserTime.Location = new System.Drawing.Point(15, 53); + this.rbUserTime.Name = "rbUserTime"; + this.rbUserTime.Size = new System.Drawing.Size(155, 17); + this.rbUserTime.TabIndex = 5; + this.rbUserTime.TabStop = true; + this.rbUserTime.Text = "At time intervals (hh:mm:ss):"; + this.rbUserTime.UseVisualStyleBackColor = true; + // + // rbUserFile + // + this.rbUserFile.AutoSize = true; + this.rbUserFile.Location = new System.Drawing.Point(15, 19); + this.rbUserFile.Name = "rbUserFile"; + this.rbUserFile.Size = new System.Drawing.Size(166, 17); + this.rbUserFile.TabIndex = 4; + this.rbUserFile.TabStop = true; + this.rbUserFile.Text = "Using file change notifications"; + this.rbUserFile.UseVisualStyleBackColor = true; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.txtKernelTime); + this.groupBox2.Controls.Add(this.rbKernelNo); + this.groupBox2.Controls.Add(this.rbKernelTime); + this.groupBox2.Controls.Add(this.rbKernelFile); + this.groupBox2.Location = new System.Drawing.Point(15, 333); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(438, 164); + this.groupBox2.TabIndex = 19; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "File Cache Monitoring"; + // + // txtKernelTime + // + this.txtKernelTime.Location = new System.Drawing.Point(35, 76); + this.txtKernelTime.Name = "txtKernelTime"; + this.txtKernelTime.Size = new System.Drawing.Size(190, 20); + this.txtKernelTime.TabIndex = 3; + // + // rbKernelNo + // + this.rbKernelNo.AutoSize = true; + this.rbKernelNo.Location = new System.Drawing.Point(15, 125); + this.rbKernelNo.Name = "rbKernelNo"; + this.rbKernelNo.Size = new System.Drawing.Size(116, 17); + this.rbKernelNo.TabIndex = 2; + this.rbKernelNo.TabStop = true; + this.rbKernelNo.Text = "Prevent all caching"; + this.rbKernelNo.UseVisualStyleBackColor = true; + // + // rbKernelTime + // + this.rbKernelTime.AutoSize = true; + this.rbKernelTime.Location = new System.Drawing.Point(15, 53); + this.rbKernelTime.Name = "rbKernelTime"; + this.rbKernelTime.Size = new System.Drawing.Size(152, 17); + this.rbKernelTime.TabIndex = 1; + this.rbKernelTime.TabStop = true; + this.rbKernelTime.Text = "At time intervals (hh:mm:ss)"; + this.rbKernelTime.UseVisualStyleBackColor = true; + // + // rbKernelFile + // + this.rbKernelFile.AutoSize = true; + this.rbKernelFile.Location = new System.Drawing.Point(15, 19); + this.rbKernelFile.Name = "rbKernelFile"; + this.rbKernelFile.Size = new System.Drawing.Size(166, 17); + this.rbKernelFile.TabIndex = 0; + this.rbKernelFile.TabStop = true; + this.rbKernelFile.Text = "Using file change notifications"; + this.rbKernelFile.UseVisualStyleBackColor = true; + // + // cbKernel + // + this.cbKernel.AutoSize = true; + this.cbKernel.Location = new System.Drawing.Point(12, 310); + this.cbKernel.Name = "cbKernel"; + this.cbKernel.Size = new System.Drawing.Size(126, 17); + this.cbKernel.TabIndex = 18; + this.cbKernel.Text = "Kernel-mode caching"; + this.cbKernel.UseVisualStyleBackColor = true; + // + // NewCachingDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(484, 541); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.cbKernel); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.cbUser); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtExtension); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewCachingDialog"; + this.Text = "Add Cache Rule"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtExtension; + private Label label3; + private CheckBox cbUser; + private GroupBox groupBox1; + private Button btnAdvanced; + private TextBox txtUserTime; + private RadioButton rbUserNo; + private RadioButton rbUserTime; + private RadioButton rbUserFile; + private GroupBox groupBox2; + private TextBox txtKernelTime; + private RadioButton rbKernelNo; + private RadioButton rbKernelTime; + private RadioButton rbKernelFile; + private CheckBox cbKernel; + } +} diff --git a/JexusManager.Features.Caching/NewCachingDialog.cs b/JexusManager.Features.Caching/NewCachingDialog.cs new file mode 100644 index 00000000..24e5c586 --- /dev/null +++ b/JexusManager.Features.Caching/NewCachingDialog.cs @@ -0,0 +1,125 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Caching +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewCachingDialog : DialogForm + { + private readonly CachingFeature _feature; + + public NewCachingDialog(IServiceProvider serviceProvider, CachingItem existing, CachingFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add Cache Rule" : "Edit Cache Rule"; + _feature = feature; + Item = existing ?? new CachingItem(null); + if (existing == null) + { + return; + } + + txtExtension.Text = Item.Extension; + cbUser.Checked = Item.Policy != 0L; + rbUserFile.Checked = Item.Policy == 1L; + rbUserTime.Checked = Item.Policy == 2L; + rbUserNo.Checked = Item.Policy == 3L; + + cbKernel.Checked = Item.KernelCachePolicy != 0L; + rbKernelFile.Checked = Item.KernelCachePolicy == 1L; + rbKernelTime.Checked = Item.KernelCachePolicy == 2L; + rbKernelNo.Checked = Item.KernelCachePolicy == 3L; + + txtKernelTime.Text = txtUserTime.Text = Item.Duration.ToString(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.Extension = txtExtension.Text; + Item.Duration = TimeSpan.Parse(txtKernelTime.Text); + if (!cbUser.Checked) + { + Item.Policy = 0L; + } + else if (rbUserFile.Checked) + { + Item.Policy = 1L; + } + else if (rbUserTime.Checked) + { + Item.Policy = 2L; + } + else if (rbUserNo.Checked) + { + Item.Policy = 3L; + } + + if (!cbKernel.Checked) + { + Item.KernelCachePolicy = 0L; + } + else if (rbKernelFile.Checked) + { + Item.KernelCachePolicy = 1L; + } + else if (rbKernelTime.Checked) + { + Item.KernelCachePolicy = 2L; + } + else if (rbKernelNo.Checked) + { + Item.KernelCachePolicy = 3L; + } + + if (_feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "This rule already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(btnAdvanced, "Click") + .Subscribe(evt => + { + var dialog = new CachingAdvancedDialog(ServiceProvider, Item); + dialog.ShowDialog(); + })); + + container.Add( + Observable.FromEventPattern(txtExtension, "TextChanged") + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtExtension.Text); + })); + } + + public CachingItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210522"); + } + } +} diff --git a/JexusManager.Features.Caching/NewCachingDialog.resx b/JexusManager.Features.Caching/NewCachingDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Caching/NewCachingDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Caching/Properties/AssemblyInfo.cs b/JexusManager.Features.Caching/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..17fd3077 --- /dev/null +++ b/JexusManager.Features.Caching/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Caching")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Caching")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("30a30352-ca83-446e-8af8-598ea7397988")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.Caching/Properties/Resources.Designer.cs b/JexusManager.Features.Caching/Properties/Resources.Designer.cs new file mode 100644 index 00000000..0bd691f4 --- /dev/null +++ b/JexusManager.Features.Caching/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Caching.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Caching.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap caching_36 { + get { + object obj = ResourceManager.GetObject("caching_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Caching/Properties/Resources.resx b/JexusManager.Features.Caching/Properties/Resources.resx new file mode 100644 index 00000000..036724fb --- /dev/null +++ b/JexusManager.Features.Caching/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\caching_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Caching/Resources/caching_36.png b/JexusManager.Features.Caching/Resources/caching_36.png new file mode 100644 index 00000000..7eb3cd37 Binary files /dev/null and b/JexusManager.Features.Caching/Resources/caching_36.png differ diff --git a/JexusManager.Features.Caching/packages.config b/JexusManager.Features.Caching/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.Caching/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/CertificatesFeature.cs b/JexusManager.Features.Certificates/CertificatesFeature.cs new file mode 100644 index 00000000..6430ee5e --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesFeature.cs @@ -0,0 +1,397 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.Certificates +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Reflection; + using System.Resources; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + using System.Windows.Forms; + + using JexusManager.Services; + using JexusManager.Features.Certificates.Wizards.CertificateRenewWizard; + using JexusManager.Features.Certificates.Wizards.CertificateRequestWizard; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class CertificatesFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly CertificatesFeature _owner; + + public FeatureTaskList(CertificatesFeature owner) + { + _owner = owner; + } + + private const string LocalhostIssuer = "CN=localhost"; + private readonly string _localMachineIssuer = string.Format("CN={0}", Environment.MachineName); + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Import", "Import...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("CreateRequest", "Create Certificate Request...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Complete", "Complete Certificate Request...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("CreateDomain", "Create Domain Certificate...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("CreateSelf", "Create Self-Signed Certificate...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("View", "View...", string.Empty).SetUsage()); + if (_owner.SelectedItem.Certificate.HasPrivateKey) + { + try + { + var keyInfo = (RSACryptoServiceProvider)_owner.SelectedItem.Certificate.PrivateKey; + if (keyInfo.CspKeyContainerInfo.Exportable) + { + result.Add(new MethodTaskItem("Export", "Export...", string.Empty).SetUsage()); + if (_owner.SelectedItem.Certificate.Issuer != LocalhostIssuer && _owner.SelectedItem.Certificate.Issuer != _localMachineIssuer) + { + result.Add(new MethodTaskItem("Renew", "Renew...", string.Empty).SetUsage()); + } + } + } + catch (CryptographicException ex) + { + if (ex.HResult != -2146893802) + { + throw; + } + } + } + + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + if (!_owner.AutomicRebindEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + } + + if (_owner.AutomicRebindEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Import() + { + _owner.Import(); + } + + [Obfuscation(Exclude = true)] + public void View() + { + _owner.View(); + } + + [Obfuscation(Exclude = true)] + public void Export() + { + _owner.Export(); + } + + [Obfuscation(Exclude = true)] + public void Renew() + { + _owner.Renew(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void CreateRequest() + { + _owner.CreateRequest(); + } + + [Obfuscation(Exclude = true)] + public void Complete() + { + _owner.Complete(); + } + + [Obfuscation(Exclude = true)] + public void CreateDomain() + { + _owner.CreateDomain(); + } + + [Obfuscation(Exclude = true)] + public void CreateSelf() + { + _owner.CreateSelf(); + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + } + + public CertificatesFeature(Module module) + { + Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (Module as IServiceProvider).GetService(type); + } + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + Items = new List(); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + if (service.ServerManager.Mode == WorkingMode.Jexus) + { + var certificate = AsyncHelper.RunSync(() => service.ServerManager.GetCertificateAsync()); + if (certificate != null) + { + Items.Add(new CertificatesItem(certificate, "Jexus", this)); + } + } + else + { + X509Store personal = new X509Store("MY", StoreLocation.LocalMachine); + personal.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + foreach (var certificate in personal.Certificates) + { + Items.Add(new CertificatesItem(certificate, "Personal", this)); + } + + personal.Close(); + + if (Environment.OSVersion.Version.Major >= 8) + { + // IMPORTANT: WebHosting store is available since Windows 8. + X509Store hosting = new X509Store("WebHosting", StoreLocation.LocalMachine); + hosting.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + foreach (var certificate in hosting.Certificates) + { + Items.Add(new CertificatesItem(certificate, "WebHosting", this)); + } + + hosting.Close(); + } + } + + OnCertificatesSettingsSaved(); + } + + public List Items { get; set; } + + public void Import() + { + var dialog = new ImportCertificateDialog(Module); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + OnCertificatesSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove this certificate, and permanently remove it from the certificate store?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != + DialogResult.Yes) + { + return; + } + + try + { + // remove certificate and mapping + using (var process = new Process()) + { + var start = process.StartInfo; + start.Verb = "runas"; + start.FileName = "cmd"; + start.Arguments = string.Format( + "/c \"\"{2}\" /h:\"{0}\" /s:{1}\"", + SelectedItem.Certificate.Thumbprint, + SelectedItem.Store == "Personal" ? "MY" : "WebHosting", + Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")); + start.CreateNoWindow = true; + start.WindowStyle = ProcessWindowStyle.Hidden; + process.Start(); + process.WaitForExit(); + + if (process.ExitCode == 0) + { + Items.Remove(SelectedItem); + SelectedItem = null; + OnCertificatesSettingsSaved(); + } + } + } + catch (Exception) + { + // elevation is cancelled. + } + } + + protected void OnCertificatesSettingsSaved() + { + CertificatesSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210528"); + return false; + } + + private void Disable() + { + } + + private void Enable() + { + } + + private void CreateSelf() + { + var dialog = new SelfCertificateDialog(Module); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + OnCertificatesSettingsSaved(); + } + + private void CreateDomain() + { + } + + private void Complete() + { + var dialog = new CompleteRequestDialog(Module); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + OnCertificatesSettingsSaved(); + } + + private void CreateRequest() + { + var wizard = new CertificateRequestWizard(Module); + wizard.ShowDialog(); + } + + private void Export() + { + var dialog = new ExportCertificateDialog(SelectedItem.Certificate, Module); + dialog.ShowDialog(); + } + + private void Renew() + { + var wizard = new CertificateRenewWizard(SelectedItem.Certificate, Module); + wizard.ShowDialog(); + } + + private void View() + { + var cert = SelectedItem.Certificate; + DialogHelper.DisplayCertificate(cert, IntPtr.Zero); + } + + public bool AutomicRebindEnabled { get; set; } + + public CertificatesItem SelectedItem { get; internal set; } + + public CertificatesSettingsSavedEventHandler CertificatesSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + + public string Name + { + get + { + return "Server Certificates"; + } + } + } +} diff --git a/JexusManager.Features.Certificates/CertificatesItem.cs b/JexusManager.Features.Certificates/CertificatesItem.cs new file mode 100644 index 00000000..9688bc10 --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesItem.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates +{ + using System; + using System.Security.Cryptography.X509Certificates; + + internal class CertificatesItem : IEquatable + { + public CertificatesItem(X509Certificate2 certificate, string store, CertificatesFeature feature) + { + Certificate = certificate; + Store = store; + Feature = feature; + } + + public X509Certificate2 Certificate { get; set; } + public string Store { get; set; } + public CertificatesFeature Feature { get; private set; } + + public bool Equals(CertificatesItem other) + { + return other != null && other.Certificate.GetCertHashString() == Certificate.GetCertHashString(); + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Certificates/CertificatesModule.cs b/JexusManager.Features.Certificates/CertificatesModule.cs new file mode 100644 index 00000000..e9b35e8a --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesModule.cs @@ -0,0 +1,25 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class CertificatesModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(CertificatesPage), "Server Certificates", + "Request and manage certificates for websites that use SSL", Resources.certificates_36, + Resources.certificates_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Certificates/CertificatesModuleProvider.cs b/JexusManager.Features.Certificates/CertificatesModuleProvider.cs new file mode 100644 index 00000000..1c687d49 --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates +{ + using System; + + using Microsoft.Web.Management.Server; + + public class CertificatesModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(CertificatesModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return scope == ManagementScope.Server; + } + } +} diff --git a/JexusManager.Features.Certificates/CertificatesPage.Designer.cs b/JexusManager.Features.Certificates/CertificatesPage.Designer.cs new file mode 100644 index 00000000..d1f44730 --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesPage.Designer.cs @@ -0,0 +1,369 @@ +namespace JexusManager.Features.Certificates +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class CertificatesPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chIssuedTo = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chIssuedBy = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chExpiration = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chHash = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chStore = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbFilter = new System.Windows.Forms.ToolStripComboBox(); + this.btnGo = new System.Windows.Forms.ToolStripSplitButton(); + this.protocolToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.btnShowAll = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); + this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitContainer1_SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "contextMenuStrip1"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 9; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chIssuedTo, + this.chIssuedBy, + this.chExpiration, + this.chHash, + this.chStore}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 180; + // + // chIssuedTo + // + this.chIssuedTo.Text = "Issued To"; + this.chIssuedTo.Width = 180; + // + // chIssuedBy + // + this.chIssuedBy.Text = "Issued By"; + this.chIssuedBy.Width = 180; + // + // chExpiration + // + this.chExpiration.Text = "Expiration Date"; + this.chExpiration.Width = 120; + // + // chHash + // + this.chHash.Text = "Certificate Hash"; + this.chHash.Width = 180; + // + // chStore + // + this.chStore.Text = "Certificate Store"; + this.chStore.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbFilter, + this.btnGo, + this.btnShowAll, + this.toolStripSeparator7, + this.toolStripLabel3, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(36, 22); + this.toolStripLabel2.Text = "Filter:"; + // + // cbFilter + // + this.cbFilter.Name = "cbFilter"; + this.cbFilter.Size = new System.Drawing.Size(121, 25); + // + // btnGo + // + this.btnGo.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.protocolToolStripMenuItem}); + this.btnGo.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnGo.Name = "btnGo"; + this.btnGo.Size = new System.Drawing.Size(54, 22); + this.btnGo.Text = "Go"; + this.btnGo.ToolTipText = "Go"; + // + // protocolToolStripMenuItem + // + this.protocolToolStripMenuItem.Name = "protocolToolStripMenuItem"; + this.protocolToolStripMenuItem.Size = new System.Drawing.Size(88, 22); + this.protocolToolStripMenuItem.Text = "All"; + // + // btnShowAll + // + this.btnShowAll.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnShowAll.Name = "btnShowAll"; + this.btnShowAll.Size = new System.Drawing.Size(73, 22); + this.btnShowAll.Text = "Show All"; + // + // toolStripSeparator7 + // + this.toolStripSeparator7.Name = "toolStripSeparator7"; + this.toolStripSeparator7.Size = new System.Drawing.Size(6, 25); + // + // toolStripLabel3 + // + this.toolStripLabel3.Name = "toolStripLabel3"; + this.toolStripLabel3.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel3.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Category", + "Area"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(545, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to request and manage certificates that the Web server can use w" + + "ith websites configured for SSL."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 8; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(189, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Server Certificates"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // CertificatesPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "CertificatesPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chName; + private ColumnHeader chIssuedTo; + private ColumnHeader chIssuedBy; + private ColumnHeader chExpiration; + private ColumnHeader chHash; + private ColumnHeader chStore; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private ContextMenuStrip cmsActionPanel; + private PictureBox pictureBox1; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbFilter; + private ToolStripSplitButton btnGo; + private ToolStripMenuItem protocolToolStripMenuItem; + private ToolStripButton btnShowAll; + private ToolStripSeparator toolStripSeparator7; + private ToolStripLabel toolStripLabel3; + private ToolStripComboBox cbGroup; + } +} diff --git a/JexusManager.Features.Certificates/CertificatesPage.cs b/JexusManager.Features.Certificates/CertificatesPage.cs new file mode 100644 index 00000000..14e450c3 --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesPage.cs @@ -0,0 +1,142 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates +{ + using System; + using System.Collections; + using System.Reflection; + using System.Security.Cryptography.X509Certificates; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class CertificatesPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly CertificatesPage _owner; + + public PageTaskList(CertificatesPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class CertificatesListViewItem : ListViewItem + { + public CertificatesItem Item { get; } + private readonly CertificatesPage _page; + + public CertificatesListViewItem(CertificatesItem item, CertificatesPage page) + : base(item.Certificate.FriendlyName) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Certificate.GetNameInfo(X509NameType.DnsName, false))); + SubItems.Add(new ListViewSubItem(this, item.Certificate.GetNameInfo(X509NameType.DnsName, true))); + SubItems.Add(new ListViewSubItem(this, item.Certificate.GetExpirationDateString())); + SubItems.Add(new ListViewSubItem(this, item.Certificate.GetCertHashString())); + SubItems.Add(new ListViewSubItem(this, item.Store)); + } + } + + private CertificatesFeature _feature; + private PageTaskList _taskList; + + public CertificatesPage() + { + InitializeComponent(); + btnGo.Image = DefaultTaskList.GoImage; + btnShowAll.Image = DefaultTaskList.ShowAllImage; + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new CertificatesFeature(Module); + _feature.CertificatesSettingsUpdated = InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new CertificatesListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + Refresh(); + return; + } + + foreach (CertificatesListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((CertificatesListViewItem)listView1.SelectedItems[0]).Item + : null; + Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.Certificates/CertificatesPage.resx b/JexusManager.Features.Certificates/CertificatesPage.resx new file mode 100644 index 00000000..330bdf72 --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesPage.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 122, 17 + + + 451, 56 + + + 17, 17 + + + 17, 17 + + + 179 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/CertificatesSettingsSavedEventHandler.cs b/JexusManager.Features.Certificates/CertificatesSettingsSavedEventHandler.cs new file mode 100644 index 00000000..3fe2e50a --- /dev/null +++ b/JexusManager.Features.Certificates/CertificatesSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates +{ + public delegate void CertificatesSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Certificates/CompleteRequestDialog.Designer.cs b/JexusManager.Features.Certificates/CompleteRequestDialog.Designer.cs new file mode 100644 index 00000000..f49c5924 --- /dev/null +++ b/JexusManager.Features.Certificates/CompleteRequestDialog.Designer.cs @@ -0,0 +1,219 @@ +namespace JexusManager.Features.Certificates +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class CompleteRequestDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel1 = new System.Windows.Forms.Panel(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.txtTitle = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbStore = new System.Windows.Forms.ComboBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.BackColor = System.Drawing.Color.White; + this.panel1.Controls.Add(this.pictureBox1); + this.panel1.Controls.Add(this.txtTitle); + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(670, 65); + this.panel1.TabIndex = 11; + // + // pictureBox1 + // + this.pictureBox1.Image = Properties.Resources.certificates_48; + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(48, 48); + this.pictureBox1.TabIndex = 1; + this.pictureBox1.TabStop = false; + // + // txtTitle + // + this.txtTitle.AutoSize = true; + this.txtTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtTitle.Location = new System.Drawing.Point(87, 22); + this.txtTitle.Name = "txtTitle"; + this.txtTitle.Size = new System.Drawing.Size(326, 24); + this.txtTitle.TabIndex = 2; + this.txtTitle.Text = "Specify Certificate Authority Response"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(23, 82); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(502, 26); + this.label1.TabIndex = 12; + this.label1.Text = "Specify a previously created certificate request by retrieving the file that cont" + + "ains the certificate authority\'s\r\nresponse."; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(23, 180); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(75, 13); + this.label2.TabIndex = 13; + this.label2.Text = "Friendly name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(26, 196); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(238, 20); + this.txtName.TabIndex = 2; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(23, 246); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(229, 13); + this.label3.TabIndex = 15; + this.label3.Text = "Select a certificate store for the new certificate:"; + // + // cbStore + // + this.cbStore.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbStore.FormattingEnabled = true; + this.cbStore.Items.AddRange(new object[] { + "Personal", + "Web Hosting"}); + this.cbStore.Location = new System.Drawing.Point(26, 262); + this.cbStore.Name = "cbStore"; + this.cbStore.Size = new System.Drawing.Size(238, 21); + this.cbStore.TabIndex = 3; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(466, 441); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(85, 23); + this.btnOK.TabIndex = 4; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(557, 441); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(85, 23); + this.btnCancel.TabIndex = 5; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(23, 119); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(278, 13); + this.label4.TabIndex = 19; + this.label4.Text = "File name containing the certification authority\'s response:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(26, 135); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(360, 20); + this.txtPath.TabIndex = 0; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(392, 133); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(30, 23); + this.btnBrowse.TabIndex = 1; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // CompleteRequestDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(654, 476); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label4); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.cbStore); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.panel1); + this.Name = "CompleteRequestDialog"; + this.Text = "Complete Certificate Request"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.SelfCertificateDialogHelpButtonClicked); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Panel panel1; + private PictureBox pictureBox1; + private Label txtTitle; + private Label label1; + private Label label2; + private TextBox txtName; + private Label label3; + private ComboBox cbStore; + private Button btnOK; + private Button btnCancel; + private Label label4; + private TextBox txtPath; + private Button btnBrowse; + } +} diff --git a/JexusManager.Features.Certificates/CompleteRequestDialog.cs b/JexusManager.Features.Certificates/CompleteRequestDialog.cs new file mode 100644 index 00000000..c737e11d --- /dev/null +++ b/JexusManager.Features.Certificates/CompleteRequestDialog.cs @@ -0,0 +1,155 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Web.Administration; + +namespace JexusManager.Features.Certificates +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Security.Cryptography.X509Certificates; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + using Mono.Security.Authenticode; + + public partial class CompleteRequestDialog : DialogForm + { + public CompleteRequestDialog(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + cbStore.SelectedIndex = 0; + if (Environment.OSVersion.Version.Major < 8) + { + // IMPORTANT: WebHosting store is available since Windows 8. + cbStore.Enabled = false; + } + + if (!Helper.IsRunningOnMono()) + { + NativeMethods.TryAddShieldToButton(btnOK); + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (!File.Exists(txtPath.Text)) + { + ShowMessage( + string.Format( + "There was an error while performing this operation.{0}{0}Details:{0}{0}Could not find file '{1}'.", + Environment.NewLine, + txtPath.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + // TODO: check administrator permission. + var x509 = new X509Certificate2(txtPath.Text); + + var filename = DialogHelper.GetPrivateKeyFile(x509.Subject); + if (!File.Exists(filename)) + { + ShowMessage( + string.Format( + "There was an error while performing this operation.{0}{0}Details:{0}{0}Could not find private key for '{1}'.", + Environment.NewLine, + txtPath.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + x509.PrivateKey = PrivateKey.CreateFromFile(filename).RSA; + x509.FriendlyName = txtName.Text; + var p12File = DialogHelper.GetTempFileName(); + var p12pwd = "test"; + var raw = x509.Export(X509ContentType.Pfx, p12pwd); + File.WriteAllBytes(p12File, raw); + + Item = x509; + Store = cbStore.SelectedIndex == 0 ? "Personal" : "WebHosting"; + + try + { + // add certificate + using (var process = new Process()) + { + var start = process.StartInfo; + start.Verb = "runas"; + start.FileName = "cmd"; + start.Arguments = string.Format("/c \"\"{4}\" /f:\"{0}\" /p:{1} /n:\"{2}\" /s:{3}\"", + p12File, + p12pwd, + txtName.Text, + cbStore.SelectedIndex == 0 ? "MY" : "WebHosting", + Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")); + start.CreateNoWindow = true; + start.WindowStyle = ProcessWindowStyle.Hidden; + process.Start(); + process.WaitForExit(); + File.Delete(p12File); + if (process.ExitCode == 0) + { + this.DialogResult = DialogResult.OK; + } + else + { + MessageBox.Show(process.ExitCode.ToString()); + } + } + } + catch (Exception) + { + // elevation is cancelled. + } + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + var dialog = new OpenFileDialog + { + FileName = txtPath.Text + }; + if (dialog.ShowDialog() == DialogResult.Cancel) + { + return; + } + + txtPath.Text = dialog.FileName; + })); + } + + public string Store { get; set; } + + public X509Certificate2 Item { get; set; } + + private void SelfCertificateDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210528"); + } + } +} diff --git a/JexusManager.Features.Certificates/CompleteRequestDialog.resx b/JexusManager.Features.Certificates/CompleteRequestDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/CompleteRequestDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/ExportCertificateDialog.Designer.cs b/JexusManager.Features.Certificates/ExportCertificateDialog.Designer.cs new file mode 100644 index 00000000..7f2d8b59 --- /dev/null +++ b/JexusManager.Features.Certificates/ExportCertificateDialog.Designer.cs @@ -0,0 +1,160 @@ +namespace JexusManager.Features.Certificates +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class ExportCertificateDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.txtPassword = new System.Windows.Forms.TextBox(); + this.txtConfirm = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 18); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(52, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Export to:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 34); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(277, 20); + this.txtPath.TabIndex = 1; + // + // txtPassword + // + this.txtPassword.Location = new System.Drawing.Point(15, 95); + this.txtPassword.Name = "txtPassword"; + this.txtPassword.PasswordChar = '*'; + this.txtPassword.Size = new System.Drawing.Size(317, 20); + this.txtPassword.TabIndex = 2; + // + // txtConfirm + // + this.txtConfirm.Location = new System.Drawing.Point(15, 159); + this.txtConfirm.Name = "txtConfirm"; + this.txtConfirm.PasswordChar = '*'; + this.txtConfirm.Size = new System.Drawing.Size(317, 20); + this.txtConfirm.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 79); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(56, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Password:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 143); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(93, 13); + this.label3.TabIndex = 5; + this.label3.Text = "Confirm password:"; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(242, 216); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(90, 23); + this.btnCancel.TabIndex = 6; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(151, 216); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(85, 23); + this.btnOK.TabIndex = 7; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(299, 32); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(33, 23); + this.btnBrowse.TabIndex = 8; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // ExportCertificateDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(344, 251); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtConfirm); + this.Controls.Add(this.txtPassword); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label1); + this.Name = "ExportCertificateDialog"; + this.Text = "Export Certificate"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.ExportCertificateDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtPath; + private TextBox txtPassword; + private TextBox txtConfirm; + private Label label2; + private Label label3; + private Button btnCancel; + private Button btnOK; + private Button btnBrowse; + } +} diff --git a/JexusManager.Features.Certificates/ExportCertificateDialog.cs b/JexusManager.Features.Certificates/ExportCertificateDialog.cs new file mode 100644 index 00000000..1d895f36 --- /dev/null +++ b/JexusManager.Features.Certificates/ExportCertificateDialog.cs @@ -0,0 +1,75 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Security.Cryptography.X509Certificates; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + public partial class ExportCertificateDialog : DialogForm + { + private readonly X509Certificate2 _certificate2; + + public ExportCertificateDialog(X509Certificate2 certificate2, IServiceProvider serviceProvider) + : base(serviceProvider) + { + _certificate2 = certificate2; + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtPath, "TextChanged") + .Merge(Observable.FromEventPattern(txtPassword, "TextChanged")) + .Merge(Observable.FromEventPattern(txtConfirm, "TextChange")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtPath.Text) + && !string.IsNullOrWhiteSpace(txtPassword.Text) + && !string.IsNullOrWhiteSpace(txtConfirm.Text) + && txtPassword.Text == txtConfirm.Text; + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + var dialog = new SaveFileDialog + { + FileName = txtPath.Text + }; + if (dialog.ShowDialog() == DialogResult.Cancel) + { + return; + } + + txtPath.Text = dialog.FileName; + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + var raw = _certificate2.Export(X509ContentType.Pfx, txtPassword.Text); + File.WriteAllBytes(txtPath.Text, raw); + DialogResult = DialogResult.OK; + })); + } + + private void ExportCertificateDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210528"); + } + } +} diff --git a/JexusManager.Features.Certificates/ExportCertificateDialog.resx b/JexusManager.Features.Certificates/ExportCertificateDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/ExportCertificateDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/ImportCertificateDialog.Designer.cs b/JexusManager.Features.Certificates/ImportCertificateDialog.Designer.cs new file mode 100644 index 00000000..25f53edc --- /dev/null +++ b/JexusManager.Features.Certificates/ImportCertificateDialog.Designer.cs @@ -0,0 +1,184 @@ +namespace JexusManager.Features.Certificates +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class ImportCertificateDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtFile = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtPassword = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbStore = new System.Windows.Forms.ComboBox(); + this.cbExport = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(252, 251); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(156, 251); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(317, 36); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(30, 23); + this.btnBrowse.TabIndex = 2; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(9, 22); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(99, 13); + this.label1.TabIndex = 3; + this.label1.Text = "Certificate file (.pfx):"; + // + // txtFile + // + this.txtFile.Location = new System.Drawing.Point(12, 38); + this.txtFile.Name = "txtFile"; + this.txtFile.Size = new System.Drawing.Size(299, 20); + this.txtFile.TabIndex = 4; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(9, 78); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(56, 13); + this.label2.TabIndex = 5; + this.label2.Text = "Password:"; + // + // txtPassword + // + this.txtPassword.Location = new System.Drawing.Point(12, 94); + this.txtPassword.Name = "txtPassword"; + this.txtPassword.Size = new System.Drawing.Size(335, 20); + this.txtPassword.TabIndex = 6; + this.txtPassword.UseSystemPasswordChar = true; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(9, 135); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(118, 13); + this.label3.TabIndex = 7; + this.label3.Text = "Select Certificate Store:"; + // + // cbStore + // + this.cbStore.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbStore.Enabled = false; + this.cbStore.FormattingEnabled = true; + this.cbStore.Items.AddRange(new object[] { + "Jexus"}); + this.cbStore.Location = new System.Drawing.Point(12, 151); + this.cbStore.Name = "cbStore"; + this.cbStore.Size = new System.Drawing.Size(335, 21); + this.cbStore.TabIndex = 8; + // + // cbExport + // + this.cbExport.AutoSize = true; + this.cbExport.Enabled = false; + this.cbExport.Location = new System.Drawing.Point(12, 200); + this.cbExport.Name = "cbExport"; + this.cbExport.Size = new System.Drawing.Size(190, 17); + this.cbExport.TabIndex = 9; + this.cbExport.Text = "Allow this certificate to be exported"; + this.cbExport.UseVisualStyleBackColor = true; + // + // ImportCertificateDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(359, 286); + this.Controls.Add(this.cbExport); + this.Controls.Add(this.cbStore); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtPassword); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtFile); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.HelpButton = true; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ImportCertificateDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Import Certificate"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.ImportCertificateDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Button btnBrowse; + private Label label1; + private TextBox txtFile; + private Label label2; + private TextBox txtPassword; + private Label label3; + private ComboBox cbStore; + private CheckBox cbExport; + } +} diff --git a/JexusManager.Features.Certificates/ImportCertificateDialog.cs b/JexusManager.Features.Certificates/ImportCertificateDialog.cs new file mode 100644 index 00000000..4267eb42 --- /dev/null +++ b/JexusManager.Features.Certificates/ImportCertificateDialog.cs @@ -0,0 +1,168 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + using System.Text; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + using Org.BouncyCastle.Crypto; + using Org.BouncyCastle.OpenSsl; + using Org.BouncyCastle.Security; + + public partial class ImportCertificateDialog : DialogForm + { + public ImportCertificateDialog(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + cbStore.SelectedIndex = 0; + if (Environment.OSVersion.Version.Major < 8) + { + // IMPORTANT: WebHosting store is available since Windows 8. + cbStore.Enabled = false; + } + + if (!Helper.IsRunningOnMono()) + { + NativeMethods.TryAddShieldToButton(btnOK); + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + var dialog = new OpenFileDialog { Filter = ".pfx|*.pfx|*.*|*.*", FilterIndex = 0, Multiselect = false }; + if (dialog.ShowDialog() == DialogResult.Cancel) + { + return; + } + + txtFile.Text = dialog.FileName; + })); + + container.Add( + Observable.FromEventPattern(txtFile, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtFile.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(async evt => + { + try + { + // Load your certificate from file + Item = new X509Certificate2(txtFile.Text, txtPassword.Text, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); + Store = cbStore.SelectedIndex == 0 ? "Personal" : "WebHosting"; + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + if (service.ServerManager.Mode == WorkingMode.Jexus) + { + // Public Key; + StringBuilder publicBuilder = new StringBuilder(); + publicBuilder.AppendLine("-----BEGIN CERTIFICATE-----"); + publicBuilder.AppendLine(Convert.ToBase64String(Item.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)); + publicBuilder.AppendLine("-----END CERTIFICATE-----"); + var file = await service.ServerManager.SaveCertificateAsync(publicBuilder.ToString()); + service.ServerManager.SetCertificate(file); + // Private Key + RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)Item.PrivateKey; + MemoryStream memoryStream = new MemoryStream(); + TextWriter streamWriter = new StreamWriter(memoryStream); + PemWriter pemWriter = new PemWriter(streamWriter); + AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetRsaKeyPair(rsa); + pemWriter.WriteObject(keyPair.Private); + streamWriter.Flush(); + string output = Encoding.ASCII.GetString(memoryStream.GetBuffer()).Trim(); + int indexOfFooter = output.IndexOf("-----END RSA PRIVATE KEY-----", StringComparison.Ordinal); + memoryStream.Close(); + streamWriter.Close(); + string key = output.Substring(0, indexOfFooter + 29); + var keyFile = await service.ServerManager.SaveKeyAsync(key); + service.ServerManager.SetKeyFile(keyFile); + await service.ServerManager.CommitChangesAsync(); + } + else + { + try + { + using (var process = new Process()) + { + // add certificate + var start = process.StartInfo; + start.Verb = "runas"; + start.FileName = "cmd"; + start.Arguments = string.Format( + "/c \"\"{4}\" /f:\"{0}\" /p:{1} /n:\"{2}\" /s:{3}\"", + txtFile.Text, + txtPassword.Text, + Item.FriendlyName, + cbStore.SelectedIndex == 0 ? "MY" : "WebHosting", + Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")); + start.CreateNoWindow = true; + start.WindowStyle = ProcessWindowStyle.Hidden; + process.Start(); + process.WaitForExit(); + if (process.ExitCode == 0) + { + this.DialogResult = DialogResult.OK; + } + else + { + MessageBox.Show(process.ExitCode.ToString()); + } + } + } + catch (Exception) + { + // elevation is cancelled. + } + } + } + catch (Exception ex) + { + var message = new StringBuilder() + .AppendLine("There was an error while performing this operation.") + .AppendLine() + .AppendLine("Details:") + .AppendLine() + .AppendLine(ex.Message); + ShowMessage( + message.ToString(), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + } + })); + } + + private void ImportCertificateDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210528"); + } + + public string Store { get; set; } + + public X509Certificate2 Item { get; set; } + } +} diff --git a/JexusManager.Features.Certificates/ImportCertificateDialog.resx b/JexusManager.Features.Certificates/ImportCertificateDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/ImportCertificateDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/JexusManager.Features.Certificates.csproj b/JexusManager.Features.Certificates/JexusManager.Features.Certificates.csproj new file mode 100644 index 00000000..c8fbb50d --- /dev/null +++ b/JexusManager.Features.Certificates/JexusManager.Features.Certificates.csproj @@ -0,0 +1,237 @@ + + + + + Debug + AnyCPU + {D37CF28B-10F1-4F28-B933-C5E18D7F09AA} + Library + Properties + JexusManager.Features.Certificates + JexusManager.Features.Certificates + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll + True + + + ..\lib\Mono.Security.dll + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + Helper.cs + + + + + + + UserControl + + + CertificatesPage.cs + + + + Form + + + CompleteRequestDialog.cs + + + Form + + + ExportCertificateDialog.cs + + + Form + + + ImportCertificateDialog.cs + + + + True + True + Resources.resx + + + Form + + + SelfCertificateDialog.cs + + + Form + + + CertificateRenewWizard.cs + + + + UserControl + + + FinishPage.cs + + + usercontrol + + + OptionsPage.cs + + + Form + + + CertificateRequestWizard.cs + + + + UserControl + + + FinishPage.cs + + + UserControl + + + KeysPage.cs + + + usercontrol + + + PropertiesPage.cs + + + + + CertificatesPage.cs + + + CompleteRequestDialog.cs + + + ExportCertificateDialog.cs + + + ImportCertificateDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SelfCertificateDialog.cs + + + CertificateRenewWizard.cs + + + FinishPage.cs + + + OptionsPage.cs + + + CertificateRequestWizard.cs + + + FinishPage.cs + + + KeysPage.cs + + + PropertiesPage.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/JexusManager.snk b/JexusManager.Features.Certificates/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Certificates/JexusManager.snk differ diff --git a/JexusManager.Features.Certificates/Properties/AssemblyInfo.cs b/JexusManager.Features.Certificates/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d103ce55 --- /dev/null +++ b/JexusManager.Features.Certificates/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Certificates")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Certificates")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("d37cf28b-10f1-4f28-b933-c5e18d7f09aa")] + +// 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")] diff --git a/JexusManager.Features.Certificates/Properties/Resources.Designer.cs b/JexusManager.Features.Certificates/Properties/Resources.Designer.cs new file mode 100644 index 00000000..d5176bb8 --- /dev/null +++ b/JexusManager.Features.Certificates/Properties/Resources.Designer.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Certificates.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Certificates.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap certificates_36 { + get { + object obj = ResourceManager.GetObject("certificates_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap certificates_48 { + get { + object obj = ResourceManager.GetObject("certificates_48", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Certificates/Properties/Resources.resx b/JexusManager.Features.Certificates/Properties/Resources.resx new file mode 100644 index 00000000..96b46ac7 --- /dev/null +++ b/JexusManager.Features.Certificates/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\certificates_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\certificates_48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Resources/certificates_36.png b/JexusManager.Features.Certificates/Resources/certificates_36.png new file mode 100644 index 00000000..790b2a64 Binary files /dev/null and b/JexusManager.Features.Certificates/Resources/certificates_36.png differ diff --git a/JexusManager.Features.Certificates/Resources/certificates_48.png b/JexusManager.Features.Certificates/Resources/certificates_48.png new file mode 100644 index 00000000..572f2bd5 Binary files /dev/null and b/JexusManager.Features.Certificates/Resources/certificates_48.png differ diff --git a/JexusManager.Features.Certificates/SelfCertificateDialog.Designer.cs b/JexusManager.Features.Certificates/SelfCertificateDialog.Designer.cs new file mode 100644 index 00000000..dbed0639 --- /dev/null +++ b/JexusManager.Features.Certificates/SelfCertificateDialog.Designer.cs @@ -0,0 +1,266 @@ +namespace JexusManager.Features.Certificates +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class SelfCertificateDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel1 = new System.Windows.Forms.Panel(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.txtTitle = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbStore = new System.Windows.Forms.ComboBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.txtCommonName = new System.Windows.Forms.TextBox(); + this.cbLength = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.cbHashing = new System.Windows.Forms.ComboBox(); + this.panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.BackColor = System.Drawing.Color.White; + this.panel1.Controls.Add(this.pictureBox1); + this.panel1.Controls.Add(this.txtTitle); + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(670, 65); + this.panel1.TabIndex = 11; + // + // pictureBox1 + // + this.pictureBox1.Image = Properties.Resources.certificates_48; + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(48, 48); + this.pictureBox1.TabIndex = 1; + this.pictureBox1.TabStop = false; + // + // txtTitle + // + this.txtTitle.AutoSize = true; + this.txtTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtTitle.Location = new System.Drawing.Point(87, 22); + this.txtTitle.Name = "txtTitle"; + this.txtTitle.Size = new System.Drawing.Size(200, 24); + this.txtTitle.TabIndex = 2; + this.txtTitle.Text = "Specify Friendly Name"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(23, 82); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(483, 26); + this.label1.TabIndex = 12; + this.label1.Text = "Specify a file name for the certificate request. This information can be sent to " + + "a certificate authority for\r\nsigning:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(23, 126); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(201, 13); + this.label2.TabIndex = 13; + this.label2.Text = "Specify a friendly name for the certificate:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(26, 142); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(360, 20); + this.txtName.TabIndex = 14; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(23, 192); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(229, 13); + this.label3.TabIndex = 15; + this.label3.Text = "Select a certificate store for the new certificate:"; + // + // cbStore + // + this.cbStore.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbStore.FormattingEnabled = true; + this.cbStore.Items.AddRange(new object[] { + "Personal", + "Web Hosting"}); + this.cbStore.Location = new System.Drawing.Point(26, 208); + this.cbStore.Name = "cbStore"; + this.cbStore.Size = new System.Drawing.Size(238, 21); + this.cbStore.TabIndex = 16; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(466, 441); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(85, 23); + this.btnOK.TabIndex = 17; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(557, 441); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(85, 23); + this.btnCancel.TabIndex = 18; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(23, 260); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(163, 13); + this.label4.TabIndex = 19; + this.label4.Text = "Specify a custom common name:"; + // + // txtCommonName + // + this.txtCommonName.Location = new System.Drawing.Point(26, 276); + this.txtCommonName.Name = "txtCommonName"; + this.txtCommonName.Size = new System.Drawing.Size(360, 20); + this.txtCommonName.TabIndex = 20; + // + // cbLength + // + this.cbLength.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbLength.FormattingEnabled = true; + this.cbLength.Items.AddRange(new object[] { + "384", + "512", + "1024", + "2048", + "4096", + "8192", + "16384"}); + this.cbLength.Location = new System.Drawing.Point(26, 331); + this.cbLength.Name = "cbLength"; + this.cbLength.Size = new System.Drawing.Size(238, 21); + this.cbLength.TabIndex = 22; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(23, 315); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(54, 13); + this.label5.TabIndex = 21; + this.label5.Text = "Bit length:"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(23, 372); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(87, 13); + this.label6.TabIndex = 23; + this.label6.Text = "Hashing method:"; + // + // cbHashing + // + this.cbHashing.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbHashing.FormattingEnabled = true; + this.cbHashing.Items.AddRange(new object[] { + "SHA1", + "SHA2 (SHA256)"}); + this.cbHashing.Location = new System.Drawing.Point(26, 388); + this.cbHashing.Name = "cbHashing"; + this.cbHashing.Size = new System.Drawing.Size(238, 21); + this.cbHashing.TabIndex = 24; + // + // SelfCertificateDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(654, 476); + this.Controls.Add(this.cbHashing); + this.Controls.Add(this.label6); + this.Controls.Add(this.cbLength); + this.Controls.Add(this.label5); + this.Controls.Add(this.txtCommonName); + this.Controls.Add(this.label4); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.cbStore); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.panel1); + this.Name = "SelfCertificateDialog"; + this.Text = "Create Self-Signed Certificate"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.SelfCertificateDialogHelpButtonClicked); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Panel panel1; + private PictureBox pictureBox1; + private Label txtTitle; + private Label label1; + private Label label2; + private TextBox txtName; + private Label label3; + private ComboBox cbStore; + private Button btnOK; + private Button btnCancel; + private Label label4; + private TextBox txtCommonName; + private ComboBox cbLength; + private Label label5; + private Label label6; + private ComboBox cbHashing; + } +} diff --git a/JexusManager.Features.Certificates/SelfCertificateDialog.cs b/JexusManager.Features.Certificates/SelfCertificateDialog.cs new file mode 100644 index 00000000..1d47a7c2 --- /dev/null +++ b/JexusManager.Features.Certificates/SelfCertificateDialog.cs @@ -0,0 +1,204 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Web.Administration; + +namespace JexusManager.Features.Certificates +{ + using System; + using System.Collections; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + using Mono.Security.X509; + using Mono.Security.X509.Extensions; + + using Org.BouncyCastle.Crypto; + using Org.BouncyCastle.Crypto.Digests; + using Org.BouncyCastle.Security; + using Org.BouncyCastle.X509; + + using X509Certificate = Mono.Security.X509.X509Certificate; + + public partial class SelfCertificateDialog : DialogForm + { + public SelfCertificateDialog(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + cbStore.SelectedIndex = 0; + cbLength.SelectedIndex = 3; + cbHashing.SelectedIndex = 1; + txtCommonName.Text = Environment.MachineName; + + if (Environment.OSVersion.Version.Major < 8) + { + // IMPORTANT: WebHosting store is available since Windows 8. + cbStore.Enabled = false; + } + + if (!Helper.IsRunningOnMono()) + { + NativeMethods.TryAddShieldToButton(btnOK); + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + // Generate certificate + string defaultIssuer = string.Format("CN={0}", txtCommonName.Text); + string defaultSubject = defaultIssuer; + byte[] sn = Guid.NewGuid().ToByteArray(); + string subject = defaultSubject; + string issuer = defaultIssuer; + DateTime notBefore = DateTime.Now; + DateTime notAfter = new DateTime(643445675990000000); // 12/31/2039 23:59:59Z + + RSA issuerKey = new RSACryptoServiceProvider(int.Parse(cbLength.Text)); + RSA subjectKey = null; + + CspParameters subjectParams = new CspParameters(); + CspParameters issuerParams = new CspParameters(); + BasicConstraintsExtension bce = new BasicConstraintsExtension + { + PathLenConstraint = BasicConstraintsExtension.NoPathLengthConstraint, + CertificateAuthority = true + }; + ExtendedKeyUsageExtension eku = new ExtendedKeyUsageExtension(); + eku.KeyPurpose.Add("1.3.6.1.5.5.7.3.1"); + SubjectAltNameExtension alt = null; + string p12File = Path.GetTempFileName(); + string p12pwd = "test"; + + // serial number MUST be positive + if ((sn[0] & 0x80) == 0x80) + { + sn[0] -= 0x80; + } + + if (subject != defaultSubject) + { + issuer = subject; + issuerKey = null; + } + else + { + subject = issuer; + subjectKey = issuerKey; + } + + if (subject == null) + throw new Exception("Missing Subject Name"); + + X509CertificateBuilder cb = new X509CertificateBuilder(3); + cb.SerialNumber = sn; + cb.IssuerName = issuer; + cb.NotBefore = notBefore; + cb.NotAfter = notAfter; + cb.SubjectName = subject; + cb.SubjectPublicKey = subjectKey; + // extensions + if (bce != null) + cb.Extensions.Add(bce); + if (eku != null) + cb.Extensions.Add(eku); + if (alt != null) + cb.Extensions.Add(alt); + + IDigest digest = new Sha1Digest(); + byte[] resBuf = new byte[digest.GetDigestSize()]; + var spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(DotNetUtilities.GetRsaPublicKey(issuerKey)); + byte[] bytes = spki.PublicKeyData.GetBytes(); + digest.BlockUpdate(bytes, 0, bytes.Length); + digest.DoFinal(resBuf, 0); + + cb.Extensions.Add(new SubjectKeyIdentifierExtension { Identifier = resBuf }); + cb.Extensions.Add(new AuthorityKeyIdentifierExtension { Identifier = resBuf }); + // signature + string hashName = cbHashing.SelectedIndex == 0 ? "SHA1" : "SHA256"; + cb.Hash = hashName; + byte[] rawcert = cb.Sign(issuerKey); + + PKCS12 p12 = new PKCS12(); + p12.Password = p12pwd; + + ArrayList list = new ArrayList(); + // we use a fixed array to avoid endianess issues + // (in case some tools requires the ID to be 1). + list.Add(new byte[] { 1, 0, 0, 0 }); + Hashtable attributes = new Hashtable(1); + attributes.Add(PKCS9.localKeyId, list); + + p12.AddCertificate(new X509Certificate(rawcert), attributes); + p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes); + p12.SaveToFile(p12File); + + Item = new X509Certificate2(p12File, p12pwd) { FriendlyName = txtName.Text }; + Store = cbStore.SelectedIndex == 0 ? "Personal" : "WebHosting"; + + try + { + using (var process = new Process()) + { + // add certificate + var start = process.StartInfo; + start.Verb = "runas"; + start.FileName = "cmd"; + start.Arguments = string.Format("/c \"\"{4}\" /f:\"{0}\" /p:{1} /n:\"{2}\" /s:{3}\"", + p12File, + p12pwd, + txtName.Text, + cbStore.SelectedIndex == 0 ? "MY" : "WebHosting", + Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")); + start.CreateNoWindow = true; + start.WindowStyle = ProcessWindowStyle.Hidden; + process.Start(); + process.WaitForExit(); + File.Delete(p12File); + if (process.ExitCode == 0) + { + this.DialogResult = DialogResult.OK; + } + else + { + MessageBox.Show(process.ExitCode.ToString()); + } + } + } + catch (Exception) + { + // elevation is cancelled. + } + })); + } + + public string Store { get; set; } + + public X509Certificate2 Item { get; set; } + + private void SelfCertificateDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210528"); + } + } +} diff --git a/JexusManager.Features.Certificates/SelfCertificateDialog.resx b/JexusManager.Features.Certificates/SelfCertificateDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/SelfCertificateDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.Designer.cs b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.Designer.cs new file mode 100644 index 00000000..78cd100a --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.Designer.cs @@ -0,0 +1,48 @@ +namespace JexusManager.Features.Certificates.Wizards.CertificateRenewWizard +{ + using System.ComponentModel; + + partial class CertificateRenewWizard + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // CertificateRequestWizard + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(669, 481); + this.Name = "CertificateRequestWizard"; + this.Text = "Connect to Server"; + this.ResumeLayout(false); + + } + + #endregion + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.cs b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.cs new file mode 100644 index 00000000..09a92062 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.cs @@ -0,0 +1,167 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRenewWizard +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + + using Microsoft.Web.Management.Client.Win32; + + using Mono.Security.Authenticode; + + using Org.BouncyCastle.Asn1; + using Org.BouncyCastle.Asn1.X509; + using Org.BouncyCastle.Crypto.Parameters; + using Org.BouncyCastle.Pkcs; + using Org.BouncyCastle.Security; + using Properties; + using Org.BouncyCastle.Crypto.Operators; + + public partial class CertificateRenewWizard : WizardForm + { + private readonly X509Certificate2 _existing; + private CertificateRenewWizardData _wizardData; + + public CertificateRenewWizard(X509Certificate2 existing, IServiceProvider serviceProvider) + : base(serviceProvider) + { + _existing = existing; + InitializeComponent(); + TaskGlyph = Resources.certificates_48; + } + + protected override object WizardData + { + get { return _wizardData ?? (_wizardData = new CertificateRenewWizardData()); } + } + + protected override void CompleteWizard() + { + X509Name subjectName = new X509Name(_existing.Subject); + + // Generate the private/public keypair + var keyPair = DotNetUtilities.GetKeyPair(_existing.PrivateKey); + + // Generate the CSR + Asn1Set attributes = new DerSet( + new DerSequence( + new DerObjectIdentifier("1.3.6.1.4.1.311.13.2.3"), + new DerSet(new DerIA5String(Environment.OSVersion.Version.ToString()))), + new DerSequence( + new DerObjectIdentifier("1.3.6.1.4.1.311.21.20"), + new DerSet( + new DerSequence( + new DerInteger(5), + new DerUtf8String(Environment.MachineName), + new DerUtf8String(Environment.UserName), + new DerUtf8String("JexusManager.exe")))), + new DerSequence( + new DerObjectIdentifier("1.3.6.1.4.1.311.13.2.2"), + new DerSet( + new DerSequence( + new DerInteger(1), + new DerBmpString("Microsoft RSA SChannel Cryptographic Provider"), + new DerBitString(new byte[0])))), + new DerSequence( + new DerObjectIdentifier("1.2.840.113549.1.9.14"), + new DerSet( + new DerSequence( + new DerSequence( + new DerObjectIdentifier("2.5.29.15"), + new DerBoolean(new byte[] { 0x01 }), + new DerOctetString(new byte[] { 0x03, 0x02, 0x04, 0xF0 })), + new DerSequence( + new DerObjectIdentifier("2.5.29.37"), + new DerOctetString(new byte[] + { + 0x30, 0x0a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x01 })), + new DerSequence( + new DerObjectIdentifier("1.2.840.113549.1.9.15"), + new DerOctetString(new byte[] + { + 0x30, 0x69, 0x30, 0x0e, + 0x06, 0x08, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, + 0x03, 0x02, 0x02, 0x02, + 0x00, 0x80, 0x30, 0x0e, + 0x06, 0x08, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, + 0x03, 0x04, 0x02, 0x02, + 0x00, 0x80, 0x30, 0x0b, + 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, + 0x04, 0x01, 0x2a, 0x30, + 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, + 0x03, 0x04, 0x01, 0x2d, + 0x30, 0x0b, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x01, + 0x02, 0x30, 0x0b, 0x06, + 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, + 0x01, 0x05, 0x30, 0x07, + 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x07, 0x30, + 0x0a, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x03, 0x07 + })), + new DerSequence( + new DerObjectIdentifier("2.5.29.14"), + new DerOctetString(new byte[] + { + 0x04, 0x14, 0xaa, 0x25, + 0xd9, 0xa2, 0x39, 0x7e, + 0x49, 0xd2, 0x94, 0x85, + 0x7e, 0x82, 0xa8, 0x8f, + 0x3b, 0x20, 0xf1, 0x4e, 0x65, 0xe5 + })))))); + + var signing = new Asn1SignatureFactory("SHA256withRSA", keyPair.Private); + Pkcs10CertificationRequest kpGen = new Pkcs10CertificationRequest(signing, subjectName, keyPair.Public, attributes, keyPair.Private); + using (var stream = new StreamWriter(_wizardData.FileName)) + { + stream.WriteLine(_wizardData.UseIisStyle ? "-----BEGIN NEW CERTIFICATE REQUEST-----" : "-----BEGIN CERTIFICATE REQUEST-----"); + stream.WriteLine(Convert.ToBase64String(kpGen.GetDerEncoded(), Base64FormattingOptions.InsertLineBreaks)); + stream.WriteLine(_wizardData.UseIisStyle ? "-----END NEW CERTIFICATE REQUEST-----" : "-----END CERTIFICATE REQUEST-----"); + } + + var key = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private); + PrivateKey pvk = new PrivateKey(); + pvk.RSA = new RSACryptoServiceProvider(); + pvk.RSA.ImportParameters(key); + pvk.Save(DialogHelper.GetPrivateKeyFile(_existing.Subject)); + } + + protected override bool CanComplete + { + get + { + var data = (CertificateRenewWizardData)WizardData; + return data?.IsComplete ?? false; + } + } + + protected override WizardPage[] GetWizardPages() + { + var optionsPage = new OptionsPage(); + optionsPage.SetWizard(this); + var finish = new FinishPage(); + finish.SetWizard(this); + return new WizardPage[] { optionsPage, finish }; + } + + protected override void ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210528"); + } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.resx b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizard.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizardData.cs b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizardData.cs new file mode 100644 index 00000000..301177cb --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/CertificateRenewWizardData.cs @@ -0,0 +1,18 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRenewWizard +{ + internal class CertificateRenewWizardData + { + public string FileName { get; set; } + + public bool IsComplete + { + get { return !string.IsNullOrWhiteSpace(FileName); } + } + + public bool UseIisStyle { get; set; } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.Designer.cs b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.Designer.cs new file mode 100644 index 00000000..7a9635f6 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.Designer.cs @@ -0,0 +1,144 @@ +namespace JexusManager.Features.Certificates.Wizards.CertificateRenewWizard +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class FinishPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label2 = new System.Windows.Forms.Label(); + this.txtConnect = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.rbOpen = new System.Windows.Forms.RadioButton(); + this.rbIis = new System.Windows.Forms.RadioButton(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(20, 74); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(259, 13); + this.label2.TabIndex = 7; + this.label2.Text = "Specify a file name for the certificate renewal request:"; + // + // txtConnect + // + this.txtConnect.AutoSize = true; + this.txtConnect.Location = new System.Drawing.Point(20, 25); + this.txtConnect.Name = "txtConnect"; + this.txtConnect.Size = new System.Drawing.Size(503, 26); + this.txtConnect.TabIndex = 6; + this.txtConnect.Text = "Specify the file name for the certificate request. This information can be sent t" + + "o a certification authority for \r\nsigning."; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(23, 90); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(358, 20); + this.txtPath.TabIndex = 8; + this.txtPath.TextChanged += new System.EventHandler(this.txtPath_TextChanged); + this.txtPath.VisibleChanged += new System.EventHandler(this.txtPath_TextChanged); + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(387, 88); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(30, 25); + this.btnBrowse.TabIndex = 9; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.rbOpen); + this.groupBox1.Controls.Add(this.rbIis); + this.groupBox1.Location = new System.Drawing.Point(23, 132); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(200, 100); + this.groupBox1.TabIndex = 10; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Request file style"; + // + // rbOpen + // + this.rbOpen.AutoSize = true; + this.rbOpen.Location = new System.Drawing.Point(15, 55); + this.rbOpen.Name = "rbOpen"; + this.rbOpen.Size = new System.Drawing.Size(71, 17); + this.rbOpen.TabIndex = 12; + this.rbOpen.Text = "OpenSSL"; + this.rbOpen.UseVisualStyleBackColor = true; + this.rbOpen.CheckedChanged += new System.EventHandler(this.FileStyle_Changed); + // + // rbIis + // + this.rbIis.AutoSize = true; + this.rbIis.Checked = true; + this.rbIis.Location = new System.Drawing.Point(15, 32); + this.rbIis.Name = "rbIis"; + this.rbIis.Size = new System.Drawing.Size(38, 17); + this.rbIis.TabIndex = 11; + this.rbIis.TabStop = true; + this.rbIis.Text = "IIS"; + this.rbIis.UseVisualStyleBackColor = true; + this.rbIis.CheckedChanged += new System.EventHandler(this.FileStyle_Changed); + // + // FinishPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtConnect); + this.Name = "FinishPage"; + this.Size = new System.Drawing.Size(670, 380); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private Label label2; + private Label txtConnect; + private TextBox txtPath; + private Button btnBrowse; + private GroupBox groupBox1; + private RadioButton rbOpen; + private RadioButton rbIis; + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.cs b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.cs new file mode 100644 index 00000000..4611fdd5 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.cs @@ -0,0 +1,61 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRenewWizard +{ + using System; + using System.Windows.Forms; + + public partial class FinishPage : DefaultWizardPage + { + private bool _initialized; + + public FinishPage() + { + InitializeComponent(); + Caption = "Specify save as file name"; + } + + + private void txtPath_TextChanged(object sender, EventArgs e) + { + if (!_initialized) + { + return; + } + + ((CertificateRenewWizardData)WizardData).FileName = txtPath.Text; + ((DefaultWizardForm)Wizard).UpdateWizard(); + } + + protected override void Activate() + { + base.Activate(); + _initialized = false; + txtPath.Text = ((CertificateRenewWizardData)WizardData).FileName; + _initialized = true; + txtPath.Focus(); + txtPath.SelectAll(); + } + + private void btnBrowse_Click(object sender, EventArgs e) + { + var dialog = new SaveFileDialog + { + FileName = txtPath.Text + }; + if (dialog.ShowDialog() == DialogResult.Cancel) + { + return; + } + + txtPath.Text = dialog.FileName; + } + + private void FileStyle_Changed(object sender, EventArgs e) + { + ((CertificateRenewWizardData)WizardData).UseIisStyle = rbIis.Checked; + } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.resx b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/FinishPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.Designer.cs b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.Designer.cs new file mode 100644 index 00000000..99378cdf --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.Designer.cs @@ -0,0 +1,133 @@ +namespace JexusManager.Features.Certificates.Wizards.CertificateRenewWizard +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class OptionsPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.RenewDomain = new System.Windows.Forms.RadioButton(); + this.label1 = new System.Windows.Forms.Label(); + this.rbRequestRenewal = new System.Windows.Forms.RadioButton(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.rbCompleteRenewal = new System.Windows.Forms.RadioButton(); + this.SuspendLayout(); + // + // RenewDomain + // + this.RenewDomain.AutoSize = true; + this.RenewDomain.Checked = true; + this.RenewDomain.Location = new System.Drawing.Point(27, 42); + this.RenewDomain.Name = "RenewDomain"; + this.RenewDomain.Size = new System.Drawing.Size(161, 17); + this.RenewDomain.TabIndex = 0; + this.RenewDomain.TabStop = true; + this.RenewDomain.Text = "Renew an existing certificate"; + this.RenewDomain.UseVisualStyleBackColor = true; + this.RenewDomain.Click += new System.EventHandler(this.Option_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(50, 62); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(387, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Lets you renew an existing certificate by using a certificate server in your doma" + + "in."; + // + // rbRequestRenewal + // + this.rbRequestRenewal.AutoSize = true; + this.rbRequestRenewal.Location = new System.Drawing.Point(27, 99); + this.rbRequestRenewal.Name = "rbRequestRenewal"; + this.rbRequestRenewal.Size = new System.Drawing.Size(192, 17); + this.rbRequestRenewal.TabIndex = 2; + this.rbRequestRenewal.Text = "Create a renewal certificate request"; + this.rbRequestRenewal.UseVisualStyleBackColor = true; + this.rbRequestRenewal.Click += new System.EventHandler(this.Option_Click); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(50, 119); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(421, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Lets you package renewal information to submit to a certification authority at a " + + "later time."; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(50, 176); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(466, 26); + this.label3.TabIndex = 4; + this.label3.Text = "Lets you complete the certificate renewal request by using the certificate that w" + + "as received from a\r\ncertification authority."; + // + // rbCompleteRenewal + // + this.rbCompleteRenewal.AutoSize = true; + this.rbCompleteRenewal.Location = new System.Drawing.Point(27, 156); + this.rbCompleteRenewal.Name = "rbCompleteRenewal"; + this.rbCompleteRenewal.Size = new System.Drawing.Size(196, 17); + this.rbCompleteRenewal.TabIndex = 5; + this.rbCompleteRenewal.Text = "Complete certificate renewal request"; + this.rbCompleteRenewal.UseVisualStyleBackColor = true; + this.rbCompleteRenewal.Click += new System.EventHandler(this.Option_Click); + // + // OptionsPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.rbCompleteRenewal); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.rbRequestRenewal); + this.Controls.Add(this.label1); + this.Controls.Add(this.RenewDomain); + this.Name = "OptionsPage"; + this.Size = new System.Drawing.Size(670, 380); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private RadioButton RenewDomain; + private Label label1; + private RadioButton rbRequestRenewal; + private Label label2; + private Label label3; + private RadioButton rbCompleteRenewal; + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.cs b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.cs new file mode 100644 index 00000000..8ad4e182 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRenewWizard +{ + using System; + + public partial class OptionsPage : DefaultWizardPage + { + public OptionsPage() + { + InitializeComponent(); + Caption = "Renew an existing certificate"; + } + + private void Option_Click(object sender, EventArgs e) + { + if (rbRequestRenewal.Checked) + { + var finish = (DefaultWizardPage)Pages[1]; + SetNextPage(finish); + finish.SetPreviousPage(this); + } + + UpdateWizard(); + } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.resx b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRenewWizard/OptionsPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.Designer.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.Designer.cs new file mode 100644 index 00000000..d003aad7 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.Designer.cs @@ -0,0 +1,48 @@ +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System.ComponentModel; + + partial class CertificateRequestWizard + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // CertificateRequestWizard + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(669, 481); + this.Name = "CertificateRequestWizard"; + this.Text = "Connect to Server"; + this.ResumeLayout(false); + + } + + #endregion + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.cs new file mode 100644 index 00000000..0ae099c1 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.cs @@ -0,0 +1,182 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Security.Cryptography; + + using Microsoft.Web.Management.Client.Win32; + + using Org.BouncyCastle.Asn1; + using Org.BouncyCastle.Asn1.X509; + using Org.BouncyCastle.Crypto; + using Org.BouncyCastle.Crypto.Generators; + using Org.BouncyCastle.Crypto.Parameters; + using Org.BouncyCastle.Crypto.Prng; + using Org.BouncyCastle.Pkcs; + using Org.BouncyCastle.Security; + using Properties; + using Mono.Security.Authenticode; + using Org.BouncyCastle.Crypto.Operators; + public partial class CertificateRequestWizard : DefaultWizardForm + { + private CertificateRequestWizardData _wizardData; + + public CertificateRequestWizard(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + TaskGlyph = Resources.certificates_48; + } + + protected override object WizardData + { + get { return _wizardData ?? (_wizardData = new CertificateRequestWizardData()); } + } + + protected override void CompleteWizard() + { + // Generate the CSR + X509Name subjectName = + new X509Name(string.Format("C={0},ST={1},L={2},O={3},OU={4},CN={5}", + _wizardData.Country, + _wizardData.State, + _wizardData.City, + _wizardData.Organization, + _wizardData.Unit, + _wizardData.CommonName)); + + // Generate the private/public keypair + RsaKeyPairGenerator kpgen = new RsaKeyPairGenerator(); + CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator(); + kpgen.Init(new KeyGenerationParameters(new SecureRandom(randomGenerator), _wizardData.Length)); + AsymmetricCipherKeyPair keyPair = kpgen.GenerateKeyPair(); + // Generate the CSR + + Asn1Set attributes = new DerSet( + new DerSequence( + new DerObjectIdentifier("1.3.6.1.4.1.311.13.2.3"), + new DerSet(new DerIA5String(Environment.OSVersion.Version.ToString()))), + new DerSequence( + new DerObjectIdentifier("1.3.6.1.4.1.311.21.20"), + new DerSet( + new DerSequence( + new DerInteger(5), + new DerUtf8String(Environment.MachineName), + new DerUtf8String(Environment.UserName), + new DerUtf8String("JexusManager.exe")))), + new DerSequence( + new DerObjectIdentifier("1.3.6.1.4.1.311.13.2.2"), + new DerSet( + new DerSequence( + new DerInteger(1), + new DerBmpString("Microsoft RSA SChannel Cryptographic Provider"), + new DerBitString(new byte[0])))), + new DerSequence( + new DerObjectIdentifier("1.2.840.113549.1.9.14"), + new DerSet( + new DerSequence( + new DerSequence( + new DerObjectIdentifier("2.5.29.15"), + new DerBoolean(new byte[] { 0x01 }), + new DerOctetString(new byte[] { 0x03, 0x02, 0x04, 0xF0 })), + new DerSequence( + new DerObjectIdentifier("2.5.29.37"), + new DerOctetString(new byte[] + { + 0x30, 0x0a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x01 })), + new DerSequence( + new DerObjectIdentifier("1.2.840.113549.1.9.15"), + new DerOctetString(new byte[] + { + 0x30, 0x69, 0x30, 0x0e, + 0x06, 0x08, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, + 0x03, 0x02, 0x02, 0x02, + 0x00, 0x80, 0x30, 0x0e, + 0x06, 0x08, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, + 0x03, 0x04, 0x02, 0x02, + 0x00, 0x80, 0x30, 0x0b, + 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, + 0x04, 0x01, 0x2a, 0x30, + 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, + 0x03, 0x04, 0x01, 0x2d, + 0x30, 0x0b, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x01, + 0x02, 0x30, 0x0b, 0x06, + 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, + 0x01, 0x05, 0x30, 0x07, + 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x07, 0x30, + 0x0a, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x03, 0x07 + })), + new DerSequence( + new DerObjectIdentifier("2.5.29.14"), + new DerOctetString(new byte[] + { + 0x04, 0x14, 0xaa, 0x25, + 0xd9, 0xa2, 0x39, 0x7e, + 0x49, 0xd2, 0x94, 0x85, + 0x7e, 0x82, 0xa8, 0x8f, + 0x3b, 0x20, 0xf1, 0x4e, 0x65, 0xe5 + })))))); + + var signing = new Asn1SignatureFactory("SHA256withRSA", keyPair.Private); + Pkcs10CertificationRequest kpGen = new Pkcs10CertificationRequest(signing, subjectName, keyPair.Public, attributes, keyPair.Private); + using (var stream = new StreamWriter(_wizardData.FileName)) + { + stream.WriteLine(_wizardData.UseIisStyle ? "-----BEGIN NEW CERTIFICATE REQUEST-----" : "-----BEGIN CERTIFICATE REQUEST-----"); + stream.WriteLine(Convert.ToBase64String(kpGen.GetDerEncoded(), Base64FormattingOptions.InsertLineBreaks)); + stream.WriteLine(_wizardData.UseIisStyle ? "-----END NEW CERTIFICATE REQUEST-----" : "-----END CERTIFICATE REQUEST-----"); + } + + var key = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private); + PrivateKey pvk = new PrivateKey(); + pvk.RSA = new RSACryptoServiceProvider(); + pvk.RSA.ImportParameters(key); + pvk.Save(DialogHelper.GetPrivateKeyFile(subjectName.ToString())); + } + + protected override bool CanComplete + { + get + { + var data = (CertificateRequestWizardData)WizardData; + return data?.IsComplete ?? false; + } + } + + protected override WizardPage[] GetWizardPages() + { + var properties = new PropertiesPage(); + var keys = new KeysPage(); + var finish = new FinishPage(); + properties.SetNextPage(keys); + properties.SetWizard(this); + keys.SetPreviousPage(properties); + keys.SetNextPage(finish); + keys.SetWizard(this); + finish.SetPreviousPage(keys); + finish.SetWizard(this); + return new WizardPage[] { properties, keys, finish }; + } + + protected override void ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210528"); + } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.resx b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizard.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizardData.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizardData.cs new file mode 100644 index 00000000..16ad64f8 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/CertificateRequestWizardData.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + internal class CertificateRequestWizardData + { + public string CommonName; + public string Organization { get; set; } + public string Unit { get; set; } + public string City { get; set; } + public string State { get; set; } + public string Country { get; set; } + public int Provider = 1; + public int Length { get; set; } + public string FileName { get; set; } + + public bool IsComplete + { + get { return !string.IsNullOrWhiteSpace(FileName); } + } + + public bool UseIisStyle { get; set; } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.Designer.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.Designer.cs new file mode 100644 index 00000000..7cd7e68e --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.Designer.cs @@ -0,0 +1,144 @@ +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class FinishPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label2 = new System.Windows.Forms.Label(); + this.txtConnect = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.rbIis = new System.Windows.Forms.RadioButton(); + this.rbOpen = new System.Windows.Forms.RadioButton(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(20, 74); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(219, 13); + this.label2.TabIndex = 7; + this.label2.Text = "Specify a file name for the certificate request:"; + // + // txtConnect + // + this.txtConnect.AutoSize = true; + this.txtConnect.Location = new System.Drawing.Point(20, 25); + this.txtConnect.Name = "txtConnect"; + this.txtConnect.Size = new System.Drawing.Size(495, 26); + this.txtConnect.TabIndex = 6; + this.txtConnect.Text = "Specify the file name for the certificate request. This information can be sent t" + + "o a certificate authority for \r\nsigning."; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(23, 90); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(358, 20); + this.txtPath.TabIndex = 8; + this.txtPath.TextChanged += new System.EventHandler(this.txtPath_TextChanged); + this.txtPath.VisibleChanged += new System.EventHandler(this.txtPath_TextChanged); + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(387, 88); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(30, 25); + this.btnBrowse.TabIndex = 9; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.rbOpen); + this.groupBox1.Controls.Add(this.rbIis); + this.groupBox1.Location = new System.Drawing.Point(23, 132); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(200, 100); + this.groupBox1.TabIndex = 10; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Request file style"; + // + // rbIis + // + this.rbIis.AutoSize = true; + this.rbIis.Checked = true; + this.rbIis.Location = new System.Drawing.Point(15, 32); + this.rbIis.Name = "rbIis"; + this.rbIis.Size = new System.Drawing.Size(38, 17); + this.rbIis.TabIndex = 11; + this.rbIis.TabStop = true; + this.rbIis.Text = "IIS"; + this.rbIis.UseVisualStyleBackColor = true; + this.rbIis.CheckedChanged += new System.EventHandler(this.FileStyle_Changed); + // + // rbOpen + // + this.rbOpen.AutoSize = true; + this.rbOpen.Location = new System.Drawing.Point(15, 55); + this.rbOpen.Name = "rbOpen"; + this.rbOpen.Size = new System.Drawing.Size(71, 17); + this.rbOpen.TabIndex = 12; + this.rbOpen.Text = "OpenSSL"; + this.rbOpen.UseVisualStyleBackColor = true; + this.rbOpen.CheckedChanged += new System.EventHandler(this.FileStyle_Changed); + // + // FinishPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtConnect); + this.Name = "FinishPage"; + this.Size = new System.Drawing.Size(670, 380); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private Label label2; + private Label txtConnect; + private TextBox txtPath; + private Button btnBrowse; + private GroupBox groupBox1; + private RadioButton rbOpen; + private RadioButton rbIis; + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.cs new file mode 100644 index 00000000..0a601b8b --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.cs @@ -0,0 +1,61 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System; + using System.Windows.Forms; + + public partial class FinishPage : DefaultWizardPage + { + private bool _initialized; + + public FinishPage() + { + InitializeComponent(); + Caption = "File Name"; + } + + + private void txtPath_TextChanged(object sender, EventArgs e) + { + if (!_initialized) + { + return; + } + + ((CertificateRequestWizardData)WizardData).FileName = txtPath.Text; + ((DefaultWizardForm)Wizard).UpdateWizard(); + } + + protected override void Activate() + { + base.Activate(); + _initialized = false; + txtPath.Text = ((CertificateRequestWizardData)WizardData).FileName; + _initialized = true; + txtPath.Focus(); + txtPath.SelectAll(); + } + + private void btnBrowse_Click(object sender, EventArgs e) + { + var dialog = new SaveFileDialog + { + FileName = txtPath.Text + }; + if (dialog.ShowDialog() == DialogResult.Cancel) + { + return; + } + + txtPath.Text = dialog.FileName; + } + + private void FileStyle_Changed(object sender, EventArgs e) + { + ((CertificateRequestWizardData)WizardData).UseIisStyle = rbIis.Checked; + } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.resx b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/FinishPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.Designer.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.Designer.cs new file mode 100644 index 00000000..827756b6 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.Designer.cs @@ -0,0 +1,116 @@ +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class KeysPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(KeysPage)); + this.txtConnect = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.cbProvider = new System.Windows.Forms.ComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbLength = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // txtConnect + // + this.txtConnect.AutoSize = true; + this.txtConnect.Location = new System.Drawing.Point(20, 25); + this.txtConnect.Name = "txtConnect"; + this.txtConnect.Size = new System.Drawing.Size(515, 39); + this.txtConnect.TabIndex = 0; + this.txtConnect.Text = resources.GetString("txtConnect.Text"); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(20, 73); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(153, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Cryptographic service provider:"; + // + // cbProvider + // + this.cbProvider.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbProvider.FormattingEnabled = true; + this.cbProvider.Items.AddRange(new object[] { + "Microsoft DH SChannel Cryptographic Provider", + "Microsoft RSA SChannel Cryptographic Provider"}); + this.cbProvider.Location = new System.Drawing.Point(23, 89); + this.cbProvider.Name = "cbProvider"; + this.cbProvider.Size = new System.Drawing.Size(392, 21); + this.cbProvider.TabIndex = 0; + this.cbProvider.SelectedIndexChanged += new System.EventHandler(this.CbProviderSelectedIndexChanged); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(20, 124); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(54, 13); + this.label3.TabIndex = 3; + this.label3.Text = "Bit length:"; + // + // cbLength + // + this.cbLength.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbLength.FormattingEnabled = true; + this.cbLength.Location = new System.Drawing.Point(23, 140); + this.cbLength.Name = "cbLength"; + this.cbLength.Size = new System.Drawing.Size(121, 21); + this.cbLength.TabIndex = 4; + this.cbLength.SelectedIndexChanged += new System.EventHandler(this.CbLengthSelectedIndexChanged); + // + // KeysPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.cbLength); + this.Controls.Add(this.label3); + this.Controls.Add(this.cbProvider); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtConnect); + this.Name = "KeysPage"; + this.Size = new System.Drawing.Size(670, 380); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label txtConnect; + private Label label2; + private ComboBox cbProvider; + private Label label3; + private ComboBox cbLength; + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.cs new file mode 100644 index 00000000..feeadd23 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.cs @@ -0,0 +1,53 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System; + + public partial class KeysPage : DefaultWizardPage + { + public KeysPage() + { + InitializeComponent(); + Caption = "Cryptographic Service Provider Properties"; + } + + protected override void Activate() + { + base.Activate(); + var wizardData = (CertificateRequestWizardData)WizardData; + cbProvider.SelectedIndex = wizardData.Provider; + } + + private void CbProviderSelectedIndexChanged(object sender, EventArgs e) + { + cbLength.Items.Clear(); + if (cbProvider.SelectedIndex == 0) + { + cbLength.Items.Add("512"); + cbLength.Items.Add("1024"); + cbLength.SelectedIndex = 0; + } + else + { + cbLength.Items.Add("384"); + cbLength.Items.Add("512"); + cbLength.Items.Add("1024"); + cbLength.Items.Add("2048"); + cbLength.Items.Add("4096"); + cbLength.Items.Add("8192"); + cbLength.Items.Add("16384"); + cbLength.SelectedIndex = 2; + } + } + + private void CbLengthSelectedIndexChanged(object sender, EventArgs e) + { + var wizardData = (CertificateRequestWizardData)WizardData; + wizardData.Provider = cbProvider.SelectedIndex; + wizardData.Length = int.Parse(cbLength.Text); + } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.resx b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.resx new file mode 100644 index 00000000..163164ed --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/KeysPage.resx @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Select a cryptographic service provider and a bit length. The bit length of the encryption key determines the +certificate's encryption strength. The greater the bit length, the stronger the security. However, a greater bit +length may decrease performance. + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.Designer.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.Designer.cs new file mode 100644 index 00000000..e6b4485e --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.Designer.cs @@ -0,0 +1,206 @@ +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class PropertiesPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.txtCommonName = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.txtOrganization = new System.Windows.Forms.TextBox(); + this.txtUnit = new System.Windows.Forms.TextBox(); + this.txtCity = new System.Windows.Forms.TextBox(); + this.txtState = new System.Windows.Forms.TextBox(); + this.cbCountry = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + this.label2.Location = new System.Drawing.Point(20, 69); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(80, 13); + this.label2.TabIndex = 5; + this.label2.Text = "Common name:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(20, 25); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(494, 26); + this.label3.TabIndex = 6; + this.label3.Text = "Specify the required information for the certificate. State/province and City/loc" + + "ality must be specified as \r\nofficial names and they cannot contain abbreviation" + + "s.\r\n"; + // + // txtCommonName + // + this.txtCommonName.Location = new System.Drawing.Point(180, 66); + this.txtCommonName.Name = "txtCommonName"; + this.txtCommonName.Size = new System.Drawing.Size(299, 20); + this.txtCommonName.TabIndex = 7; + this.txtCommonName.TextChanged += new System.EventHandler(this.txtCommonName_TextChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(20, 95); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(69, 13); + this.label1.TabIndex = 8; + this.label1.Text = "Organization:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(20, 121); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(97, 13); + this.label4.TabIndex = 9; + this.label4.Text = "Organizational unit:"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(20, 147); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(64, 13); + this.label5.TabIndex = 10; + this.label5.Text = "City/locality:"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(20, 173); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(81, 13); + this.label6.TabIndex = 11; + this.label6.Text = "State/province:"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(20, 199); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(80, 13); + this.label7.TabIndex = 12; + this.label7.Text = "Country/region:"; + // + // txtOrganization + // + this.txtOrganization.Location = new System.Drawing.Point(180, 92); + this.txtOrganization.Name = "txtOrganization"; + this.txtOrganization.Size = new System.Drawing.Size(299, 20); + this.txtOrganization.TabIndex = 13; + this.txtOrganization.TextChanged += new System.EventHandler(this.txtOrganization_TextChanged); + // + // txtUnit + // + this.txtUnit.Location = new System.Drawing.Point(180, 118); + this.txtUnit.Name = "txtUnit"; + this.txtUnit.Size = new System.Drawing.Size(94, 20); + this.txtUnit.TabIndex = 14; + this.txtUnit.TextChanged += new System.EventHandler(this.txtUnit_TextChanged); + // + // txtCity + // + this.txtCity.Location = new System.Drawing.Point(180, 144); + this.txtCity.Name = "txtCity"; + this.txtCity.Size = new System.Drawing.Size(154, 20); + this.txtCity.TabIndex = 15; + this.txtCity.TextChanged += new System.EventHandler(this.txtCity_TextChanged); + // + // txtState + // + this.txtState.Location = new System.Drawing.Point(180, 170); + this.txtState.Name = "txtState"; + this.txtState.Size = new System.Drawing.Size(154, 20); + this.txtState.TabIndex = 16; + this.txtState.TextChanged += new System.EventHandler(this.txtState_TextChanged); + // + // cbCountry + // + this.cbCountry.FormattingEnabled = true; + this.cbCountry.Location = new System.Drawing.Point(180, 196); + this.cbCountry.Name = "cbCountry"; + this.cbCountry.Size = new System.Drawing.Size(299, 21); + this.cbCountry.TabIndex = 17; + this.cbCountry.Text = "US"; + this.cbCountry.TextChanged += new System.EventHandler(this.cbCountry_TextChanged); + // + // PropertiesPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.cbCountry); + this.Controls.Add(this.txtState); + this.Controls.Add(this.txtCity); + this.Controls.Add(this.txtUnit); + this.Controls.Add(this.txtOrganization); + this.Controls.Add(this.label7); + this.Controls.Add(this.label6); + this.Controls.Add(this.label5); + this.Controls.Add(this.label4); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtCommonName); + this.Controls.Add(this.label2); + this.Controls.Add(this.label3); + this.Name = "PropertiesPage"; + this.Size = new System.Drawing.Size(670, 380); + this.Load += new System.EventHandler(this.PropertiesPage_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label2; + private Label label3; + private TextBox txtCommonName; + private Label label1; + private Label label4; + private Label label5; + private Label label6; + private Label label7; + private TextBox txtOrganization; + private TextBox txtUnit; + private TextBox txtCity; + private TextBox txtState; + private ComboBox cbCountry; + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.cs b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.cs new file mode 100644 index 00000000..e04ab9e3 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.cs @@ -0,0 +1,118 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Certificates.Wizards.CertificateRequestWizard +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Threading.Tasks; + + using Microsoft.Web.Management.Client.Win32; + + public partial class PropertiesPage : DefaultWizardPage + { + public PropertiesPage() + { + InitializeComponent(); + Caption = "Distinguished Name Properties"; + } + + protected override bool CanNavigateNext + { + get + { + return base.CanNavigateNext + && !string.IsNullOrWhiteSpace(txtCommonName.Text) + && !string.IsNullOrWhiteSpace(txtOrganization.Text) + && !string.IsNullOrWhiteSpace(txtUnit.Text) + && !string.IsNullOrWhiteSpace(txtCity.Text) + && !string.IsNullOrWhiteSpace(txtState.Text) + && !string.IsNullOrWhiteSpace(cbCountry.Text); + } + } + + private void txtCommonName_TextChanged(object sender, EventArgs e) + { + var wizardData = ((CertificateRequestWizardData)WizardData); + wizardData.CommonName = txtCommonName.Text; + UpdateWizard(); + } + + protected override void Activate() + { + base.Activate(); + txtCommonName.SelectAll(); + txtCommonName.Focus(); + } + + private void txtOrganization_TextChanged(object sender, EventArgs e) + { + var wizardData = ((CertificateRequestWizardData)WizardData); + wizardData.Organization = txtOrganization.Text; + UpdateWizard(); + } + + private void txtUnit_TextChanged(object sender, EventArgs e) + { + var wizardData = ((CertificateRequestWizardData)WizardData); + wizardData.Unit = txtUnit.Text; + UpdateWizard(); + } + + private void txtCity_TextChanged(object sender, EventArgs e) + { + var wizardData = ((CertificateRequestWizardData)WizardData); + wizardData.City = txtCity.Text; + UpdateWizard(); + } + + private void txtState_TextChanged(object sender, EventArgs e) + { + var wizardData = ((CertificateRequestWizardData)WizardData); + wizardData.State = txtState.Text; + UpdateWizard(); + } + + private void cbCountry_TextChanged(object sender, EventArgs e) + { + var wizardData = ((CertificateRequestWizardData)WizardData); + wizardData.Country = cbCountry.Text; + UpdateWizard(); + } + + private async void PropertiesPage_Load(object sender, EventArgs e) + { + var codes = await Task.Factory.StartNew(() => + { + var result = new HashSet(); + CultureInfo[] cinfo = CultureInfo.GetCultures(CultureTypes.AllCultures & ~CultureTypes.NeutralCultures); + foreach (CultureInfo cul in cinfo) + { + RegionInfo ri; + try + { + ri = new RegionInfo(cul.Name); + result.Add(ri.TwoLetterISORegionName); + } + catch + { + } + } + + var list = result.ToList(); + list.Sort(); + return list; + }); + + foreach (var code in codes) + { + cbCountry.Items.Add(code); + } + + cbCountry.Text = "US"; + } + } +} diff --git a/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.resx b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Certificates/Wizards/CertificateRequestWizard/PropertiesPage.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Certificates/packages.config b/JexusManager.Features.Certificates/packages.config new file mode 100644 index 00000000..789e25ea --- /dev/null +++ b/JexusManager.Features.Certificates/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Cgi/CgiFeature.cs b/JexusManager.Features.Cgi/CgiFeature.cs new file mode 100644 index 00000000..4f2c5a0a --- /dev/null +++ b/JexusManager.Features.Cgi/CgiFeature.cs @@ -0,0 +1,131 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Cgi +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Resources; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal class CgiFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly CgiFeature _owner; + + public FeatureTaskList(CgiFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + return new TaskItem[0]; + } + } + + public CgiFeature(Module module) + { + this.Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + private TaskList _taskList; + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (this.Module as IServiceProvider).GetService(type); + } + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/cgi"); + PropertyGridObject = new CgiItem(section); + this.OnCgiSettingsSaved(); + } + + public CgiItem PropertyGridObject { get; set; } + + protected void OnCgiSettingsSaved() + { + this.CgiSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210534"); + return false; + } + + public CgiSettingsSavedEventHandler CgiSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + public ServerManager Server { get; set; } + public Application Application { get; set; } + + public string Name + { + get { return "Cgi"; } + } + + public bool IsEnabled { get; set; } + + public void CancelChanges() + { + this.Load(); + } + + public bool ApplyChanges() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + PropertyGridObject.Apply(); + service.ServerManager.CommitChanges(); + return true; + } + + public bool LongDateEnabled { get; set; } + + public bool ExtensionEnabled { get; set; } + + public bool SizeEnabled { get; set; } + + public bool TimeEnabled { get; set; } + + public bool DateEnabled { get; set; } + } +} diff --git a/JexusManager.Features.Cgi/CgiItem.cs b/JexusManager.Features.Cgi/CgiItem.cs new file mode 100644 index 00000000..f717b7ae --- /dev/null +++ b/JexusManager.Features.Cgi/CgiItem.cs @@ -0,0 +1,52 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Cgi +{ + using System; + using System.ComponentModel; + + using Microsoft.Web.Administration; + + internal class CgiItem + { + public ConfigurationElement Element { get; set; } + + public CgiItem(ConfigurationElement element) + { + this.Element = element; + CreateCgiWithNewConsole = (bool)element["createCGIWithNewConsole"]; + CreateProcessAsUser = (bool)element["createProcessAsUser"]; + Timeout = (TimeSpan)element["timeout"]; + } + + public void Apply() + { + Element["createCGIWithNewConsole"] = CreateCgiWithNewConsole; + Element["createProcessAsUser"] = CreateProcessAsUser; + Element["timeout"] = Timeout; + } + + [Browsable(true)] + [Category("Behavior")] + [Description("Indicates whether a CGI application runs in its own console. If the value is set to true, each CGI application creates a new console when started. A value of false indicaes that CGI applications should run without a console.")] + [DisplayName("Use New Console For Each Invocation")] + [DefaultValue(false)] + public bool CreateCgiWithNewConsole { get; set; } + + [Browsable(true)] + [Category("Security")] + [Description("Specifies whether a CGI process is created in the system context or in the context of the requesting user.")] + [DisplayName("Impersonate User")] + [DefaultValue(true)] + public bool CreateProcessAsUser { get; set; } + + [Browsable(true)] + [Category("Behavior")] + [Description("Specifies the time-out value for CGI applications.")] + [DisplayName("Timeout (hh:mm:ss)")] + [DefaultValue(typeof(TimeSpan), "0:15:00")] + public TimeSpan Timeout { get; set; } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Cgi/CgiModule.cs b/JexusManager.Features.Cgi/CgiModule.cs new file mode 100644 index 00000000..1ffc7ea9 --- /dev/null +++ b/JexusManager.Features.Cgi/CgiModule.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Cgi +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class CgiModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo( + this, + typeof(CgiPage), + "CGI", + "Configure properties for CGI programs", + Resources.cgi_36, + Resources.cgi_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Cgi/CgiModuleProvider.cs b/JexusManager.Features.Cgi/CgiModuleProvider.cs new file mode 100644 index 00000000..a9713ba5 --- /dev/null +++ b/JexusManager.Features.Cgi/CgiModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Cgi +{ + using System; + + using Microsoft.Web.Management.Server; + + public class CgiModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(CgiModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Cgi/CgiPage.Designer.cs b/JexusManager.Features.Cgi/CgiPage.Designer.cs new file mode 100644 index 00000000..0fbfa0fc --- /dev/null +++ b/JexusManager.Features.Cgi/CgiPage.Designer.cs @@ -0,0 +1,243 @@ +namespace JexusManager.Features.Cgi +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class CgiPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.pgCGI = new System.Windows.Forms.PropertyGrid(); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel1 = new System.Windows.Forms.ToolStripLabel(); + this.toolStripComboBox1 = new System.Windows.Forms.ToolStripComboBox(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 5; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.pgCGI); + this.panel2.Controls.Add(this.toolStrip1); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 11; + // + // pgCGI + // + this.pgCGI.CategoryForeColor = System.Drawing.SystemColors.InactiveCaptionText; + this.pgCGI.Dock = System.Windows.Forms.DockStyle.Fill; + this.pgCGI.Location = new System.Drawing.Point(0, 25); + this.pgCGI.Name = "pgCGI"; + this.pgCGI.Size = new System.Drawing.Size(560, 515); + this.pgCGI.TabIndex = 1; + this.pgCGI.ToolbarVisible = false; + this.pgCGI.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.PgCgiPropertyValueChanged); + // + // toolStrip1 + // + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel1, + this.toolStripComboBox1}); + this.toolStrip1.Location = new System.Drawing.Point(0, 0); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.Size = new System.Drawing.Size(560, 25); + this.toolStrip1.TabIndex = 0; + this.toolStrip1.Text = "toolStrip1"; + // + // toolStripLabel1 + // + this.toolStripLabel1.Name = "toolStripLabel1"; + this.toolStripLabel1.Size = new System.Drawing.Size(48, 22); + this.toolStripLabel1.Text = "Display:"; + // + // toolStripComboBox1 + // + this.toolStripComboBox1.Items.AddRange(new object[] { + "Friendly Names", + "Configuration Names", + "Both Names"}); + this.toolStripComboBox1.Name = "toolStripComboBox1"; + this.toolStripComboBox1.Size = new System.Drawing.Size(121, 25); + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(48, 25); + this.label3.TabIndex = 2; + this.label3.Text = "CGI"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // CgiPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "CgiPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private PropertyGrid pgCGI; + private ToolStrip toolStrip1; + private ToolStripLabel toolStripLabel1; + private ToolStripComboBox toolStripComboBox1; + } +} diff --git a/JexusManager.Features.Cgi/CgiPage.cs b/JexusManager.Features.Cgi/CgiPage.cs new file mode 100644 index 00000000..77698e06 --- /dev/null +++ b/JexusManager.Features.Cgi/CgiPage.cs @@ -0,0 +1,160 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Cgi +{ + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + using Microsoft.Web.Management.Server; + + internal partial class CgiPage : ModulePropertiesPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly CgiPage _owner; + + public PageTaskList(CgiPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private CgiFeature _feature; + private bool _hasChanges; + private bool _initialized; + private TaskList _taskList; + + public CgiPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new CgiFeature(this.Module); + _feature.CgiSettingsUpdated = this.Refresh; + _feature.Load(); + base.Initialize(navigationData); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override bool ApplyChanges() + { + if (!_feature.ApplyChanges()) + { + return false; + } + + this.ClearChanges(); + return true; + } + + protected override void CancelChanges() + { + _feature.CancelChanges(); + this.ClearChanges(); + } + + protected override bool HasChanges + { + get { return _hasChanges; } + } + + protected override bool CanApplyChanges + { + get { return true; } + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + this.Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + this.Refresh(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + protected override void OnRefresh() + { + if (!_hasChanges) + { + pgCGI.SelectedObject = _feature.PropertyGridObject; + _initialized = true; + } + + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + } + + private void PgCgiPropertyValueChanged(object s, PropertyValueChangedEventArgs e) + { + InformChanges(); + } + + protected override PropertyBag GetProperties() + { + return null; + } + + protected override PropertyBag UpdateProperties(out bool updateSuccessful) + { + updateSuccessful = false; + return null; + } + + protected override void ProcessProperties(PropertyBag properties) + { + } + } +} diff --git a/JexusManager.Features.Cgi/CgiPage.resx b/JexusManager.Features.Cgi/CgiPage.resx new file mode 100644 index 00000000..85df29e1 --- /dev/null +++ b/JexusManager.Features.Cgi/CgiPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 281, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Cgi/CgiSettingsSavedEventHandler.cs b/JexusManager.Features.Cgi/CgiSettingsSavedEventHandler.cs new file mode 100644 index 00000000..31789ef9 --- /dev/null +++ b/JexusManager.Features.Cgi/CgiSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Cgi +{ + public delegate void CgiSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Cgi/JexusManager.Features.Cgi.csproj b/JexusManager.Features.Cgi/JexusManager.Features.Cgi.csproj new file mode 100644 index 00000000..cc2c439c --- /dev/null +++ b/JexusManager.Features.Cgi/JexusManager.Features.Cgi.csproj @@ -0,0 +1,104 @@ + + + + + Debug + AnyCPU + {0ADF28C5-377F-4AA3-B732-AA8C8AEEBB16} + Library + Properties + JexusManager.Features.Cgi + JexusManager.Features.Cgi + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + + + + + + + + + + + + + + UserControl + + + CgiPage.cs + + + + + True + True + Resources.resx + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + CgiPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Cgi/JexusManager.snk b/JexusManager.Features.Cgi/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Cgi/JexusManager.snk differ diff --git a/JexusManager.Features.Cgi/Properties/AssemblyInfo.cs b/JexusManager.Features.Cgi/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..6c249dae --- /dev/null +++ b/JexusManager.Features.Cgi/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Cgi")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Cgi")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("0adf28c5-377f-4aa3-b732-aa8c8aeebb16")] + +// 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")] diff --git a/JexusManager.Features.Cgi/Properties/Resources.Designer.cs b/JexusManager.Features.Cgi/Properties/Resources.Designer.cs new file mode 100644 index 00000000..55248572 --- /dev/null +++ b/JexusManager.Features.Cgi/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Cgi.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Cgi.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap cgi_36 { + get { + object obj = ResourceManager.GetObject("cgi_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Cgi/Properties/Resources.resx b/JexusManager.Features.Cgi/Properties/Resources.resx new file mode 100644 index 00000000..5e320a7a --- /dev/null +++ b/JexusManager.Features.Cgi/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\cgi_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Cgi/Resources/cgi_36.png b/JexusManager.Features.Cgi/Resources/cgi_36.png new file mode 100644 index 00000000..7812d7e6 Binary files /dev/null and b/JexusManager.Features.Cgi/Resources/cgi_36.png differ diff --git a/JexusManager.Features.Compression/CompressionFeature.cs b/JexusManager.Features.Compression/CompressionFeature.cs new file mode 100644 index 00000000..2cb1dbc0 --- /dev/null +++ b/JexusManager.Features.Compression/CompressionFeature.cs @@ -0,0 +1,132 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Compression +{ + using System; + using System.Diagnostics; + using System.Resources; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal class CompressionFeature + { + public CompressionFeature(Module module) + { + Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (Module as IServiceProvider).GetService(type); + } + + public void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var urlCompressionSection = service.GetSection("system.webServer/urlCompression"); + StaticEnabled = (bool)urlCompressionSection["doStaticCompression"]; + DynamicEnabled = (bool)urlCompressionSection["doDynamicCompression"]; + if (service.Server != null) + { + var httpCompressionSection = service.GetSection("system.webServer/httpCompression"); + DoDiskSpaceLimiting = (bool)httpCompressionSection["doDiskSpaceLimiting"]; + MaxDiskSpaceUsage = (uint)httpCompressionSection["maxDiskSpaceUsage"]; + Directory = httpCompressionSection["directory"].ToString(); + MinFileSizeForComp = httpCompressionSection["minFileSizeForComp"].ToString(); + DoFileSize = MinFileSizeForComp != "0"; + } + + OnCompressionSettingsSaved(); + } + + public string Directory { get; set; } + + protected void OnCompressionSettingsSaved() + { + CompressionSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210466"); + return false; + } + + public CompressionSettingsSavedEventHandler CompressionSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + public string Name + { + get + { + return "Compression"; + } + } + + public string MinFileSizeForComp { get; set; } + public uint MaxDiskSpaceUsage { get; set; } + public bool DoDiskSpaceLimiting { get; set; } + public bool DynamicEnabled { get; set; } + public bool StaticEnabled { get; set; } + + public string FileSize + { + get { return DoFileSize ? MinFileSizeForComp : "0"; } + } + + public bool DoFileSize { get; set; } + + public void CancelChanges() + { + Load(); + } + + public bool ApplyChanges() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var urlCompressionSection = service.GetSection("system.webServer/urlCompression"); + urlCompressionSection["doStaticCompression"] = StaticEnabled; + urlCompressionSection["doDynamicCompression"] = DynamicEnabled; + + if (service.Server != null) + { + var httpCompressionSection = service.GetSection("system.webServer/httpCompression"); + httpCompressionSection["doDiskSpaceLimiting"] = DoDiskSpaceLimiting; + httpCompressionSection["maxDiskSpaceUsage"] = MaxDiskSpaceUsage; + httpCompressionSection["directory"] = Directory; + httpCompressionSection["minFileSizeForComp"] = MinFileSizeForComp; + } + + AsyncHelper.RunSync(() => service.ServerManager.CommitChangesAsync()); + return true; + } + } +} diff --git a/JexusManager.Features.Compression/CompressionModule.cs b/JexusManager.Features.Compression/CompressionModule.cs new file mode 100644 index 00000000..e4673674 --- /dev/null +++ b/JexusManager.Features.Compression/CompressionModule.cs @@ -0,0 +1,25 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Compression +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class CompressionModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(CompressionPage), "Compression", + "Configure settings to compress responses", + Resources.compression_36, Resources.compression_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Compression/CompressionModuleProvider.cs b/JexusManager.Features.Compression/CompressionModuleProvider.cs new file mode 100644 index 00000000..8c5daef3 --- /dev/null +++ b/JexusManager.Features.Compression/CompressionModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Compression +{ + using System; + + using Microsoft.Web.Management.Server; + + public class CompressionModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(CompressionModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Compression/CompressionPage.Designer.cs b/JexusManager.Features.Compression/CompressionPage.Designer.cs new file mode 100644 index 00000000..030af0c4 --- /dev/null +++ b/JexusManager.Features.Compression/CompressionPage.Designer.cs @@ -0,0 +1,338 @@ +namespace JexusManager.Features.Compression +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class CompressionPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.gbStatic = new System.Windows.Forms.GroupBox(); + this.txtDiskspaceLimit = new System.Windows.Forms.TextBox(); + this.cbDiskspaceLimit = new System.Windows.Forms.CheckBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.txtFileSize = new System.Windows.Forms.TextBox(); + this.cbFileSize = new System.Windows.Forms.CheckBox(); + this.cbDynamic = new System.Windows.Forms.CheckBox(); + this.cbStatic = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.gbStatic.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 5; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitContainer1_SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.gbStatic); + this.panel2.Controls.Add(this.cbDynamic); + this.panel2.Controls.Add(this.cbStatic); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 11; + // + // gbStatic + // + this.gbStatic.Controls.Add(this.txtDiskspaceLimit); + this.gbStatic.Controls.Add(this.cbDiskspaceLimit); + this.gbStatic.Controls.Add(this.btnBrowse); + this.gbStatic.Controls.Add(this.txtPath); + this.gbStatic.Controls.Add(this.label4); + this.gbStatic.Controls.Add(this.txtFileSize); + this.gbStatic.Controls.Add(this.cbFileSize); + this.gbStatic.Location = new System.Drawing.Point(16, 80); + this.gbStatic.Name = "gbStatic"; + this.gbStatic.Size = new System.Drawing.Size(450, 210); + this.gbStatic.TabIndex = 5; + this.gbStatic.TabStop = false; + this.gbStatic.Text = "Static Compression"; + // + // txtDiskspaceLimit + // + this.txtDiskspaceLimit.Location = new System.Drawing.Point(27, 172); + this.txtDiskspaceLimit.Name = "txtDiskspaceLimit"; + this.txtDiskspaceLimit.Size = new System.Drawing.Size(110, 20); + this.txtDiskspaceLimit.TabIndex = 6; + this.txtDiskspaceLimit.TextChanged += new System.EventHandler(this.cbStatic_CheckedChanged); + // + // cbDiskspaceLimit + // + this.cbDiskspaceLimit.AutoSize = true; + this.cbDiskspaceLimit.Location = new System.Drawing.Point(6, 142); + this.cbDiskspaceLimit.Name = "cbDiskspaceLimit"; + this.cbDiskspaceLimit.Size = new System.Drawing.Size(232, 17); + this.cbDiskspaceLimit.TabIndex = 5; + this.cbDiskspaceLimit.Text = "Per application pool disk space limit (in MB):"; + this.cbDiskspaceLimit.UseVisualStyleBackColor = true; + this.cbDiskspaceLimit.CheckedChanged += new System.EventHandler(this.cbDiskspaceLimit_CheckedChanged); + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(387, 107); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(30, 23); + this.btnBrowse.TabIndex = 4; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click); + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(6, 109); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(375, 20); + this.txtPath.TabIndex = 3; + this.txtPath.TextChanged += new System.EventHandler(this.cbStatic_CheckedChanged); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(6, 82); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(84, 13); + this.label4.TabIndex = 2; + this.label4.Text = "Cache directory:"; + // + // txtFileSize + // + this.txtFileSize.Location = new System.Drawing.Point(27, 46); + this.txtFileSize.Name = "txtFileSize"; + this.txtFileSize.Size = new System.Drawing.Size(110, 20); + this.txtFileSize.TabIndex = 1; + this.txtFileSize.TextChanged += new System.EventHandler(this.txtFileSize_TextChanged); + // + // cbFileSize + // + this.cbFileSize.AutoSize = true; + this.cbFileSize.Location = new System.Drawing.Point(6, 19); + this.cbFileSize.Name = "cbFileSize"; + this.cbFileSize.Size = new System.Drawing.Size(217, 17); + this.cbFileSize.TabIndex = 0; + this.cbFileSize.Text = "Only compress files larger than (in bytes):"; + this.cbFileSize.UseVisualStyleBackColor = true; + this.cbFileSize.CheckedChanged += new System.EventHandler(this.cbFileSize_CheckedChanged); + // + // cbDynamic + // + this.cbDynamic.AutoSize = true; + this.cbDynamic.Location = new System.Drawing.Point(16, 21); + this.cbDynamic.Name = "cbDynamic"; + this.cbDynamic.Size = new System.Drawing.Size(202, 17); + this.cbDynamic.TabIndex = 4; + this.cbDynamic.Text = "Enable dynamic content compression"; + this.cbDynamic.UseVisualStyleBackColor = true; + this.cbDynamic.CheckedChanged += new System.EventHandler(this.cbDynamic_CheckedChanged); + // + // cbStatic + // + this.cbStatic.AutoSize = true; + this.cbStatic.Location = new System.Drawing.Point(16, 44); + this.cbStatic.Name = "cbStatic"; + this.cbStatic.Size = new System.Drawing.Size(188, 17); + this.cbStatic.TabIndex = 3; + this.cbStatic.Text = "Enable static content compression"; + this.cbStatic.UseVisualStyleBackColor = true; + this.cbStatic.CheckedChanged += new System.EventHandler(this.cbStatic_CheckedChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(818, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure settings for compression of responses. This can imp" + + "rove the perceived performance of a website greatly and reduce bandwidth-related" + + " charges."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(138, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Compression"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // CompressionPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "CompressionPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.gbStatic.ResumeLayout(false); + this.gbStatic.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private CheckBox cbStatic; + private Panel panel2; + private PictureBox pictureBox1; + private CheckBox cbDynamic; + private GroupBox gbStatic; + private TextBox txtDiskspaceLimit; + private CheckBox cbDiskspaceLimit; + private Button btnBrowse; + private TextBox txtPath; + private Label label4; + private TextBox txtFileSize; + private CheckBox cbFileSize; + private ContextMenuStrip cmsActionPanel; + } +} diff --git a/JexusManager.Features.Compression/CompressionPage.cs b/JexusManager.Features.Compression/CompressionPage.cs new file mode 100644 index 00000000..57b435f3 --- /dev/null +++ b/JexusManager.Features.Compression/CompressionPage.cs @@ -0,0 +1,190 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Compression +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + using Microsoft.Web.Management.Server; + + internal partial class CompressionPage : ModuleDialogPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly CompressionPage _owner; + + public PageTaskList(CompressionPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private CompressionFeature _feature; + private bool _hasChanges; + private bool _initialized; + private TaskList _taskList; + + public CompressionPage() + { + InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new CompressionFeature(Module); + _feature.CompressionSettingsUpdated = Refresh; + _feature.Load(); + base.Initialize(navigationData); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void cbStatic_CheckedChanged(object sender, EventArgs e) + { + _feature.StaticEnabled = cbStatic.Checked; + InformChanges(); + } + + private void cbDynamic_CheckedChanged(object sender, EventArgs e) + { + _feature.DynamicEnabled = cbDynamic.Checked; + InformChanges(); + } + + private void btnBrowse_Click(object sender, EventArgs e) + { + DialogHelper.ShowBrowseDialog(txtPath); + _feature.Directory = txtPath.Text; + InformChanges(); + } + + private void cbFileSize_CheckedChanged(object sender, EventArgs e) + { + _feature.DoFileSize = cbFileSize.Checked; + InformChanges(); + } + + private void cbDiskspaceLimit_CheckedChanged(object sender, EventArgs e) + { + _feature.DoDiskSpaceLimiting = cbDiskspaceLimit.Checked; + InformChanges(); + } + + private void txtFileSize_TextChanged(object sender, EventArgs e) + { + _feature.MinFileSizeForComp = txtFileSize.Text; + InformChanges(); + } + + protected override bool ApplyChanges() + { + if (!_feature.ApplyChanges()) + { + return false; + } + + ClearChanges(); + return true; + } + + protected override void CancelChanges() + { + _feature.CancelChanges(); + ClearChanges(); + } + + protected override bool HasChanges + { + get { return _hasChanges; } + } + + protected override bool CanApplyChanges + { + get { return true; } + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + Refresh(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + protected override void OnRefresh() + { + if (!_hasChanges) + { + cbDynamic.Checked = _feature.DynamicEnabled; + cbStatic.Checked = _feature.StaticEnabled; + + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + gbStatic.Visible = service.Scope == ManagementScope.Server; + if (service.Scope == ManagementScope.Server) + { + txtDiskspaceLimit.Enabled = cbDiskspaceLimit.Checked = _feature.DoDiskSpaceLimiting; + txtDiskspaceLimit.Text = _feature.MaxDiskSpaceUsage.ToString(); + txtPath.Text = _feature.Directory; + txtFileSize.Text = _feature.FileSize; + txtFileSize.Enabled = cbFileSize.Checked = _feature.DoFileSize; + } + + _initialized = true; + } + + Tasks.Fill(tsActionPanel, cmsActionPanel); + } + } +} diff --git a/JexusManager.Features.Compression/CompressionPage.resx b/JexusManager.Features.Compression/CompressionPage.resx new file mode 100644 index 00000000..950be5d2 --- /dev/null +++ b/JexusManager.Features.Compression/CompressionPage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Compression/CompressionSettingsSavedEventHandler.cs b/JexusManager.Features.Compression/CompressionSettingsSavedEventHandler.cs new file mode 100644 index 00000000..9e5e1ba3 --- /dev/null +++ b/JexusManager.Features.Compression/CompressionSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Compression +{ + public delegate void CompressionSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Compression/JexusManager.Features.Compression.csproj b/JexusManager.Features.Compression/JexusManager.Features.Compression.csproj new file mode 100644 index 00000000..716153c3 --- /dev/null +++ b/JexusManager.Features.Compression/JexusManager.Features.Compression.csproj @@ -0,0 +1,103 @@ + + + + + Debug + AnyCPU + {CD9A8535-A6B1-46BB-8ACB-73D5E393147E} + Library + Properties + JexusManager.Features.Compression + JexusManager.Features.Compression + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + + + + + + + + + + + + + UserControl + + + CompressionPage.cs + + + + + True + True + Resources.resx + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + CompressionPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Compression/JexusManager.snk b/JexusManager.Features.Compression/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Compression/JexusManager.snk differ diff --git a/JexusManager.Features.Compression/Properties/AssemblyInfo.cs b/JexusManager.Features.Compression/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f8c5d9d3 --- /dev/null +++ b/JexusManager.Features.Compression/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Compression")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Compression")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("cd9a8535-a6b1-46bb-8acb-73d5e393147e")] + +// 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")] diff --git a/JexusManager.Features.Compression/Properties/Resources.Designer.cs b/JexusManager.Features.Compression/Properties/Resources.Designer.cs new file mode 100644 index 00000000..d30cbb62 --- /dev/null +++ b/JexusManager.Features.Compression/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Compression.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Compression.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap compression_36 { + get { + object obj = ResourceManager.GetObject("compression_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Compression/Properties/Resources.resx b/JexusManager.Features.Compression/Properties/Resources.resx new file mode 100644 index 00000000..7603ddfe --- /dev/null +++ b/JexusManager.Features.Compression/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\compression_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Compression/Resources/compression_36.png b/JexusManager.Features.Compression/Resources/compression_36.png new file mode 100644 index 00000000..49a0e3de Binary files /dev/null and b/JexusManager.Features.Compression/Resources/compression_36.png differ diff --git a/JexusManager.Features.DefaultDocument/DefaultDocumentFeature.cs b/JexusManager.Features.DefaultDocument/DefaultDocumentFeature.cs new file mode 100644 index 00000000..a49b2a10 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DefaultDocumentFeature.cs @@ -0,0 +1,289 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.DefaultDocument +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Linq; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + public class DefaultDocumentFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly DefaultDocumentFeature _owner; + + public FeatureTaskList(DefaultDocumentFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + result.Add(GetMoveUpTaskItem(_owner.CanMoveUp)); + result.Add(GetMoveDownTaskItem(_owner.CanMoveDown)); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + if (_owner.CanRevert) + { + result.Add(new MethodTaskItem("Revert", "Revert To Parent", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public override void MoveUp() + { + _owner.MoveUp(); + } + + [Obfuscation(Exclude = true)] + public override void MoveDown() + { + _owner.MoveDown(); + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void Revert() + { + _owner.Revert(); + } + } + + public DefaultDocumentFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/defaultDocument"); + var enabled = (bool)section["enabled"]; + CanRevert = section.CanRevert(); + SetEnabled(enabled); + + LoadItems(); + } + + public void Add() + { + var dialog = new NewDefaultDocumentDialog(Module); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var index = this.Items.FindIndex(item => item.Flag == "Local"); + this.InsertItem(index == -1 ? 0 : index, dialog.Item); + } + + public void Remove() + { + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + var result = + service.ShowMessage( + "Are you sure that you want to remove the selected default document?", + "Confirm Remove", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void MoveUp() + { + if (Items.Any(item => item.Flag != "Local")) + { + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + var result = + service.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + this.MoveUpItem(); + } + + public void MoveDown() + { + if (Items.Any(item => item.Flag != "Local")) + { + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + var result = + service.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + this.MoveDownItem(); + } + + public void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/defaultDocument"); + section["enabled"] = true; + SetEnabled(true); + service.ServerManager.CommitChanges(); + } + + public void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/defaultDocument"); + section["enabled"] = false; + SetEnabled(false); + service.ServerManager.CommitChanges(); + } + + public void Revert() + { + if (!CanRevert) + { + throw new InvalidOperationException("Revert operation cannot be done at server level"); + } + + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + var result = + service.ShowMessage( + "Reverting to the parent configuration will result in the loss of all settings in the local configuration file for this feature. Are you sure you want to continue?", + Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + this.RevertItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + var section = service.GetSection("system.webServer/defaultDocument"); + return section.GetCollection("files"); + } + + protected override void OnSettingsSaved() + { + DefaultDocumentSettingsUpdated?.Invoke(); + } + + public void SetEnabled(bool enabled) + { + IsEnabled = enabled; + this.OnSettingsSaved(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210478"); + return false; + } + + public DefaultDocumentSettingsSavedEventHandler DefaultDocumentSettingsUpdated { get; set; } + public string Description { get; } + public bool IsEnabled { get; private set; } + public bool CanRevert { get; private set; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "Default Document"; } + } + } +} diff --git a/JexusManager.Features.DefaultDocument/DefaultDocumentModule.cs b/JexusManager.Features.DefaultDocument/DefaultDocumentModule.cs new file mode 100644 index 00000000..c6e2300b --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DefaultDocumentModule.cs @@ -0,0 +1,23 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DefaultDocument +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + public class DefaultDocumentModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(DefaultDocumentPage), "Default Document", "Configure default files to return when clients do not specify a file in a request", Resources.default_document_36, Resources.default_document_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.DefaultDocument/DefaultDocumentModuleProvider.cs b/JexusManager.Features.DefaultDocument/DefaultDocumentModuleProvider.cs new file mode 100644 index 00000000..fbda0945 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DefaultDocumentModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DefaultDocument +{ + using System; + + using Microsoft.Web.Management.Server; + + public class DefaultDocumentModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(DefaultDocumentModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.DefaultDocument/DefaultDocumentPage.Designer.cs b/JexusManager.Features.DefaultDocument/DefaultDocumentPage.Designer.cs new file mode 100644 index 00000000..ad4fbd70 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DefaultDocumentPage.Designer.cs @@ -0,0 +1,385 @@ +namespace JexusManager.Features.DefaultDocument +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class DefaultDocumentPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbFilter = new System.Windows.Forms.ToolStripComboBox(); + this.btnGo = new System.Windows.Forms.ToolStripSplitButton(); + this.protocolToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.btnShowAll = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.btnView = new System.Windows.Forms.ToolStripSplitButton(); + this.detailsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.iconsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.tilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.listToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 3; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitContainer1_SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 9; + this.pictureBox1.TabStop = false; + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 8; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 200; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 80; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbFilter, + this.btnGo, + this.btnShowAll, + this.toolStripSeparator4, + this.toolStripLabel3, + this.cbGroup, + this.btnView}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(36, 22); + this.toolStripLabel2.Text = "Filter:"; + // + // cbFilter + // + this.cbFilter.Name = "cbFilter"; + this.cbFilter.Size = new System.Drawing.Size(121, 25); + // + // btnGo + // + this.btnGo.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.protocolToolStripMenuItem}); + this.btnGo.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnGo.Name = "btnGo"; + this.btnGo.Size = new System.Drawing.Size(54, 22); + this.btnGo.Text = "Go"; + this.btnGo.ToolTipText = "Go"; + // + // protocolToolStripMenuItem + // + this.protocolToolStripMenuItem.Name = "protocolToolStripMenuItem"; + this.protocolToolStripMenuItem.Size = new System.Drawing.Size(88, 22); + this.protocolToolStripMenuItem.Text = "All"; + // + // btnShowAll + // + this.btnShowAll.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnShowAll.Name = "btnShowAll"; + this.btnShowAll.Size = new System.Drawing.Size(73, 22); + this.btnShowAll.Text = "Show All"; + // + // toolStripSeparator4 + // + this.toolStripSeparator4.Name = "toolStripSeparator4"; + this.toolStripSeparator4.Size = new System.Drawing.Size(6, 25); + // + // toolStripLabel3 + // + this.toolStripLabel3.Name = "toolStripLabel3"; + this.toolStripLabel3.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel3.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Category", + "Area"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // btnView + // + this.btnView.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnView.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.detailsToolStripMenuItem, + this.iconsToolStripMenuItem, + this.tilesToolStripMenuItem, + this.listToolStripMenuItem}); + this.btnView.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnView.Name = "btnView"; + this.btnView.Size = new System.Drawing.Size(32, 22); + this.btnView.Text = "toolStripButton1"; + // + // detailsToolStripMenuItem + // + this.detailsToolStripMenuItem.Name = "detailsToolStripMenuItem"; + this.detailsToolStripMenuItem.Size = new System.Drawing.Size(109, 22); + this.detailsToolStripMenuItem.Text = "Details"; + // + // iconsToolStripMenuItem + // + this.iconsToolStripMenuItem.Name = "iconsToolStripMenuItem"; + this.iconsToolStripMenuItem.Size = new System.Drawing.Size(109, 22); + this.iconsToolStripMenuItem.Text = "Icons"; + // + // tilesToolStripMenuItem + // + this.tilesToolStripMenuItem.Name = "tilesToolStripMenuItem"; + this.tilesToolStripMenuItem.Size = new System.Drawing.Size(109, 22); + this.tilesToolStripMenuItem.Text = "Tiles"; + // + // listToolStripMenuItem + // + this.listToolStripMenuItem.Name = "listToolStripMenuItem"; + this.listToolStripMenuItem.Size = new System.Drawing.Size(109, 22); + this.listToolStripMenuItem.Text = "List"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(666, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to specify the default files(s) to return when a client does not" + + " request a specific file. Set default documents in order of priority."; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(183, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Default Document"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // DefaultDocumentPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "DefaultDocumentPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chName; + private ColumnHeader chType; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Label label2; + private Label label3; + private PictureBox pictureBox1; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbFilter; + private ToolStripSplitButton btnGo; + private ToolStripMenuItem protocolToolStripMenuItem; + private ToolStripButton btnShowAll; + private ToolStripSeparator toolStripSeparator4; + private ToolStripLabel toolStripLabel3; + private ToolStripComboBox cbGroup; + private ToolStripSplitButton btnView; + private ToolStripMenuItem detailsToolStripMenuItem; + private ToolStripMenuItem iconsToolStripMenuItem; + private ToolStripMenuItem tilesToolStripMenuItem; + private ToolStripMenuItem listToolStripMenuItem; + private ContextMenuStrip cmsActionPanel; + } +} diff --git a/JexusManager.Features.DefaultDocument/DefaultDocumentPage.cs b/JexusManager.Features.DefaultDocument/DefaultDocumentPage.cs new file mode 100644 index 00000000..e3e2bfe7 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DefaultDocumentPage.cs @@ -0,0 +1,137 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DefaultDocument +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class DefaultDocumentPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly DefaultDocumentPage _owner; + + public PageTaskList(DefaultDocumentPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class DefaultDocumentListViewItem : ListViewItem + { + public DocumentItem Item { get; } + private readonly DefaultDocumentPage _page; + + public DefaultDocumentListViewItem(DocumentItem item, DefaultDocumentPage page) + : base(item.Name) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private DefaultDocumentFeature _feature; + private PageTaskList _taskList; + + public DefaultDocumentPage() + { + InitializeComponent(); + btnView.Image = DefaultTaskList.ViewImage; + btnGo.Image = DefaultTaskList.GoImage; + btnShowAll.Image = DefaultTaskList.ShowAllImage; + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new DefaultDocumentFeature(Module); + _feature.DefaultDocumentSettingsUpdated = InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new DefaultDocumentListViewItem(file, this)); + } + + if (_feature.SelectedItem != null) + { + foreach (DefaultDocumentListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + Refresh(); + } + + protected override void Refresh() + { + Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + // TODO: use WatchDog to suppress extra refresh (one selection triggers this event twice) + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((DefaultDocumentListViewItem)listView1.SelectedItems[0]).Item + : null; + Refresh(); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.DefaultDocument/DefaultDocumentPage.resx b/JexusManager.Features.DefaultDocument/DefaultDocumentPage.resx new file mode 100644 index 00000000..5408911a --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DefaultDocumentPage.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 807, 17 + + + 702, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.DefaultDocument/DefaultDocumentSettingsSavedEventHandler.cs b/JexusManager.Features.DefaultDocument/DefaultDocumentSettingsSavedEventHandler.cs new file mode 100644 index 00000000..fef86a2f --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DefaultDocumentSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DefaultDocument +{ + public delegate void DefaultDocumentSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.DefaultDocument/DocumentItem.cs b/JexusManager.Features.DefaultDocument/DocumentItem.cs new file mode 100644 index 00000000..f939c36a --- /dev/null +++ b/JexusManager.Features.DefaultDocument/DocumentItem.cs @@ -0,0 +1,42 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DefaultDocument +{ + using Microsoft.Web.Administration; + + public class DocumentItem : IItem + { + public ConfigurationElement Element { get; set; } + public string Name { get; set; } + public string Flag { get; set; } + + public DocumentItem(ConfigurationElement element) + { + Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + Element = element; + if (element == null) + { + return; + } + + Name = (string)element["value"]; + } + + public bool Equals(DocumentItem other) + { + return Match(other); + } + + public void Apply() + { + Element["value"] = Name; + } + + public bool Match(DocumentItem other) + { + return other != null && other.Name == Name; + } + } +} diff --git a/JexusManager.Features.DefaultDocument/JexusManager.Features.DefaultDocument.csproj b/JexusManager.Features.DefaultDocument/JexusManager.Features.DefaultDocument.csproj new file mode 100644 index 00000000..e53a9361 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/JexusManager.Features.DefaultDocument.csproj @@ -0,0 +1,130 @@ + + + + + Debug + AnyCPU + {1E54F67A-3538-4F8D-B2EE-5DA6A821A528} + Library + Properties + JexusManager.Features.DefaultDocument + JexusManager.Features.DefaultDocument + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + UserControl + + + DefaultDocumentPage.cs + + + + + Form + + + NewDefaultDocumentDialog.cs + + + + True + True + Resources.resx + + + + + DefaultDocumentPage.cs + + + NewDefaultDocumentDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.DefaultDocument/JexusManager.snk b/JexusManager.Features.DefaultDocument/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.DefaultDocument/JexusManager.snk differ diff --git a/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.Designer.cs b/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.Designer.cs new file mode 100644 index 00000000..d8ce5f6e --- /dev/null +++ b/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.Designer.cs @@ -0,0 +1,108 @@ +namespace JexusManager.Features.DefaultDocument +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class NewDefaultDocumentDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(242, 91); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(141, 91); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(17, 29); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 2; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(14, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(38, 13); + this.label1.TabIndex = 3; + this.label1.Text = "Name:"; + // + // NewDefaultDocumentDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 126); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.HelpButton = true; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "NewDefaultDocumentDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Add Default Document"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewDefaultDocumentDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private TextBox txtName; + private Label label1; + } +} diff --git a/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.cs b/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.cs new file mode 100644 index 00000000..fc2621de --- /dev/null +++ b/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.cs @@ -0,0 +1,62 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DefaultDocument +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + public partial class NewDefaultDocumentDialog : DialogForm + { + public NewDefaultDocumentDialog(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + var invalid = "*".ToCharArray(); + foreach (var ch in invalid) + { + if (txtName.Text.Contains(ch)) + { + MessageBox.Show(string.Format("The specific default document contains the following invalid character: {0}.", ch), Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + } + + Item = new DocumentItem(null); + Item.Name = txtName.Text; + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + } + + public DocumentItem Item { get; set; } + + private void NewDefaultDocumentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210478"); + } + } +} diff --git a/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.resx b/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/NewDefaultDocumentDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.DefaultDocument/Properties/AssemblyInfo.cs b/JexusManager.Features.DefaultDocument/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..aa9e597e --- /dev/null +++ b/JexusManager.Features.DefaultDocument/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.DefaultDocument")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.DefaultDocument")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("1e54f67a-3538-4f8d-b2ee-5da6a821a528")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.DefaultDocument/Properties/Resources.Designer.cs b/JexusManager.Features.DefaultDocument/Properties/Resources.Designer.cs new file mode 100644 index 00000000..c1b98093 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.DefaultDocument.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.DefaultDocument.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap default_document_36 { + get { + object obj = ResourceManager.GetObject("default_document_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.DefaultDocument/Properties/Resources.resx b/JexusManager.Features.DefaultDocument/Properties/Resources.resx new file mode 100644 index 00000000..db6c7fcc --- /dev/null +++ b/JexusManager.Features.DefaultDocument/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\default_document_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.DefaultDocument/Resources/default_document_36.png b/JexusManager.Features.DefaultDocument/Resources/default_document_36.png new file mode 100644 index 00000000..afb3741a Binary files /dev/null and b/JexusManager.Features.DefaultDocument/Resources/default_document_36.png differ diff --git a/JexusManager.Features.DefaultDocument/packages.config b/JexusManager.Features.DefaultDocument/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.DefaultDocument/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.DirectoryBrowse/DirectoryBrowseFeature.cs b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseFeature.cs new file mode 100644 index 00000000..b1bbdfc7 --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseFeature.cs @@ -0,0 +1,209 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DirectoryBrowse +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Resources; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class DirectoryBrowseFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly DirectoryBrowseFeature _owner; + + public FeatureTaskList(DirectoryBrowseFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + } + + public DirectoryBrowseFeature(Module module) + { + this.Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + private TaskList _taskList; + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (this.Module as IServiceProvider).GetService(type); + } + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/directoryBrowse"); + var enabled = (bool)section["enabled"]; + var flags = (long)section["showFlags"]; + TimeEnabled = (flags & 4) == 4; + SizeEnabled = (flags & 8) == 8; + ExtensionEnabled = (flags & 16) == 16; + DateEnabled = (flags & 2) == 2; + LongDateEnabled = (flags & 32) == 32; + this.SetEnabled(enabled); + } + + private void Enable() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationSection section = service.GetSection("system.webServer/directoryBrowse"); + section["enabled"] = true; + this.SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationSection section = service.GetSection("system.webServer/directoryBrowse"); + section["enabled"] = false; + this.SetEnabled(false); + } + + public void SetEnabled(bool enabled) + { + this.IsEnabled = enabled; + this.OnDirectoryBrowseSettingsSaved(); + } + + protected void OnDirectoryBrowseSettingsSaved() + { + this.DirectoryBrowseSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210534"); + return false; + } + + public DirectoryBrowseSettingsSavedEventHandler DirectoryBrowseSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + + public string Name + { + get { return "Directory Browsing"; } + } + + public bool IsEnabled { get; set; } + + public void CancelChanges() + { + this.Load(); + } + + public bool ApplyChanges() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/directoryBrowse"); + section["enabled"] = this.IsEnabled; + long flags = 0; + if (DateEnabled) + { + flags |= 2; + } + + if (TimeEnabled) + { + flags |= 4; + } + + if (SizeEnabled) + { + flags |= 8; + } + + if (ExtensionEnabled) + { + flags |= 16; + } + + if (LongDateEnabled) + { + flags |= 32; + } + + section["showFlags"] = flags; + service.ServerManager.CommitChanges(); + return true; + } + + public bool LongDateEnabled { get; set; } + + public bool ExtensionEnabled { get; set; } + + public bool SizeEnabled { get; set; } + + public bool TimeEnabled { get; set; } + + public bool DateEnabled { get; set; } + } +} diff --git a/JexusManager.Features.DirectoryBrowse/DirectoryBrowseModule.cs b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseModule.cs new file mode 100644 index 00000000..15384bc6 --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseModule.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DirectoryBrowse +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class DirectoryBrowseModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo( + this, + typeof(DirectoryBrowsePage), + "Directory Browsing", + "Configure information to display in a directory listing", + Resources.directory_browsing_36, + Resources.directory_browsing_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.DirectoryBrowse/DirectoryBrowseModuleProvider.cs b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseModuleProvider.cs new file mode 100644 index 00000000..4a07c61d --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DirectoryBrowse +{ + using System; + + using Microsoft.Web.Management.Server; + + public class DirectoryBrowseModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(DirectoryBrowseModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.Designer.cs b/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.Designer.cs new file mode 100644 index 00000000..03435590 --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.Designer.cs @@ -0,0 +1,283 @@ +namespace JexusManager.Features.DirectoryBrowse +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class DirectoryBrowsePage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.cbLongDate = new System.Windows.Forms.CheckBox(); + this.cbDate = new System.Windows.Forms.CheckBox(); + this.cbExtension = new System.Windows.Forms.CheckBox(); + this.cbSize = new System.Windows.Forms.CheckBox(); + this.cbTime = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 5; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.cbLongDate); + this.panel2.Controls.Add(this.cbDate); + this.panel2.Controls.Add(this.cbExtension); + this.panel2.Controls.Add(this.cbSize); + this.panel2.Controls.Add(this.cbTime); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 11; + // + // cbLongDate + // + this.cbLongDate.AutoSize = true; + this.cbLongDate.Enabled = false; + this.cbLongDate.Location = new System.Drawing.Point(33, 158); + this.cbLongDate.Name = "cbLongDate"; + this.cbLongDate.Size = new System.Drawing.Size(74, 17); + this.cbLongDate.TabIndex = 8; + this.cbLongDate.Text = "Long date"; + this.cbLongDate.UseVisualStyleBackColor = true; + this.cbLongDate.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // cbDate + // + this.cbDate.AutoSize = true; + this.cbDate.Enabled = false; + this.cbDate.Location = new System.Drawing.Point(16, 125); + this.cbDate.Name = "cbDate"; + this.cbDate.Size = new System.Drawing.Size(49, 17); + this.cbDate.TabIndex = 7; + this.cbDate.Text = "Date"; + this.cbDate.UseVisualStyleBackColor = true; + this.cbDate.CheckedChanged += new System.EventHandler(this.CbDateCheckedChanged); + // + // cbExtension + // + this.cbExtension.AutoSize = true; + this.cbExtension.Enabled = false; + this.cbExtension.Location = new System.Drawing.Point(16, 90); + this.cbExtension.Name = "cbExtension"; + this.cbExtension.Size = new System.Drawing.Size(72, 17); + this.cbExtension.TabIndex = 6; + this.cbExtension.Text = "Extension"; + this.cbExtension.UseVisualStyleBackColor = true; + this.cbExtension.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // cbSize + // + this.cbSize.AutoSize = true; + this.cbSize.Enabled = false; + this.cbSize.Location = new System.Drawing.Point(16, 54); + this.cbSize.Name = "cbSize"; + this.cbSize.Size = new System.Drawing.Size(46, 17); + this.cbSize.TabIndex = 5; + this.cbSize.Text = "Size"; + this.cbSize.UseVisualStyleBackColor = true; + this.cbSize.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // cbTime + // + this.cbTime.AutoSize = true; + this.cbTime.Enabled = false; + this.cbTime.Location = new System.Drawing.Point(16, 21); + this.cbTime.Name = "cbTime"; + this.cbTime.Size = new System.Drawing.Size(49, 17); + this.cbTime.TabIndex = 4; + this.cbTime.Text = "Time"; + this.cbTime.UseVisualStyleBackColor = true; + this.cbTime.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(357, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to specify the information that displays in a directory listing." + + ""; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(192, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Directory Browsing"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // DirectoryBrowsePage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "DirectoryBrowsePage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private CheckBox cbTime; + private ContextMenuStrip cmsActionPanel; + private CheckBox cbLongDate; + private CheckBox cbDate; + private CheckBox cbExtension; + private CheckBox cbSize; + } +} diff --git a/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.cs b/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.cs new file mode 100644 index 00000000..e1d4a6ba --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.cs @@ -0,0 +1,160 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DirectoryBrowse +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class DirectoryBrowsePage : ModuleDialogPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly DirectoryBrowsePage _owner; + + public PageTaskList(DirectoryBrowsePage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private DirectoryBrowseFeature _feature; + private bool _hasChanges; + private bool _initialized; + private TaskList _taskList; + + public DirectoryBrowsePage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new DirectoryBrowseFeature(this.Module); + _feature.DirectoryBrowseSettingsUpdated = this.Refresh; + _feature.Load(); + base.Initialize(navigationData); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override bool ApplyChanges() + { + _feature.TimeEnabled = cbTime.Checked; + _feature.SizeEnabled = cbSize.Checked; + _feature.ExtensionEnabled = cbExtension.Checked; + _feature.DateEnabled = cbDate.Checked; + _feature.LongDateEnabled = cbLongDate.Checked; + if (!_feature.ApplyChanges()) + { + return false; + } + + this.ClearChanges(); + return true; + } + + protected override void CancelChanges() + { + _feature.CancelChanges(); + this.ClearChanges(); + } + + protected override bool HasChanges + { + get { return _hasChanges; } + } + + protected override bool CanApplyChanges + { + get { return true; } + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + this.Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + this.Refresh(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + protected override void OnRefresh() + { + cbTime.Enabled = cbSize.Enabled = cbExtension.Enabled = cbDate.Enabled = _feature.IsEnabled; + if (!_hasChanges) + { + cbTime.Checked = _feature.TimeEnabled; + cbSize.Checked = _feature.SizeEnabled; + cbExtension.Checked = _feature.ExtensionEnabled; + cbDate.Checked = _feature.DateEnabled; + cbLongDate.Checked = _feature.LongDateEnabled; + _initialized = true; + } + + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + } + + private void CbDateCheckedChanged(object sender, EventArgs e) + { + this.InformChanges(); + cbLongDate.Enabled = cbDate.Checked && cbDate.Enabled; + } + + private void CbSslCheckedChanged(object sender, EventArgs e) + { + InformChanges(); + } + } +} diff --git a/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.resx b/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.resx new file mode 100644 index 00000000..950be5d2 --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/DirectoryBrowsePage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.DirectoryBrowse/DirectoryBrowseSettingsSavedEventHandler.cs b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseSettingsSavedEventHandler.cs new file mode 100644 index 00000000..ba7ae2ba --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/DirectoryBrowseSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.DirectoryBrowse +{ + public delegate void DirectoryBrowseSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.DirectoryBrowse/JexusManager.Features.DirectoryBrowse.csproj b/JexusManager.Features.DirectoryBrowse/JexusManager.Features.DirectoryBrowse.csproj new file mode 100644 index 00000000..04cd8a25 --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/JexusManager.Features.DirectoryBrowse.csproj @@ -0,0 +1,103 @@ + + + + + Debug + AnyCPU + {5FB94CD1-AF29-4396-A27F-891243F12E8C} + Library + Properties + JexusManager.Features.DirectoryBrowse + JexusManager.Features.DirectoryBrowse + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + + + + + + + + + + + + + UserControl + + + DirectoryBrowsePage.cs + + + + + True + True + Resources.resx + + + + + DirectoryBrowsePage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.DirectoryBrowse/JexusManager.snk b/JexusManager.Features.DirectoryBrowse/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.DirectoryBrowse/JexusManager.snk differ diff --git a/JexusManager.Features.DirectoryBrowse/Properties/AssemblyInfo.cs b/JexusManager.Features.DirectoryBrowse/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..24b92c07 --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.DirectoryBrowse")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.DirectoryBrowse")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("5fb94cd1-af29-4396-a27f-891243f12e8c")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.DirectoryBrowse/Properties/Resources.Designer.cs b/JexusManager.Features.DirectoryBrowse/Properties/Resources.Designer.cs new file mode 100644 index 00000000..f2f230b7 --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.DirectoryBrowse.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.DirectoryBrowse.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap directory_browsing_36 { + get { + object obj = ResourceManager.GetObject("directory_browsing_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.DirectoryBrowse/Properties/Resources.resx b/JexusManager.Features.DirectoryBrowse/Properties/Resources.resx new file mode 100644 index 00000000..4c08315c --- /dev/null +++ b/JexusManager.Features.DirectoryBrowse/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\directory_browsing_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.DirectoryBrowse/Resources/directory_browsing_36.png b/JexusManager.Features.DirectoryBrowse/Resources/directory_browsing_36.png new file mode 100644 index 00000000..f550bdfe Binary files /dev/null and b/JexusManager.Features.DirectoryBrowse/Resources/directory_browsing_36.png differ diff --git a/JexusManager.Features.FastCgi/EnvironmentVariables.cs b/JexusManager.Features.FastCgi/EnvironmentVariables.cs new file mode 100644 index 00000000..2461198b --- /dev/null +++ b/JexusManager.Features.FastCgi/EnvironmentVariables.cs @@ -0,0 +1,20 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System.ComponentModel; + + internal class EnvironmentVariables + { + [Category("Misc")] + [DisplayName("Name")] + [DefaultValue("Name")] + public string Name { get; set; } + + [Category("Misc")] + [DisplayName("Value")] + public string Value { get; set; } + } +} diff --git a/JexusManager.Features.FastCgi/EnvironmentVariablesCollection.cs b/JexusManager.Features.FastCgi/EnvironmentVariablesCollection.cs new file mode 100644 index 00000000..c4cbcafe --- /dev/null +++ b/JexusManager.Features.FastCgi/EnvironmentVariablesCollection.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System.Collections; + + internal class EnvironmentVariablesCollection : CollectionBase + { + public EnvironmentVariables this[int index] + { + get + { + return (EnvironmentVariables)this.List[index]; + } + } + + public void Add(EnvironmentVariables variable) + { + this.List.Add(variable); + } + + public void Remove(EnvironmentVariables variable) + { + this.List.Remove(variable); + } + } +} diff --git a/JexusManager.Features.FastCgi/EnvironmentVariablesCollectionEditor.cs b/JexusManager.Features.FastCgi/EnvironmentVariablesCollectionEditor.cs new file mode 100644 index 00000000..c80bd91c --- /dev/null +++ b/JexusManager.Features.FastCgi/EnvironmentVariablesCollectionEditor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System; + using System.ComponentModel.Design; + + internal class EnvironmentVariablesCollectionEditor : CollectionEditor + { + public EnvironmentVariablesCollectionEditor(Type type) + : base(type) + { + } + + protected override string GetDisplayText(object value) + { + EnvironmentVariables item = (EnvironmentVariables)value; + return item.Name; + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.FastCgi/ErrorMode.cs b/JexusManager.Features.FastCgi/ErrorMode.cs new file mode 100644 index 00000000..b495fa2b --- /dev/null +++ b/JexusManager.Features.FastCgi/ErrorMode.cs @@ -0,0 +1,23 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System.ComponentModel; + + internal enum ErrorMode : long + { + [Description("ReturnStdErrIn500")] + ReturnStdErrIn500 = 0, + + [Description("ReturnGeneric500")] + ReturnGeneric500 = 1, + + [Description("IgnoreAndReturn200")] + IgnoreAndReturn200 = 2, + + [Description("TerminateProcess")] + TerminateProcess = 3 + } +} diff --git a/JexusManager.Features.FastCgi/FastCgiFeature.cs b/JexusManager.Features.FastCgi/FastCgiFeature.cs new file mode 100644 index 00000000..f2ff50b3 --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiFeature.cs @@ -0,0 +1,166 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.FastCgi +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Authorization feature. + /// + internal class FastCgiFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly FastCgiFeature _owner; + + public FeatureTaskList(FastCgiFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("AddAllow", "Add Application...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Edit...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddAllow() + { + _owner.AddAllow(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Edit(); + } + } + + public FastCgiFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + // IMPORTANT: force to be server only. + var section = service.ServerManager.GetApplicationHostConfiguration().GetSection("system.webServer/fastCgi"); + return section.GetCollection(); + } + + public void Load() + { + this.LoadItems(); + } + + public void AddAllow() + { + var dialog = new NewApplicationDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected FastCGI Application?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + this.RemoveItem(); + } + + public void Edit() + { + var dialog = new NewApplicationDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + protected override void OnSettingsSaved() + { + this.FastCgiSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210483"); + return false; + } + + public FastCgiSettingsSavedEventHandler FastCgiSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "FastCGI Settings"; } + } + } +} diff --git a/JexusManager.Features.FastCgi/FastCgiItem.cs b/JexusManager.Features.FastCgi/FastCgiItem.cs new file mode 100644 index 00000000..ef3ed91e --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiItem.cs @@ -0,0 +1,212 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System.ComponentModel; + + using Microsoft.Web.Administration; + + internal class FastCgiItem : IItem + { + public FastCgiItem(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inhertied"; + EnvironmentVariables = new EnvironmentVariablesCollection(); + AdvancedSettings = new AdvancedSettings(); + if (element == null) + { + this.Path = this.Arguments = MonitorChangesTo = string.Empty; + MaxInstances = 4U; + InstanceMaxRequests = 200U; + ActivityTimeout = 30U; + IdleTimeout = 300U; + QueueLength = 1000U; + RapidFailsPerMinute = 10U; + RequestTimeout = 90U; + return; + } + + this.Path = (string)element["fullPath"]; + this.Arguments = (string)element["arguments"]; + + this.MonitorChangesTo = (string)element["monitorChangesTo"]; + ErrorMode = (ErrorMode)element["stderrMode"]; + MaxInstances = (uint)element["maxInstances"]; + IdleTimeout = (uint)element["idleTimeout"]; + ActivityTimeout = (uint)element["activityTimeout"]; + RequestTimeout = (uint)element["requestTimeout"]; + this.InstanceMaxRequests = (uint)element["instanceMaxRequests"]; + this.SignalBeforeTerminateSeconds = (uint)element["signalBeforeTerminateSeconds"]; + AdvancedSettings.Protocol = (Protocol)element["protocol"]; + QueueLength = (uint)element["queueLength"]; + AdvancedSettings.FlushNamedPipe = (bool)element["flushNamedPipe"]; + this.RapidFailsPerMinute = (uint)element["rapidFailsPerMinute"]; + + foreach (ConfigurationElement child in element.GetCollection("environmentVariables")) + { + EnvironmentVariables.Add( + new EnvironmentVariables { Name = (string)child["name"], Value = (string)child["value"] }); + } + } + + [Browsable(false)] + public string Arguments { get; set; } + + [Browsable(false)] + public string Path { get; set; } + + [Browsable(false)] + public ConfigurationElement Element { get; set; } + + [Browsable(false)] + public string Flag { get; set; } + + public bool Equals(FastCgiItem other) + { + // all properties + return this.Match(other); + } + + public void Apply() + { + this.Element["fullPath"] = this.Path; + this.Element["arguments"] = this.Arguments; + + this.Element["monitorChangesTo"] = this.MonitorChangesTo; + this.Element["stderrMode"] = this.ErrorMode; + this.Element["maxInstances"] = this.MaxInstances; + this.Element["idleTimeout"] = this.IdleTimeout; + this.Element["activityTimeout"] = this.ActivityTimeout; + this.Element["requestTimeout"] = this.RequestTimeout; + this.Element["instanceMaxRequests"] = this.InstanceMaxRequests; + this.Element["signalBeforeTerminateSeconds"] = this.SignalBeforeTerminateSeconds; + this.Element["protocol"] = this.AdvancedSettings.Protocol; + this.Element["queueLength"] = this.QueueLength; + this.Element["flushNamedPipe"] = this.AdvancedSettings.FlushNamedPipe; + this.Element["rapidFailsPerMinute"] = this.RapidFailsPerMinute; + + var collection = Element.GetCollection("environmentVariables"); + collection.Clear(); + foreach (EnvironmentVariables item in EnvironmentVariables) + { + var newElement = collection.CreateElement(); + newElement["name"] = item.Name; + newElement["value"] = item.Value; + collection.Add(newElement); + } + } + + public bool Match(FastCgiItem other) + { + // match combined keys. + return other != null && other.Arguments == this.Arguments && other.Path == this.Path; + } + + [Browsable(true)] + [Category("General")] + [Description("Specifies optional environment variables that will be set in the FastCGI executable.")] + [DisplayName("Environment Variables")] + [Editor(typeof(EnvironmentVariablesCollectionEditor), + typeof(System.Drawing.Design.UITypeEditor))] + public EnvironmentVariablesCollection EnvironmentVariables { get; set; } + + [Browsable(true)] + [Category("General")] + [Description("Specifies the number of requests a FastCGI process for this application is allowed to handle.")] + [DisplayName("Instance MaxRequests")] + [DefaultValue(200U)] + public uint InstanceMaxRequests { get; set; } + + [Browsable(true)] + [Category("General")] + [Description("Specifies the maximum number of FastCGI process that are allowed in the application's process pool.")] + [DisplayName("Max Instances")] + [DefaultValue(4U)] + public uint MaxInstances { get; set; } + + [Browsable(true)] + [Category("General")] + [Description("Specifies path to a file changes to which will trigger recycle of FastCGI processes.")] + [DisplayName("Monitor changes to file")] + [DefaultValue("")] + [Editor(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(System.Drawing.Design.UITypeEditor))] + public string MonitorChangesTo { get; set; } + + [Browsable(true)] + [Category("General")] + [Description("Specifies how FastCGI module should behave when FastCGI process sends text on standard error stream.")] + [DisplayName("Standard error mode")] + [TypeConverter(typeof(DescriptionConverter))] + public ErrorMode ErrorMode { get; set; } + + [Browsable(true)] + [Category("Process Model")] + [Description("Specifies the time, in seconds, that a FastCGI process for this application is allowed to run without communicating with IIS.")] + [DisplayName("Activity Timeout")] + [DefaultValue(30U)] + public uint ActivityTimeout { get; set; } + + [Browsable(true)] + [Category("Process Model")] + [DisplayName("Advanced Settings")] + public AdvancedSettings AdvancedSettings { get; set; } + + [Browsable(true)] + [Category("Process Model")] + [Description("Specifies the time, in seconds, that a FastCGI process for this application is allowed to remain idle.")] + [DisplayName("Idle Timeout")] + [DefaultValue(300U)] + public uint IdleTimeout { get; set; } + + [Browsable(true)] + [Category("Process Model")] + [Description("Maximum number of requests that are permitted into FastCGI handler queue.")] + [DisplayName("Queue Length")] + [DefaultValue(1000U)] + public uint QueueLength { get; set; } + + [Browsable(true)] + [Category("Process Model")] + [Description("Specifies the number of FastCGI process failures allowed in a single minute before the FastCGI handler takes it off line.")] + [DisplayName("Rapid Fails PerMinute")] + [DefaultValue(10U)] + public uint RapidFailsPerMinute { get; set; } + + [Browsable(true)] + [Category("Process Model")] + [Description("Specifies the maximum allowed time, in seconds, for request processing.")] + [DisplayName("Request Timeout")] + [DefaultValue(90U)] + public uint RequestTimeout { get; set; } + + [Browsable(true)] + [Category("Process Model")] + [Description("Specifies the amount of time, in seconds, that IIS will wait after IIS signals a FastCGI application that it needs to shut down.")] + [DisplayName("Signal Before Terminate")] + [DefaultValue(300U)] + public uint SignalBeforeTerminateSeconds { get; set; } + } + + [TypeConverter(typeof(ExpandableObjectConverter))] + internal class AdvancedSettings + { + [Browsable(true)] + [Description("Specifies whether or not the named pipe between FastCGI processes for this application is flushed before shutting down the application.")] + [DisplayName("FlushNamedpipe")] + public bool FlushNamedPipe { get; set; } + + [Browsable(true)] + [Description("Specifies protocol to be used to communicate with FastCGI process.")] + [DisplayName("Protocol")] + [TypeConverter(typeof(DescriptionConverter))] + public Protocol Protocol { get; set; } + + public override string ToString() + { + return string.Empty; + } + } +} diff --git a/JexusManager.Features.FastCgi/FastCgiModule.cs b/JexusManager.Features.FastCgi/FastCgiModule.cs new file mode 100644 index 00000000..8b708c58 --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiModule.cs @@ -0,0 +1,25 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class FastCgiModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(FastCgiPage), "FastCGI Settings", + "Configure FastCGI process application pools", Resources.response_header_36, + Resources.response_header_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.FastCgi/FastCgiModuleProvider.cs b/JexusManager.Features.FastCgi/FastCgiModuleProvider.cs new file mode 100644 index 00000000..680ed72c --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System; + + using Microsoft.Web.Management.Server; + + public class FastCgiModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(FastCgiModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return scope == ManagementScope.Server; + } + } +} diff --git a/JexusManager.Features.FastCgi/FastCgiPage.Designer.cs b/JexusManager.Features.FastCgi/FastCgiPage.Designer.cs new file mode 100644 index 00000000..0f93ab49 --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiPage.Designer.cs @@ -0,0 +1,259 @@ +namespace JexusManager.Features.FastCgi +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class FastCgiPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chPath = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chArguments = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chInstances = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chRequests = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chPath, + this.chArguments, + this.chInstances, + this.chRequests}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 13); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 527); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + this.listView1.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.ListView1MouseDoubleClick); + // + // chPath + // + this.chPath.Text = "Full Path"; + this.chPath.Width = 300; + // + // chArguments + // + this.chArguments.Text = "Arguments"; + this.chArguments.Width = 200; + // + // chInstances + // + this.chInstances.Text = "Max. Instances"; + this.chInstances.Width = 120; + // + // chRequests + // + this.chRequests.Text = "Instance Max. Requests"; + this.chRequests.Width = 180; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(393, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure FastCGI applications used by websites on this serve" + + "r."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(174, 25); + this.label3.TabIndex = 2; + this.label3.Text = "FastCGI Settings"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // FastCgiPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "FastCgiPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chPath; + private ColumnHeader chArguments; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chInstances; + private ColumnHeader chRequests; + } +} diff --git a/JexusManager.Features.FastCgi/FastCgiPage.cs b/JexusManager.Features.FastCgi/FastCgiPage.cs new file mode 100644 index 00000000..442187a5 --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiPage.cs @@ -0,0 +1,134 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class FastCgiPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly FastCgiPage _owner; + + public PageTaskList(FastCgiPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class FastCgiListViewItem : ListViewItem + { + public FastCgiItem Item { get; } + + private readonly FastCgiPage _page; + + public FastCgiListViewItem(FastCgiItem item, FastCgiPage page) + : base(item.Path) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Arguments)); + this.SubItems.Add(new ListViewSubItem(this, item.MaxInstances.ToString())); + this.SubItems.Add(new ListViewSubItem(this, item.InstanceMaxRequests.ToString())); + } + } + + private FastCgiFeature _feature; + private PageTaskList _taskList; + + public FastCgiPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new FastCgiFeature(this.Module); + _feature.FastCgiSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new FastCgiListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (FastCgiListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((FastCgiListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + private void ListView1MouseDoubleClick(object sender, MouseEventArgs e) + { + _feature.Edit(); + } + } +} diff --git a/JexusManager.Features.FastCgi/FastCgiPage.resx b/JexusManager.Features.FastCgi/FastCgiPage.resx new file mode 100644 index 00000000..80c56e6e --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiPage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.FastCgi/FastCgiSettingsSavedEventHandler.cs b/JexusManager.Features.FastCgi/FastCgiSettingsSavedEventHandler.cs new file mode 100644 index 00000000..ac31185a --- /dev/null +++ b/JexusManager.Features.FastCgi/FastCgiSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + public delegate void FastCgiSettingsSavedEventHandler(); +} diff --git a/JexusManager.Features.FastCgi/JexusManager.Features.FastCgi.csproj b/JexusManager.Features.FastCgi/JexusManager.Features.FastCgi.csproj new file mode 100644 index 00000000..d58d6942 --- /dev/null +++ b/JexusManager.Features.FastCgi/JexusManager.Features.FastCgi.csproj @@ -0,0 +1,136 @@ + + + + + Debug + AnyCPU + {AA816B3F-8B23-4440-A276-898958204A6D} + Library + Properties + JexusManager.Features.FastCgi + JexusManager.Features.FastCgi + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + + + + + + UserControl + + + FastCgiPage.cs + + + + Form + + + NewApplicationDialog.cs + + + + True + True + Resources.resx + + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + FastCgiPage.cs + + + NewApplicationDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.FastCgi/JexusManager.snk b/JexusManager.Features.FastCgi/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.FastCgi/JexusManager.snk differ diff --git a/JexusManager.Features.FastCgi/NewApplicationDialog.Designer.cs b/JexusManager.Features.FastCgi/NewApplicationDialog.Designer.cs new file mode 100644 index 00000000..e1f83c1b --- /dev/null +++ b/JexusManager.Features.FastCgi/NewApplicationDialog.Designer.cs @@ -0,0 +1,160 @@ +namespace JexusManager.Features.FastCgi +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewApplicationDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtArguments = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.pgProperties = new System.Windows.Forms.PropertyGrid(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(382, 471); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(281, 471); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 25); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(420, 20); + this.txtPath.TabIndex = 6; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 9); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(51, 13); + this.label2.TabIndex = 7; + this.label2.Text = "Full Path:"; + // + // txtArguments + // + this.txtArguments.Location = new System.Drawing.Point(15, 80); + this.txtArguments.Name = "txtArguments"; + this.txtArguments.Size = new System.Drawing.Size(462, 20); + this.txtArguments.TabIndex = 8; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 64); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(60, 13); + this.label1.TabIndex = 11; + this.label1.Text = "Arguments:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 114); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(98, 13); + this.label3.TabIndex = 13; + this.label3.Text = "FastCGI Properties:"; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(441, 23); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(35, 23); + this.btnBrowse.TabIndex = 14; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // pgProperties + // + this.pgProperties.CategoryForeColor = System.Drawing.SystemColors.InactiveCaptionText; + this.pgProperties.Location = new System.Drawing.Point(15, 130); + this.pgProperties.Name = "pgProperties"; + this.pgProperties.Size = new System.Drawing.Size(461, 335); + this.pgProperties.TabIndex = 15; + this.pgProperties.ToolbarVisible = false; + // + // NewApplicationDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(489, 506); + this.Controls.Add(this.pgProperties); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.label3); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtArguments); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewApplicationDialog"; + this.Text = "Add FastCGI Application"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private TextBox txtPath; + private Label label2; + private TextBox txtArguments; + private Label label1; + private Label label3; + private Button btnBrowse; + private PropertyGrid pgProperties; + } +} diff --git a/JexusManager.Features.FastCgi/NewApplicationDialog.cs b/JexusManager.Features.FastCgi/NewApplicationDialog.cs new file mode 100644 index 00000000..258669d7 --- /dev/null +++ b/JexusManager.Features.FastCgi/NewApplicationDialog.cs @@ -0,0 +1,78 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewApplicationDialog : DialogForm + { + private readonly FastCgiFeature _feature; + + public NewApplicationDialog(IServiceProvider serviceProvider, FastCgiItem existing, FastCgiFeature feature) + : base(serviceProvider) + { + this.InitializeComponent(); + this.Text = string.Format("{0} FastCGI Application", existing == null ? "Add" : "Edit"); + _feature = feature; + txtPath.ReadOnly = txtArguments.ReadOnly = existing != null; + this.Item = existing ?? new FastCgiItem(null); + txtPath.Text = this.Item.Path; + txtArguments.Text = this.Item.Arguments; + pgProperties.SelectedObject = this.Item; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + this.Item.Path = txtPath.Text; + Item.Arguments = txtArguments.Text; + + if (!txtPath.ReadOnly && _feature.Items.Any(item => item.Match(this.Item))) + { + ShowMessage( + "This FastCGI application already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + this.DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtPath, "TextChanged") + .Subscribe(evt => + { + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowFileDialog(txtPath, "CGI Executables|*.exe|CGI Files|*.dll|All Files|*.*"); + })); + } + + public FastCgiItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210483"); + } + } +} diff --git a/JexusManager.Features.FastCgi/NewApplicationDialog.resx b/JexusManager.Features.FastCgi/NewApplicationDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.FastCgi/NewApplicationDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.FastCgi/Properties/AssemblyInfo.cs b/JexusManager.Features.FastCgi/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..32ee4e73 --- /dev/null +++ b/JexusManager.Features.FastCgi/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.FastCgi")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.FastCgi")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("aa816b3f-8b23-4440-a276-898958204a6d")] + +// 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")] + +[assembly: InternalsVisibleTo("JexusManager.Features.Handlers, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.FastCgi/Properties/Resources.Designer.cs b/JexusManager.Features.FastCgi/Properties/Resources.Designer.cs new file mode 100644 index 00000000..3be8801d --- /dev/null +++ b/JexusManager.Features.FastCgi/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.FastCgi.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.FastCgi.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap response_header_36 { + get { + object obj = ResourceManager.GetObject("response_header_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.FastCgi/Properties/Resources.resx b/JexusManager.Features.FastCgi/Properties/Resources.resx new file mode 100644 index 00000000..d6b63b74 --- /dev/null +++ b/JexusManager.Features.FastCgi/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\response_header_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.FastCgi/Protocol.cs b/JexusManager.Features.FastCgi/Protocol.cs new file mode 100644 index 00000000..d137a94c --- /dev/null +++ b/JexusManager.Features.FastCgi/Protocol.cs @@ -0,0 +1,17 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.FastCgi +{ + using System.ComponentModel; + + internal enum Protocol : long + { + [Description("NamedPipe")] + NamedPipe = 0, + + [Description("Tcp")] + Tcp = 1 + } +} diff --git a/JexusManager.Features.FastCgi/Resources/response_header_36.png b/JexusManager.Features.FastCgi/Resources/response_header_36.png new file mode 100644 index 00000000..7b24c374 Binary files /dev/null and b/JexusManager.Features.FastCgi/Resources/response_header_36.png differ diff --git a/JexusManager.Features.FastCgi/packages.config b/JexusManager.Features.FastCgi/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.FastCgi/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/HandlersFeature.cs b/JexusManager.Features.Handlers/HandlersFeature.cs new file mode 100644 index 00000000..c438f097 --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersFeature.cs @@ -0,0 +1,414 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.Handlers +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + using Microsoft.Web.Management.Server; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class HandlersFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly HandlersFeature _owner; + + public FeatureTaskList(HandlersFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (_owner.IsInOrder) + { + result.Add(GetMoveUpTaskItem(_owner.CanMoveUp)); + result.Add(GetMoveDownTaskItem(_owner.CanMoveDown)); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Unorder", "View Unordered List...", string.Empty).SetUsage()); + } + else + { + result.Add(new MethodTaskItem("AddManaged", "Add Managed Handler...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("AddScript", "Add Script Map...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("AddWildcard", "Add Wildcard Script Map...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Add", "Add Module Mapping...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Edit Feature Permissions...", string.Empty).SetUsage()); + if (_owner.CanRevert) + { + result.Add(new MethodTaskItem("Revert", "Revert to Parent", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("InOrder", "View Ordered List...", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddManaged() + { + _owner.AddManaged(); + } + + [Obfuscation(Exclude = true)] + public void AddScript() + { + _owner.AddScript(); + } + + [Obfuscation(Exclude = true)] + public void AddWildcard() + { + _owner.AddWildcard(); + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + + [Obfuscation(Exclude = true)] + public void Rename() + { + _owner.Rename(); + } + + [Obfuscation(Exclude = true)] + public override void MoveUp() + { + _owner.MoveUp(); + } + + [Obfuscation(Exclude = true)] + public override void MoveDown() + { + _owner.MoveDown(); + } + + [Obfuscation(Exclude = true)] + public void InOrder() + { + _owner.InOrder(); + } + + [Obfuscation(Exclude = true)] + public void Unorder() + { + _owner.Unorder(); + } + + [Obfuscation(Exclude = true)] + public void Revert() + { + _owner.Revert(); + } + } + + public HandlersFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + this.Items = new List(); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + + // server level modules are in "" location. + ConfigurationSection section = service.Scope == ManagementScope.Server ? service.GetSection("system.webServer/handlers", string.Empty) : service.GetSection("system.webServer/handlers", null, false); + this.AccessPolicy = (long)section["accessPolicy"]; + this.CanRevert = service.Scope != ManagementScope.Server; + this.IsInOrder = false; + LoadItems(); + } + + public long AccessPolicy { get; set; } + + public void Add() + { + var dialog = new NewMappingDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + AddItem(dialog.Item); + } + + public void AddManaged() + { + var dialog = new NewHandlerDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddScript() + { + var dialog = new NewScriptMapDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddWildcard() + { + var dialog = new NewWildcardDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Set() + { + var dialog = new PermissionsDialog(this.Module, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + service.ServerManager.CommitChanges(); + this.OnSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected authorization rule?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + HandlersItem newItem; + + if (!string.IsNullOrWhiteSpace(SelectedItem.Type)) + { + var dialog = new NewHandlerDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + newItem = dialog.Item; + } + else if (SelectedItem.Modules == "IsapiModule" && !string.IsNullOrWhiteSpace(SelectedItem.ScriptProcessor)) + { + var dialog = new NewScriptMapDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + newItem = dialog.Item; + } + else + { + var dialog = new NewMappingDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + newItem = dialog.Item; + } + + this.EditItem(newItem); + } + + public void Rename() + { + } + + public void MoveUp() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + MoveUpItem(); + } + + public void MoveDown() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + MoveDownItem(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + // server level modules are in "" location. + ConfigurationSection section = service.Scope == ManagementScope.Server ? service.GetSection("system.webServer/handlers", string.Empty) : service.GetSection("system.webServer/handlers", null, false); + return section.GetCollection(); + } + + public void InOrder() + { + this.IsInOrder = true; + this.OnSettingsSaved(); + } + + public void Unorder() + { + this.IsInOrder = false; + this.OnSettingsSaved(); + } + + public void Revert() + { + if (!CanRevert) + { + throw new InvalidOperationException("Revert operation cannot be done at server level"); + } + + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "Reverting to the parent configuration will result in the loss of all settings in the local configuration file for this feature. Are you sure you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + RevertItems(); + } + + protected override void OnSettingsSaved() + { + this.HandlersSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210505"); + return false; + } + + public bool IsInOrder { get; private set; } + + public bool CanRevert { get; private set; } + + public HandlersSettingsSavedEventHandler HandlersSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + + public string Name + { + get { return "Handler Mappings"; } + } + } +} diff --git a/JexusManager.Features.Handlers/HandlersItem.cs b/JexusManager.Features.Handlers/HandlersItem.cs new file mode 100644 index 00000000..1ef2f75c --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersItem.cs @@ -0,0 +1,170 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System.Collections.Generic; + using System.Linq; + using System.Text; + + using Microsoft.Web.Administration; + + internal class HandlersItem : IItem + { + public HandlersItem(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inhertied"; + if (element == null) + { + this.PreConditions = new List(0); + Verb = "*"; + Path = Modules = ScriptProcessor = Type = string.Empty; + ResponseBufferLimit = 4194304; + RequireAccess = 3; + return; + } + + this.Name = (string)element["name"]; + this.Path = (string)element["path"]; + this.ResourceType = (long)element["resourceType"]; + this.Verb = (string)element["verb"]; + this.RequireAccess = (long)element["requireAccess"]; + this.Modules = (string)element["modules"]; + this.ScriptProcessor = (string)element["scriptProcessor"]; + this.Type = (string)element["type"]; + var content = (string)element["preCondition"]; + this.PreConditions = content.Split(',').ToList(); + ResponseBufferLimit = (uint)element["responseBufferLimit"]; + AllowPathInfo = (bool)element["allowPathInfo"]; + } + + public bool AllowPathInfo { get; set; } + + public uint ResponseBufferLimit { get; set; } + + public string ScriptProcessor { get; set; } + + public long RequireAccess { get; set; } + + public string Verb { get; set; } + + public long ResourceType { get; set; } + + public string Path { get; set; } + + public List PreConditions { get; set; } + + public string Type { get; set; } + + public string Modules { get; set; } + + public string Name { get; set; } + + public ConfigurationElement Element { get; set; } + + public string Flag { get; set; } + + public string GetState(long accessPolicy) + { + if (this.RequireAccess == 0L) + { + return "Enabled"; + } + + if (this.RequireAccess == 1L && (accessPolicy & 1L) == 1L) + { + return "Enabled"; + } + + if (this.RequireAccess == 2L && (accessPolicy & 2L) == 2L) + { + return "Enabled"; + } + + if (this.RequireAccess == 3L && (accessPolicy & 512L) == 512L) + { + return "Enabled"; + } + + if (this.RequireAccess == 4L && (accessPolicy & 4L) == 4L) + { + return "Enabled"; + } + + return "Disabled"; + } + + public string PathType + { + get + { + switch (this.ResourceType) + { + case 0: + return "File"; + case 1: + return "Folder"; + case 2: + return "File or Folder"; + case 3: + return "Unspecified"; + } + + return "Unspecified"; + } + } + + public string TypeString + { + get + { + return !string.IsNullOrWhiteSpace(this.Type) ? this.Type : this.Modules; + } + } + + public bool Equals(HandlersItem other) + { + // all properties + return this.Match(other) && other.Type == this.Type; + } + + public void Apply() + { + Element["name"] = Name; + Element["path"] = Path; + Element["resourceType"] = ResourceType; + Element["verb"] = Verb; + Element["requireAccess"] = RequireAccess; + Element["modules"] = Modules; + Element["scriptProcessor"] = ScriptProcessor; + Element["type"] = Type; + Element["preCondition"] = Combine(PreConditions); + Element["responseBufferLimit"] = ResponseBufferLimit; + Element["allowPathInfo"] = AllowPathInfo; + } + + private static string Combine(List preConditions) + { + if (preConditions.Count == 0) + { + return string.Empty; + } + + var result = new StringBuilder(preConditions[0]); + for (int index = 1; index < preConditions.Count; index++) + { + result.AppendFormat(",{0}", preConditions[index]); + } + + return result.ToString(); + } + + public bool Match(HandlersItem other) + { + // match combined keys. + return other != null && other.Name == this.Name; + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Handlers/HandlersModule.cs b/JexusManager.Features.Handlers/HandlersModule.cs new file mode 100644 index 00000000..aec3ffe3 --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersModule.cs @@ -0,0 +1,25 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class HandlersModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(HandlersPage), "Handler Mappings", + "Specify resources that handle responses for specific request types", Resources.handlers_36, + Resources.handlers_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Handlers/HandlersModuleProvider.cs b/JexusManager.Features.Handlers/HandlersModuleProvider.cs new file mode 100644 index 00000000..ac0abf01 --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + + using Microsoft.Web.Management.Server; + + public class HandlersModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(HandlersModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Handlers/HandlersPage.Designer.cs b/JexusManager.Features.Handlers/HandlersPage.Designer.cs new file mode 100644 index 00000000..a415547c --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersPage.Designer.cs @@ -0,0 +1,314 @@ +namespace JexusManager.Features.Handlers +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class HandlersPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chPath = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chState = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.cbPathType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chHandler = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chPath, + this.chState, + this.cbPathType, + this.chHandler, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 180; + // + // chPath + // + this.chPath.Text = "Path"; + this.chPath.Width = 120; + // + // chState + // + this.chState.Text = "State"; + this.chState.Width = 100; + // + // cbPathType + // + this.cbPathType.Text = "Path Type"; + this.cbPathType.Width = 100; + // + // chHandler + // + this.chHandler.Text = "Handler"; + this.chHandler.Width = 180; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 80; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(595, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to specify the resources, such as DLLs and managed code, that ha" + + "ndle responses for specific request types."; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(187, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Handler Mappings"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // HandlersPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "HandlersPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chName; + private ColumnHeader chPath; + private ColumnHeader chHandler; + private ColumnHeader chState; + private ColumnHeader cbPathType; + } +} diff --git a/JexusManager.Features.Handlers/HandlersPage.cs b/JexusManager.Features.Handlers/HandlersPage.cs new file mode 100644 index 00000000..b3d7579c --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersPage.cs @@ -0,0 +1,134 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class HandlersPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly HandlersPage _owner; + + public PageTaskList(HandlersPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class HandlersListViewItem : ListViewItem + { + public HandlersItem Item { get; } + + private readonly HandlersPage _page; + + public HandlersListViewItem(HandlersItem item, HandlersPage page) + : base(item.Name) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Path)); + this.SubItems.Add(new ListViewSubItem(this, item.GetState(page._feature.AccessPolicy))); + this.SubItems.Add(new ListViewSubItem(this, item.PathType)); + this.SubItems.Add(new ListViewSubItem(this, item.TypeString)); + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private HandlersFeature _feature; + private PageTaskList _taskList; + + public HandlersPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new HandlersFeature(this.Module); + _feature.HandlersSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + toolStrip2.Visible = !_feature.IsInOrder; + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new HandlersListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (HandlersListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((HandlersListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.Handlers/HandlersPage.resx b/JexusManager.Features.Handlers/HandlersPage.resx new file mode 100644 index 00000000..464d127c --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersPage.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 497, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/HandlersSettingsSavedEventHandler.cs b/JexusManager.Features.Handlers/HandlersSettingsSavedEventHandler.cs new file mode 100644 index 00000000..eba701d4 --- /dev/null +++ b/JexusManager.Features.Handlers/HandlersSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + public delegate void HandlersSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Handlers/JexusManager.Features.Handlers.csproj b/JexusManager.Features.Handlers/JexusManager.Features.Handlers.csproj new file mode 100644 index 00000000..30bac6b2 --- /dev/null +++ b/JexusManager.Features.Handlers/JexusManager.Features.Handlers.csproj @@ -0,0 +1,183 @@ + + + + + Debug + AnyCPU + {BD450763-248D-4117-9AF0-51A625667B61} + Library + Properties + JexusManager.Features.Handlers + JexusManager.Features.Handlers + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + + UserControl + + + HandlersPage.cs + + + + Form + + + NewHandlerDialog.cs + + + Form + + + NewMappingDialog.cs + + + Form + + + NewScriptMapDialog.cs + + + Form + + + NewWildcardDialog.cs + + + Form + + + PermissionsDialog.cs + + + + True + True + Resources.resx + + + Form + + + RestrictionsDialog.cs + + + + + HandlersPage.cs + + + NewHandlerDialog.cs + + + NewMappingDialog.cs + + + NewScriptMapDialog.cs + + + NewWildcardDialog.cs + + + PermissionsDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + RestrictionsDialog.cs + + + + + {aa816b3f-8b23-4440-a276-898958204a6d} + JexusManager.Features.FastCgi + + + {8bfab6bc-57c1-44fc-88e2-b3ce5b75be9f} + JexusManager.Features.Modules + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/JexusManager.snk b/JexusManager.Features.Handlers/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Handlers/JexusManager.snk differ diff --git a/JexusManager.Features.Handlers/NewHandlerDialog.Designer.cs b/JexusManager.Features.Handlers/NewHandlerDialog.Designer.cs new file mode 100644 index 00000000..7dbaa8b4 --- /dev/null +++ b/JexusManager.Features.Handlers/NewHandlerDialog.Designer.cs @@ -0,0 +1,182 @@ +namespace JexusManager.Features.Handlers +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewHandlerDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtType = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.btnRestrictions = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(332, 301); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(231, 301); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 156); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(38, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 172); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(269, 20); + this.txtName.TabIndex = 6; + // + // txtType + // + this.txtType.Location = new System.Drawing.Point(15, 96); + this.txtType.Name = "txtType"; + this.txtType.Size = new System.Drawing.Size(393, 21); + this.txtType.TabIndex = 8; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 80); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(34, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Type:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 120); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(227, 13); + this.label2.TabIndex = 10; + this.label2.Text = "Example: System.Web.UI.PageHandlerFactory"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(74, 13); + this.label3.TabIndex = 11; + this.label3.Text = "Request path:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 25); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(297, 20); + this.txtPath.TabIndex = 12; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 48); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(128, 13); + this.label4.TabIndex = 13; + this.label4.Text = "Example: *.bas, wsvc.axd"; + // + // btnRestrictions + // + this.btnRestrictions.Location = new System.Drawing.Point(15, 216); + this.btnRestrictions.Name = "btnRestrictions"; + this.btnRestrictions.Size = new System.Drawing.Size(140, 23); + this.btnRestrictions.TabIndex = 14; + this.btnRestrictions.Text = "Request Restrictions..."; + this.btnRestrictions.UseVisualStyleBackColor = true; + // + // NewHandlerDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(439, 336); + this.Controls.Add(this.btnRestrictions); + this.Controls.Add(this.label4); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtType); + this.Controls.Add(this.txtName); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewHandlerDialog"; + this.Text = "Add Managed Handler"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtName; + private ComboBox txtType; + private Label label1; + private Label label2; + private Label label3; + private TextBox txtPath; + private Label label4; + private Button btnRestrictions; + } +} diff --git a/JexusManager.Features.Handlers/NewHandlerDialog.cs b/JexusManager.Features.Handlers/NewHandlerDialog.cs new file mode 100644 index 00000000..0e987971 --- /dev/null +++ b/JexusManager.Features.Handlers/NewHandlerDialog.cs @@ -0,0 +1,90 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewHandlerDialog : DialogForm + { + private readonly HandlersFeature _feature; + + public NewHandlerDialog(IServiceProvider serviceProvider, HandlersItem existing, HandlersFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add Managed Handler" : "Edit Managed Handler"; + txtName.ReadOnly = existing != null; + _feature = feature; + Item = existing ?? new HandlersItem(null); + if (existing != null) + { + txtType.Text = Item.Type; + txtName.Text = Item.Name; + txtPath.Text = Item.Path; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.Type = txtType.Text; + Item.Name = txtName.Text; + Item.Path = txtPath.Text; + if (!txtName.ReadOnly && _feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A handler with this name already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtType, "TextChanged")) + .Merge(Observable.FromEventPattern(txtPath, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtType.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnRestrictions, "Click") + .Subscribe(evt => + { + var dialog = new RestrictionsDialog(ServiceProvider, Item); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + })); + } + + public HandlersItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210505#Add_Handler"); + } + } +} diff --git a/JexusManager.Features.Handlers/NewHandlerDialog.resx b/JexusManager.Features.Handlers/NewHandlerDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Handlers/NewHandlerDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/NewMappingDialog.Designer.cs b/JexusManager.Features.Handlers/NewMappingDialog.Designer.cs new file mode 100644 index 00000000..31a599f7 --- /dev/null +++ b/JexusManager.Features.Handlers/NewMappingDialog.Designer.cs @@ -0,0 +1,204 @@ +namespace JexusManager.Features.Handlers +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewMappingDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtExecutable = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.btnRestrictions = new System.Windows.Forms.Button(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.txtModule = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(332, 301); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(231, 301); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 175); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(38, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 191); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(269, 20); + this.txtName.TabIndex = 6; + // + // txtExecutable + // + this.txtExecutable.Location = new System.Drawing.Point(15, 140); + this.txtExecutable.Name = "txtExecutable"; + this.txtExecutable.Size = new System.Drawing.Size(367, 20); + this.txtExecutable.TabIndex = 8; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 124); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(109, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Executable (optional):"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(74, 13); + this.label3.TabIndex = 11; + this.label3.Text = "Request path:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 25); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(297, 20); + this.txtPath.TabIndex = 12; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 48); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(128, 13); + this.label4.TabIndex = 13; + this.label4.Text = "Example: *.bas, wsvc.axd"; + // + // btnRestrictions + // + this.btnRestrictions.Location = new System.Drawing.Point(15, 220); + this.btnRestrictions.Name = "btnRestrictions"; + this.btnRestrictions.Size = new System.Drawing.Size(140, 23); + this.btnRestrictions.TabIndex = 14; + this.btnRestrictions.Text = "Request Restrictions..."; + this.btnRestrictions.UseVisualStyleBackColor = true; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(388, 138); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(28, 23); + this.btnBrowse.TabIndex = 15; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 76); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(45, 13); + this.label2.TabIndex = 17; + this.label2.Text = "Module:"; + // + // txtModule + // + this.txtModule.Location = new System.Drawing.Point(15, 92); + this.txtModule.Name = "txtModule"; + this.txtModule.Size = new System.Drawing.Size(393, 21); + this.txtModule.TabIndex = 16; + // + // NewMappingDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(439, 336); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtModule); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.btnRestrictions); + this.Controls.Add(this.label4); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label3); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtExecutable); + this.Controls.Add(this.txtName); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewMappingDialog"; + this.Text = "Add Module Mapping"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtName; + private TextBox txtExecutable; + private Label label1; + private Label label3; + private TextBox txtPath; + private Label label4; + private Button btnRestrictions; + private Button btnBrowse; + private Label label2; + private ComboBox txtModule; + } +} diff --git a/JexusManager.Features.Handlers/NewMappingDialog.cs b/JexusManager.Features.Handlers/NewMappingDialog.cs new file mode 100644 index 00000000..c6f88673 --- /dev/null +++ b/JexusManager.Features.Handlers/NewMappingDialog.cs @@ -0,0 +1,142 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using JexusManager.Features.FastCgi; + using JexusManager.Features.Modules; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewMappingDialog : DialogForm + { + private readonly HandlersFeature _feature; + + public NewMappingDialog(IServiceProvider serviceProvider, HandlersItem existing, HandlersFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add Module Mapping" : "Edit Module Mapping"; + txtName.ReadOnly = existing != null; + _feature = feature; + Item = existing ?? new HandlersItem(null); + + var modules = new ModulesFeature((Module)serviceProvider); + modules.Load(); + foreach (var module in modules.Items.Where(module => !module.IsManaged).OrderBy(module => module.Name)) + { + txtModule.Items.Add(module.Name); + } + + if (existing != null) + { + txtExecutable.Text = Item.ScriptProcessor; + txtModule.Text = Item.Modules; + txtName.Text = Item.Name; + txtPath.Text = Item.Path; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.ScriptProcessor = txtExecutable.Text; + Item.Modules = txtModule.Text; + Item.Name = txtName.Text; + Item.Path = txtPath.Text; + if (!txtName.ReadOnly) + { + if (_feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A handler with this name already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + if (txtModule.Text == "FastCgiModule") + { + var result = ShowMessage( + "Do you want to create a FastCGI application for this executable? Click \"Yes\" to add the entry to the FastCGI collection and to enable this executable to run as a FastCGI application.", + MessageBoxButtons.YesNoCancel, + MessageBoxIcon.Information, + MessageBoxDefaultButton.Button1); + if (result == DialogResult.Yes) + { + var fastCgi = new FastCgiFeature((Module)ServiceProvider); + if (fastCgi.Items.All(item => item.Path != txtExecutable.Text)) + { + fastCgi.AddItem(new FastCgiItem(null) { Path = txtExecutable.Text }); + } + } + } + + if (!string.IsNullOrWhiteSpace(txtExecutable.Text) && !File.Exists(txtExecutable.Text)) + { + ShowMessage( + "The specific executable does not exist on the server.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtExecutable, "TextChanged")) + .Merge(Observable.FromEventPattern(txtPath, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtModule.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnRestrictions, "Click") + .Subscribe(evt => + { + var dialog = new RestrictionsDialog(ServiceProvider, Item); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowFileDialog(txtExecutable, "|*.dll||*.exe"); + })); + } + + public HandlersItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210505#Add_Module"); + } + } +} diff --git a/JexusManager.Features.Handlers/NewMappingDialog.resx b/JexusManager.Features.Handlers/NewMappingDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Handlers/NewMappingDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/NewScriptMapDialog.Designer.cs b/JexusManager.Features.Handlers/NewScriptMapDialog.Designer.cs new file mode 100644 index 00000000..03ac8068 --- /dev/null +++ b/JexusManager.Features.Handlers/NewScriptMapDialog.Designer.cs @@ -0,0 +1,182 @@ +namespace JexusManager.Features.Handlers +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewScriptMapDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtExecutable = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.btnRestrictions = new System.Windows.Forms.Button(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(332, 301); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(231, 301); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 156); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(38, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 172); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(269, 20); + this.txtName.TabIndex = 6; + // + // txtExecutable + // + this.txtExecutable.Location = new System.Drawing.Point(15, 96); + this.txtExecutable.Name = "txtExecutable"; + this.txtExecutable.Size = new System.Drawing.Size(367, 20); + this.txtExecutable.TabIndex = 8; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 80); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(63, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Executable:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(74, 13); + this.label3.TabIndex = 11; + this.label3.Text = "Request path:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 25); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(297, 20); + this.txtPath.TabIndex = 12; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 48); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(128, 13); + this.label4.TabIndex = 13; + this.label4.Text = "Example: *.bas, wsvc.axd"; + // + // btnRestrictions + // + this.btnRestrictions.Location = new System.Drawing.Point(15, 216); + this.btnRestrictions.Name = "btnRestrictions"; + this.btnRestrictions.Size = new System.Drawing.Size(140, 23); + this.btnRestrictions.TabIndex = 14; + this.btnRestrictions.Text = "Request Restrictions..."; + this.btnRestrictions.UseVisualStyleBackColor = true; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(388, 94); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(28, 23); + this.btnBrowse.TabIndex = 15; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // NewScriptMapDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(439, 336); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.btnRestrictions); + this.Controls.Add(this.label4); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label3); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtExecutable); + this.Controls.Add(this.txtName); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewScriptMapDialog"; + this.Text = "Add Script Map"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtName; + private TextBox txtExecutable; + private Label label1; + private Label label3; + private TextBox txtPath; + private Label label4; + private Button btnRestrictions; + private Button btnBrowse; + } +} diff --git a/JexusManager.Features.Handlers/NewScriptMapDialog.cs b/JexusManager.Features.Handlers/NewScriptMapDialog.cs new file mode 100644 index 00000000..06a6ecc3 --- /dev/null +++ b/JexusManager.Features.Handlers/NewScriptMapDialog.cs @@ -0,0 +1,101 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewScriptMapDialog : DialogForm + { + private readonly HandlersFeature _feature; + + public NewScriptMapDialog(IServiceProvider serviceProvider, HandlersItem existing, HandlersFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add Script Map" : "Edit Script Map"; + txtName.ReadOnly = existing != null; + _feature = feature; + Item = existing ?? new HandlersItem(null); + if (existing == null) + { + Item.Modules = "IsapiModule"; + } + else + { + txtExecutable.Text = Item.ScriptProcessor; + txtName.Text = Item.Name; + txtPath.Text = Item.Path; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.ScriptProcessor = txtExecutable.Text; + Item.Name = txtName.Text; + Item.Path = txtPath.Text; + if (!txtName.ReadOnly && _feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A handler with this name already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtExecutable, "TextChanged")) + .Merge(Observable.FromEventPattern(txtPath, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtExecutable.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnRestrictions, "Click") + .Subscribe(evt => + { + var dialog = new RestrictionsDialog(ServiceProvider, Item); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowBrowseDialog(txtExecutable); + })); + } + + public HandlersItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210505#Add_ScriptMap"); + } + } +} diff --git a/JexusManager.Features.Handlers/NewScriptMapDialog.resx b/JexusManager.Features.Handlers/NewScriptMapDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Handlers/NewScriptMapDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/NewWildcardDialog.Designer.cs b/JexusManager.Features.Handlers/NewWildcardDialog.Designer.cs new file mode 100644 index 00000000..26c8a218 --- /dev/null +++ b/JexusManager.Features.Handlers/NewWildcardDialog.Designer.cs @@ -0,0 +1,160 @@ +namespace JexusManager.Features.Handlers +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewWildcardDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtExecutable = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(332, 301); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(231, 301); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 156); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(38, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 172); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(269, 20); + this.txtName.TabIndex = 6; + // + // txtExecutable + // + this.txtExecutable.Location = new System.Drawing.Point(15, 96); + this.txtExecutable.Name = "txtExecutable"; + this.txtExecutable.Size = new System.Drawing.Size(367, 20); + this.txtExecutable.TabIndex = 8; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 80); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(63, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Executable:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(74, 13); + this.label3.TabIndex = 11; + this.label3.Text = "Request path:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 25); + this.txtPath.Name = "txtPath"; + this.txtPath.ReadOnly = true; + this.txtPath.Size = new System.Drawing.Size(297, 20); + this.txtPath.TabIndex = 12; + this.txtPath.Text = "*"; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(388, 94); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(28, 23); + this.btnBrowse.TabIndex = 15; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // NewWildcardDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(439, 336); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label3); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtExecutable); + this.Controls.Add(this.txtName); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewWildcardDialog"; + this.Text = "Add Wildcard Script Map"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtName; + private TextBox txtExecutable; + private Label label1; + private Label label3; + private TextBox txtPath; + private Button btnBrowse; + } +} diff --git a/JexusManager.Features.Handlers/NewWildcardDialog.cs b/JexusManager.Features.Handlers/NewWildcardDialog.cs new file mode 100644 index 00000000..be7e48f3 --- /dev/null +++ b/JexusManager.Features.Handlers/NewWildcardDialog.cs @@ -0,0 +1,90 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewWildcardDialog : DialogForm + { + private readonly HandlersFeature _feature; + + public NewWildcardDialog(IServiceProvider serviceProvider, HandlersItem existing, HandlersFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add Wildcard Script Map" : "Edit Wildcard Script Map"; + txtName.ReadOnly = existing != null; + _feature = feature; + Item = existing ?? new HandlersItem(null); + if (existing == null) + { + Item.Modules = "IsapiModule"; + } + else + { + txtExecutable.Text = Item.ScriptProcessor; + txtName.Text = Item.Name; + txtPath.Text = Item.Path; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.ScriptProcessor = txtExecutable.Text; + Item.Name = txtName.Text; + Item.Path = txtPath.Text; + if (!txtName.ReadOnly && _feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A handler with this name already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtExecutable, "TextChanged")) + .Merge(Observable.FromEventPattern(txtPath, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtExecutable.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowBrowseDialog(txtExecutable); + })); + } + + public HandlersItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210505#Add_Wildcard"); + } + } +} diff --git a/JexusManager.Features.Handlers/NewWildcardDialog.resx b/JexusManager.Features.Handlers/NewWildcardDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Handlers/NewWildcardDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/PermissionsDialog.Designer.cs b/JexusManager.Features.Handlers/PermissionsDialog.Designer.cs new file mode 100644 index 00000000..3e393d2d --- /dev/null +++ b/JexusManager.Features.Handlers/PermissionsDialog.Designer.cs @@ -0,0 +1,130 @@ +namespace JexusManager.Features.Handlers +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class PermissionsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.cbRead = new System.Windows.Forms.CheckBox(); + this.cbScript = new System.Windows.Forms.CheckBox(); + this.cbExecute = new System.Windows.Forms.CheckBox(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(65, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Permissions:"; + // + // cbRead + // + this.cbRead.AutoSize = true; + this.cbRead.Location = new System.Drawing.Point(15, 38); + this.cbRead.Name = "cbRead"; + this.cbRead.Size = new System.Drawing.Size(52, 17); + this.cbRead.TabIndex = 1; + this.cbRead.Text = "Read"; + this.cbRead.UseVisualStyleBackColor = true; + // + // cbScript + // + this.cbScript.AutoSize = true; + this.cbScript.Location = new System.Drawing.Point(15, 61); + this.cbScript.Name = "cbScript"; + this.cbScript.Size = new System.Drawing.Size(53, 17); + this.cbScript.TabIndex = 2; + this.cbScript.Text = "Script"; + this.cbScript.UseVisualStyleBackColor = true; + // + // cbExecute + // + this.cbExecute.AutoSize = true; + this.cbExecute.Location = new System.Drawing.Point(37, 84); + this.cbExecute.Name = "cbExecute"; + this.cbExecute.Size = new System.Drawing.Size(65, 17); + this.cbExecute.TabIndex = 3; + this.cbExecute.Text = "Execute"; + this.cbExecute.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(192, 131); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(96, 131); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // PermissionsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(299, 166); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.cbExecute); + this.Controls.Add(this.cbScript); + this.Controls.Add(this.cbRead); + this.Controls.Add(this.label1); + this.Name = "PermissionsDialog"; + this.Text = "Edit Feature Permissions"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.PermissionsDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private CheckBox cbRead; + private CheckBox cbScript; + private CheckBox cbExecute; + private Button btnCancel; + private Button btnOK; + } +} diff --git a/JexusManager.Features.Handlers/PermissionsDialog.cs b/JexusManager.Features.Handlers/PermissionsDialog.cs new file mode 100644 index 00000000..ffc8028b --- /dev/null +++ b/JexusManager.Features.Handlers/PermissionsDialog.cs @@ -0,0 +1,69 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class PermissionsDialog : DialogForm + { + public PermissionsDialog(IServiceProvider serviceProvider, HandlersFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + cbRead.Checked = (feature.AccessPolicy & 1L) == 1L; + cbScript.Checked = (feature.AccessPolicy & 512L) == 512L; + cbExecute.Checked = (feature.AccessPolicy & 4L) == 4L; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (cbRead.Checked) + { + feature.AccessPolicy |= 1L; + } + else + { + feature.AccessPolicy &= ~1L; + } + + if (cbScript.Checked) + { + feature.AccessPolicy |= 512L; + } + else + { + feature.AccessPolicy &= ~512L; + } + + if (cbExecute.Checked) + { + feature.AccessPolicy |= 4L; + } + else + { + feature.AccessPolicy &= ~4L; + } + + DialogResult = DialogResult.OK; + })); + } + + private void PermissionsDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210505"); + } + } +} diff --git a/JexusManager.Features.Handlers/PermissionsDialog.resx b/JexusManager.Features.Handlers/PermissionsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Handlers/PermissionsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/Properties/AssemblyInfo.cs b/JexusManager.Features.Handlers/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5dc76fe3 --- /dev/null +++ b/JexusManager.Features.Handlers/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Handlers")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Handlers")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("bd450763-248d-4117-9af0-51a625667b61")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.Handlers/Properties/Resources.Designer.cs b/JexusManager.Features.Handlers/Properties/Resources.Designer.cs new file mode 100644 index 00000000..8d60a11c --- /dev/null +++ b/JexusManager.Features.Handlers/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Handlers.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Handlers.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap handlers_36 { + get { + object obj = ResourceManager.GetObject("handlers_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Handlers/Properties/Resources.resx b/JexusManager.Features.Handlers/Properties/Resources.resx new file mode 100644 index 00000000..e851bc89 --- /dev/null +++ b/JexusManager.Features.Handlers/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\handlers_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/Resources/handlers_36.png b/JexusManager.Features.Handlers/Resources/handlers_36.png new file mode 100644 index 00000000..b333c93f Binary files /dev/null and b/JexusManager.Features.Handlers/Resources/handlers_36.png differ diff --git a/JexusManager.Features.Handlers/RestrictionsDialog.Designer.cs b/JexusManager.Features.Handlers/RestrictionsDialog.Designer.cs new file mode 100644 index 00000000..e4ca165c --- /dev/null +++ b/JexusManager.Features.Handlers/RestrictionsDialog.Designer.cs @@ -0,0 +1,342 @@ +namespace JexusManager.Features.Handlers +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class RestrictionsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.rbEither = new System.Windows.Forms.RadioButton(); + this.rbFolder = new System.Windows.Forms.RadioButton(); + this.rbFile = new System.Windows.Forms.RadioButton(); + this.cbInvoke = new System.Windows.Forms.CheckBox(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.label2 = new System.Windows.Forms.Label(); + this.txtVerbs = new System.Windows.Forms.TextBox(); + this.rbSelectedVerbs = new System.Windows.Forms.RadioButton(); + this.rbAllVerbs = new System.Windows.Forms.RadioButton(); + this.label1 = new System.Windows.Forms.Label(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.rbExecute = new System.Windows.Forms.RadioButton(); + this.rbScript = new System.Windows.Forms.RadioButton(); + this.rbWrite = new System.Windows.Forms.RadioButton(); + this.rbRead = new System.Windows.Forms.RadioButton(); + this.label3 = new System.Windows.Forms.Label(); + this.rbNone = new System.Windows.Forms.RadioButton(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(332, 301); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(236, 301); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.Location = new System.Drawing.Point(12, 12); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(415, 283); + this.tabControl1.TabIndex = 2; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.rbEither); + this.tabPage1.Controls.Add(this.rbFolder); + this.tabPage1.Controls.Add(this.rbFile); + this.tabPage1.Controls.Add(this.cbInvoke); + 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(407, 257); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Mapping"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // rbEither + // + this.rbEither.AutoSize = true; + this.rbEither.Enabled = false; + this.rbEither.Location = new System.Drawing.Point(44, 110); + this.rbEither.Name = "rbEither"; + this.rbEither.Size = new System.Drawing.Size(82, 17); + this.rbEither.TabIndex = 3; + this.rbEither.TabStop = true; + this.rbEither.Text = "File or folder"; + this.rbEither.UseVisualStyleBackColor = true; + // + // rbFolder + // + this.rbFolder.AutoSize = true; + this.rbFolder.Enabled = false; + this.rbFolder.Location = new System.Drawing.Point(44, 73); + this.rbFolder.Name = "rbFolder"; + this.rbFolder.Size = new System.Drawing.Size(54, 17); + this.rbFolder.TabIndex = 2; + this.rbFolder.TabStop = true; + this.rbFolder.Text = "Folder"; + this.rbFolder.UseVisualStyleBackColor = true; + // + // rbFile + // + this.rbFile.AutoSize = true; + this.rbFile.Enabled = false; + this.rbFile.Location = new System.Drawing.Point(44, 40); + this.rbFile.Name = "rbFile"; + this.rbFile.Size = new System.Drawing.Size(41, 17); + this.rbFile.TabIndex = 1; + this.rbFile.TabStop = true; + this.rbFile.Text = "File"; + this.rbFile.UseVisualStyleBackColor = true; + // + // cbInvoke + // + this.cbInvoke.AutoSize = true; + this.cbInvoke.Location = new System.Drawing.Point(23, 17); + this.cbInvoke.Name = "cbInvoke"; + this.cbInvoke.Size = new System.Drawing.Size(231, 17); + this.cbInvoke.TabIndex = 0; + this.cbInvoke.Text = "Invoke handler only if request is mapped to:"; + this.cbInvoke.UseVisualStyleBackColor = true; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.label2); + this.tabPage2.Controls.Add(this.txtVerbs); + this.tabPage2.Controls.Add(this.rbSelectedVerbs); + this.tabPage2.Controls.Add(this.rbAllVerbs); + this.tabPage2.Controls.Add(this.label1); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(407, 257); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Verbs"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(49, 137); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(110, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Example: GET, POST"; + // + // txtVerbs + // + this.txtVerbs.Enabled = false; + this.txtVerbs.Location = new System.Drawing.Point(52, 114); + this.txtVerbs.Name = "txtVerbs"; + this.txtVerbs.Size = new System.Drawing.Size(241, 20); + this.txtVerbs.TabIndex = 3; + // + // rbSelectedVerbs + // + this.rbSelectedVerbs.AutoSize = true; + this.rbSelectedVerbs.Location = new System.Drawing.Point(34, 82); + this.rbSelectedVerbs.Name = "rbSelectedVerbs"; + this.rbSelectedVerbs.Size = new System.Drawing.Size(151, 17); + this.rbSelectedVerbs.TabIndex = 2; + this.rbSelectedVerbs.TabStop = true; + this.rbSelectedVerbs.Text = "One of the following verbs:"; + this.rbSelectedVerbs.UseVisualStyleBackColor = true; + // + // rbAllVerbs + // + this.rbAllVerbs.AutoSize = true; + this.rbAllVerbs.Location = new System.Drawing.Point(34, 47); + this.rbAllVerbs.Name = "rbAllVerbs"; + this.rbAllVerbs.Size = new System.Drawing.Size(65, 17); + this.rbAllVerbs.TabIndex = 1; + this.rbAllVerbs.TabStop = true; + this.rbAllVerbs.Text = "All verbs"; + this.rbAllVerbs.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(31, 21); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(160, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Specify the verbs to be handled:"; + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.rbExecute); + this.tabPage3.Controls.Add(this.rbScript); + this.tabPage3.Controls.Add(this.rbWrite); + this.tabPage3.Controls.Add(this.rbRead); + this.tabPage3.Controls.Add(this.label3); + this.tabPage3.Controls.Add(this.rbNone); + this.tabPage3.Location = new System.Drawing.Point(4, 22); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Padding = new System.Windows.Forms.Padding(3); + this.tabPage3.Size = new System.Drawing.Size(407, 257); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "Access"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // rbExecute + // + this.rbExecute.AutoSize = true; + this.rbExecute.Location = new System.Drawing.Point(47, 138); + this.rbExecute.Name = "rbExecute"; + this.rbExecute.Size = new System.Drawing.Size(64, 17); + this.rbExecute.TabIndex = 5; + this.rbExecute.TabStop = true; + this.rbExecute.Text = "Execute"; + this.rbExecute.UseVisualStyleBackColor = true; + // + // rbScript + // + this.rbScript.AutoSize = true; + this.rbScript.Location = new System.Drawing.Point(47, 115); + this.rbScript.Name = "rbScript"; + this.rbScript.Size = new System.Drawing.Size(52, 17); + this.rbScript.TabIndex = 4; + this.rbScript.TabStop = true; + this.rbScript.Text = "Script"; + this.rbScript.UseVisualStyleBackColor = true; + // + // rbWrite + // + this.rbWrite.AutoSize = true; + this.rbWrite.Location = new System.Drawing.Point(47, 92); + this.rbWrite.Name = "rbWrite"; + this.rbWrite.Size = new System.Drawing.Size(50, 17); + this.rbWrite.TabIndex = 3; + this.rbWrite.TabStop = true; + this.rbWrite.Text = "Write"; + this.rbWrite.UseVisualStyleBackColor = true; + // + // rbRead + // + this.rbRead.AutoSize = true; + this.rbRead.Location = new System.Drawing.Point(47, 69); + this.rbRead.Name = "rbRead"; + this.rbRead.Size = new System.Drawing.Size(51, 17); + this.rbRead.TabIndex = 2; + this.rbRead.TabStop = true; + this.rbRead.Text = "Read"; + this.rbRead.UseVisualStyleBackColor = true; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(34, 16); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(211, 13); + this.label3.TabIndex = 1; + this.label3.Text = "Specify the access required by the handler:"; + // + // rbNone + // + this.rbNone.AutoSize = true; + this.rbNone.Location = new System.Drawing.Point(47, 46); + this.rbNone.Name = "rbNone"; + this.rbNone.Size = new System.Drawing.Size(51, 17); + this.rbNone.TabIndex = 0; + this.rbNone.TabStop = true; + this.rbNone.Text = "None"; + this.rbNone.UseVisualStyleBackColor = true; + // + // RestrictionsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(439, 336); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "RestrictionsDialog"; + this.Text = "Request Restrictions"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.RestrictionsDialogHelpButtonClicked); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.tabPage3.ResumeLayout(false); + this.tabPage3.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private TabControl tabControl1; + private TabPage tabPage1; + private RadioButton rbEither; + private RadioButton rbFolder; + private RadioButton rbFile; + private CheckBox cbInvoke; + private TabPage tabPage2; + private Label label2; + private TextBox txtVerbs; + private RadioButton rbSelectedVerbs; + private RadioButton rbAllVerbs; + private Label label1; + private TabPage tabPage3; + private RadioButton rbExecute; + private RadioButton rbScript; + private RadioButton rbWrite; + private RadioButton rbRead; + private Label label3; + private RadioButton rbNone; + } +} diff --git a/JexusManager.Features.Handlers/RestrictionsDialog.cs b/JexusManager.Features.Handlers/RestrictionsDialog.cs new file mode 100644 index 00000000..88ab3b98 --- /dev/null +++ b/JexusManager.Features.Handlers/RestrictionsDialog.cs @@ -0,0 +1,119 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Handlers +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class RestrictionsDialog : DialogForm + { + public RestrictionsDialog(IServiceProvider serviceProvider, HandlersItem item) + : base(serviceProvider) + { + InitializeComponent(); + cbInvoke.Checked = item.ResourceType != 3L; + rbFile.Checked = item.ResourceType == 0L; + rbFolder.Checked = item.ResourceType == 1L; + rbEither.Checked = item.ResourceType == 2L; + + rbNone.Checked = item.RequireAccess == 0L; + rbRead.Checked = item.RequireAccess == 1L; + rbWrite.Checked = item.RequireAccess == 2L; + rbScript.Checked = item.RequireAccess == 3L; + rbExecute.Checked = item.RequireAccess == 4L; + + rbAllVerbs.Checked = item.Verb == "*"; + rbSelectedVerbs.Checked = !rbAllVerbs.Checked; + if (rbSelectedVerbs.Checked) + { + txtVerbs.Text = item.Verb; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (!cbInvoke.Checked) + { + item.ResourceType = 3L; + } + else if (rbFile.Checked) + { + item.ResourceType = 0L; + } + else if (rbFolder.Checked) + { + item.ResourceType = 1L; + } + else if (rbEither.Checked) + { + item.ResourceType = 2L; + } + + if (rbNone.Checked) + { + item.RequireAccess = 0L; + } + else if (rbRead.Checked) + { + item.RequireAccess = 1L; + } + else if (rbWrite.Checked) + { + item.RequireAccess = 2L; + } + else if (rbScript.Checked) + { + item.RequireAccess = 3L; + } + else if (rbExecute.Checked) + { + item.RequireAccess = 4L; + } + + if (rbAllVerbs.Checked) + { + item.Verb = "*"; + } + else if (rbSelectedVerbs.Checked) + { + item.Verb = txtVerbs.Text; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(cbInvoke, "CheckedChanged") + .Subscribe(evt => + { + rbFile.Enabled = rbFolder.Enabled = rbEither.Enabled = cbInvoke.Checked; + })); + + container.Add( + Observable.FromEventPattern(rbSelectedVerbs, "CheckedChanged") + .Merge(Observable.FromEventPattern(rbAllVerbs, "CheckedChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + txtVerbs.Enabled = rbSelectedVerbs.Checked; + })); + } + + private void RestrictionsDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210505#Request_Restrictions"); + } + } +} diff --git a/JexusManager.Features.Handlers/RestrictionsDialog.resx b/JexusManager.Features.Handlers/RestrictionsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Handlers/RestrictionsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Handlers/packages.config b/JexusManager.Features.Handlers/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.Handlers/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.HttpApi/HttpApiFeature.cs b/JexusManager.Features.HttpApi/HttpApiFeature.cs new file mode 100644 index 00000000..73812ee4 --- /dev/null +++ b/JexusManager.Features.HttpApi/HttpApiFeature.cs @@ -0,0 +1,45 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Web.Management.Client; +using System; + +namespace JexusManager.Features.HttpApi +{ + internal abstract class HttpApiFeature : FeatureBase, IHttpApiFeature + where T : class, IItem + { + protected HttpApiFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + public abstract TaskList GetTaskList(); + + public abstract void Load(); + + protected override void OnSettingsSaved() + { + this.HttpApiSettingsUpdate?.Invoke(); + } + + public abstract bool ShowHelp(); + + public HttpApiSettingsSavedEventHandler HttpApiSettingsUpdate { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public abstract string Name { get; } + } +} diff --git a/JexusManager.Features.HttpApi/HttpApiModule.cs b/JexusManager.Features.HttpApi/HttpApiModule.cs new file mode 100644 index 00000000..ef3a9be1 --- /dev/null +++ b/JexusManager.Features.HttpApi/HttpApiModule.cs @@ -0,0 +1,25 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpApi +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class HttpApiModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(HttpApiPage), "HTTP API", + "Manage certificate bindings for websites that use SSL", Resources.http_36, + Resources.http_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.HttpApi/HttpApiModuleProvider.cs b/JexusManager.Features.HttpApi/HttpApiModuleProvider.cs new file mode 100644 index 00000000..44bd51ca --- /dev/null +++ b/JexusManager.Features.HttpApi/HttpApiModuleProvider.cs @@ -0,0 +1,29 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpApi +{ + using System; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Server; + + public class HttpApiModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(HttpApiModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return scope == ManagementScope.Server && !Helper.IsRunningOnMono(); + } + } +} diff --git a/JexusManager.Features.HttpApi/HttpApiPage.Designer.cs b/JexusManager.Features.HttpApi/HttpApiPage.Designer.cs new file mode 100644 index 00000000..0f23fe7c --- /dev/null +++ b/JexusManager.Features.HttpApi/HttpApiPage.Designer.cs @@ -0,0 +1,437 @@ +namespace JexusManager.Features.HttpApi +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class HttpApiPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tpIP = new System.Windows.Forms.TabPage(); + this.lvIP = new System.Windows.Forms.ListView(); + this.chAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chPort = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chApp = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chAppId = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chHash = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chStore = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpSNI = new System.Windows.Forms.TabPage(); + this.lvSni = new System.Windows.Forms.ListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpURL = new System.Windows.Forms.TabPage(); + this.lvURL = new System.Windows.Forms.ListView(); + this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader10 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tpIP.SuspendLayout(); + this.tpSNI.SuspendLayout(); + this.tpURL.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitContainer1_SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "contextMenuStrip1"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.tabControl1); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 9; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tpIP); + this.tabControl1.Controls.Add(this.tpSNI); + this.tabControl1.Controls.Add(this.tpURL); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.Location = new System.Drawing.Point(0, 13); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(560, 527); + this.tabControl1.TabIndex = 2; + this.tabControl1.SelectedIndexChanged += new System.EventHandler(this.TabControl1SelectedIndexChanged); + // + // tpIP + // + this.tpIP.Controls.Add(this.lvIP); + this.tpIP.Location = new System.Drawing.Point(4, 22); + this.tpIP.Name = "tpIP"; + this.tpIP.Padding = new System.Windows.Forms.Padding(3); + this.tpIP.Size = new System.Drawing.Size(552, 501); + this.tpIP.TabIndex = 0; + this.tpIP.Text = "IP Based"; + this.tpIP.UseVisualStyleBackColor = true; + // + // lvIP + // + this.lvIP.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chAddress, + this.chPort, + this.chApp, + this.chAppId, + this.chHash, + this.chStore}); + this.lvIP.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvIP.FullRowSelect = true; + this.lvIP.HideSelection = false; + this.lvIP.Location = new System.Drawing.Point(3, 3); + this.lvIP.Margin = new System.Windows.Forms.Padding(5); + this.lvIP.MultiSelect = false; + this.lvIP.Name = "lvIP"; + this.lvIP.Size = new System.Drawing.Size(546, 495); + this.lvIP.TabIndex = 1; + this.lvIP.UseCompatibleStateImageBehavior = false; + this.lvIP.View = System.Windows.Forms.View.Details; + this.lvIP.SelectedIndexChanged += new System.EventHandler(this.LvIPSelectedIndexChanged); + // + // chAddress + // + this.chAddress.Text = "Address"; + this.chAddress.Width = 180; + // + // chPort + // + this.chPort.Text = "Port"; + this.chPort.Width = 80; + // + // chApp + // + this.chApp.Text = "Application Name"; + this.chApp.Width = 100; + // + // chAppId + // + this.chAppId.Text = "Application ID"; + this.chAppId.Width = 180; + // + // chHash + // + this.chHash.Text = "Certificate Hash"; + this.chHash.Width = 180; + // + // chStore + // + this.chStore.Text = "Certificate Store"; + this.chStore.Width = 120; + // + // tpSNI + // + this.tpSNI.Controls.Add(this.lvSni); + this.tpSNI.Location = new System.Drawing.Point(4, 22); + this.tpSNI.Name = "tpSNI"; + this.tpSNI.Padding = new System.Windows.Forms.Padding(3); + this.tpSNI.Size = new System.Drawing.Size(552, 501); + this.tpSNI.TabIndex = 1; + this.tpSNI.Text = "SNI"; + this.tpSNI.UseVisualStyleBackColor = true; + // + // lvSni + // + this.lvSni.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2, + this.columnHeader3, + this.columnHeader4, + this.columnHeader5, + this.columnHeader6}); + this.lvSni.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvSni.FullRowSelect = true; + this.lvSni.HideSelection = false; + this.lvSni.Location = new System.Drawing.Point(3, 3); + this.lvSni.Margin = new System.Windows.Forms.Padding(5); + this.lvSni.MultiSelect = false; + this.lvSni.Name = "lvSni"; + this.lvSni.Size = new System.Drawing.Size(546, 495); + this.lvSni.TabIndex = 2; + this.lvSni.UseCompatibleStateImageBehavior = false; + this.lvSni.View = System.Windows.Forms.View.Details; + this.lvSni.SelectedIndexChanged += new System.EventHandler(this.LvSniSelectedIndexChanged); + // + // columnHeader1 + // + this.columnHeader1.Text = "Host Name"; + this.columnHeader1.Width = 180; + // + // columnHeader2 + // + this.columnHeader2.Text = "Port"; + this.columnHeader2.Width = 80; + // + // columnHeader3 + // + this.columnHeader3.Text = "Application Name"; + this.columnHeader3.Width = 100; + // + // columnHeader4 + // + this.columnHeader4.Text = "Application ID"; + this.columnHeader4.Width = 180; + // + // columnHeader5 + // + this.columnHeader5.Text = "Certificate Hash"; + this.columnHeader5.Width = 180; + // + // columnHeader6 + // + this.columnHeader6.Text = "Certificate Store"; + this.columnHeader6.Width = 120; + // + // tpURL + // + this.tpURL.Controls.Add(this.lvURL); + this.tpURL.Location = new System.Drawing.Point(4, 22); + this.tpURL.Name = "tpURL"; + this.tpURL.Padding = new System.Windows.Forms.Padding(3); + this.tpURL.Size = new System.Drawing.Size(552, 501); + this.tpURL.TabIndex = 2; + this.tpURL.Text = "Reserved URL"; + this.tpURL.UseVisualStyleBackColor = true; + // + // lvURL + // + this.lvURL.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader7, + this.columnHeader10}); + this.lvURL.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvURL.FullRowSelect = true; + this.lvURL.HideSelection = false; + this.lvURL.Location = new System.Drawing.Point(3, 3); + this.lvURL.Margin = new System.Windows.Forms.Padding(5); + this.lvURL.MultiSelect = false; + this.lvURL.Name = "lvURL"; + this.lvURL.Size = new System.Drawing.Size(546, 495); + this.lvURL.TabIndex = 2; + this.lvURL.UseCompatibleStateImageBehavior = false; + this.lvURL.View = System.Windows.Forms.View.Details; + this.lvURL.SelectedIndexChanged += new System.EventHandler(this.LvURLSelectedIndexChanged); + // + // columnHeader7 + // + this.columnHeader7.Text = "URL Prefix"; + this.columnHeader7.Width = 180; + // + // columnHeader10 + // + this.columnHeader10.Text = "Security Descriptor"; + this.columnHeader10.Width = 180; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(523, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to manage certificate bindings that the Web server can use with " + + "websites configured for SSL."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 8; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(106, 25); + this.label3.TabIndex = 2; + this.label3.Text = "HTTP API"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // HttpApiPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "HttpApiPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.tabControl1.ResumeLayout(false); + this.tpIP.ResumeLayout(false); + this.tpSNI.ResumeLayout(false); + this.tpURL.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private ContextMenuStrip cmsActionPanel; + private PictureBox pictureBox1; + private Panel panel2; + private TabControl tabControl1; + private TabPage tpIP; + private ListView lvIP; + private ColumnHeader chAddress; + private ColumnHeader chPort; + private ColumnHeader chApp; + private ColumnHeader chAppId; + private ColumnHeader chHash; + private ColumnHeader chStore; + private TabPage tpSNI; + private TabPage tpURL; + private ListView lvSni; + private ColumnHeader columnHeader1; + private ColumnHeader columnHeader2; + private ColumnHeader columnHeader3; + private ColumnHeader columnHeader4; + private ColumnHeader columnHeader5; + private ColumnHeader columnHeader6; + private ListView lvURL; + private ColumnHeader columnHeader7; + private ColumnHeader columnHeader10; + } +} diff --git a/JexusManager.Features.HttpApi/HttpApiPage.cs b/JexusManager.Features.HttpApi/HttpApiPage.cs new file mode 100644 index 00000000..fcdc6267 --- /dev/null +++ b/JexusManager.Features.HttpApi/HttpApiPage.cs @@ -0,0 +1,277 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpApi +{ + using System; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class HttpApiPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly HttpApiPage _owner; + + public PageTaskList(HttpApiPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class SniMappingListViewItem : ListViewItem + { + public SniMappingItem Item { get; } + private readonly HttpApiPage _page; + + public SniMappingListViewItem(SniMappingItem item, HttpApiPage page) + : base(item.Host) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Port)); + this.SubItems.Add(new ListViewSubItem(this, BindingExtensions.GetAppName(item.AppId))); + this.SubItems.Add(new ListViewSubItem(this, item.AppId)); + this.SubItems.Add(new ListViewSubItem(this, item.Hash)); + this.SubItems.Add(new ListViewSubItem(this, item.Store)); + } + } + + private sealed class IpMappingListViewItem : ListViewItem + { + public IpMappingItem Item { get; } + private readonly HttpApiPage _page; + + public IpMappingListViewItem(IpMappingItem item, HttpApiPage page) + : base(item.Address) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Port)); + this.SubItems.Add(new ListViewSubItem(this, BindingExtensions.GetAppName(item.AppId))); + this.SubItems.Add(new ListViewSubItem(this, item.AppId)); + this.SubItems.Add(new ListViewSubItem(this, item.Hash)); + this.SubItems.Add(new ListViewSubItem(this, item.Store)); + } + } + + private sealed class ReservedUrlListViewItem : ListViewItem + { + public ReservedUrlsItem Item { get; } + private readonly HttpApiPage _page; + + public ReservedUrlListViewItem(ReservedUrlsItem item, HttpApiPage page) + : base(item.UrlPrefix) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.SecurityDescriptor)); + } + } + + private PageTaskList _taskList; + + public HttpApiPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + var ip = new IpMappingFeature(Module); + ip.HttpApiSettingsUpdate = RefreshIp; + ip.Load(); + tpIP.Tag = ip; + RefreshIp(); + + var sni = new SniMappingFeature(Module); + sni.HttpApiSettingsUpdate = RefreshSni; + sni.Load(); + tpSNI.Tag = sni; + + var url = new ReservedUrlsFeature(Module); + url.HttpApiSettingsUpdate = RefreshUrl; + url.Load(); + tpURL.Tag = url; + } + + private void RefreshSni() + { + var feature = (SniMappingFeature)tpSNI.Tag; + if (feature == null) + { + return; + } + + lvSni.Items.Clear(); + foreach (var file in feature.Items) + { + lvSni.Items.Add(new SniMappingListViewItem(file, this)); + } + + if (feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (SniMappingListViewItem item in lvSni.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshIp() + { + var feature = (IpMappingFeature)tpIP.Tag; + if (feature == null) + { + return; + } + + lvIP.Items.Clear(); + foreach (var file in feature.Items) + { + lvIP.Items.Add(new IpMappingListViewItem(file, this)); + } + + if (feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (IpMappingListViewItem item in lvIP.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshUrl() + { + var feature = (ReservedUrlsFeature)tpURL.Tag; + if (feature == null) + { + return; + } + + lvURL.Items.Clear(); + foreach (var file in feature.Items) + { + lvURL.Items.Add(new ReservedUrlListViewItem(file, this)); + } + + if (feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (ReservedUrlListViewItem item in lvURL.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void InitializeListPage() + { + } + + protected override void Refresh() + { + var feature = (IHttpApiFeature)tabControl1.SelectedTab.Tag; + var extra = feature?.GetTaskList(); + this.Tasks.Fill(tsActionPanel, cmsActionPanel, extra); + base.Refresh(); + } + + private void LvSniSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (SniMappingFeature)tpSNI.Tag; + feature.SelectedItem = lvSni.SelectedItems.Count > 0 + ? ((SniMappingListViewItem)lvSni.SelectedItems[0]).Item + : null; + this.Refresh(); + } + + private void LvIPSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (IpMappingFeature)tpIP.Tag; + feature.SelectedItem = lvIP.SelectedItems.Count > 0 + ? ((IpMappingListViewItem)lvIP.SelectedItems[0]).Item + : null; + this.Refresh(); + } + + private void LvURLSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (ReservedUrlsFeature)tpURL.Tag; + feature.SelectedItem = lvURL.SelectedItems.Count > 0 + ? ((ReservedUrlListViewItem)lvURL.SelectedItems[0]).Item + : null; + this.Refresh(); + } + + protected override bool ShowHelp() + { + var feature = (IHttpApiFeature)tabControl1.SelectedTab.Tag; + feature.ShowHelp(); + return true; + } + + private void TabControl1SelectedIndexChanged(object sender, EventArgs e) + { + var feature = (IHttpApiFeature)tabControl1.SelectedTab.Tag; + feature.HttpApiSettingsUpdate.Invoke(); + Refresh(); + } + + private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.HttpApi/HttpApiPage.resx b/JexusManager.Features.HttpApi/HttpApiPage.resx new file mode 100644 index 00000000..8d1dcaa6 --- /dev/null +++ b/JexusManager.Features.HttpApi/HttpApiPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 122, 17 + + + 17, 17 + + + 179 + + \ No newline at end of file diff --git a/JexusManager.Features.HttpApi/HttpApiSettingsSavedEventHandler.cs b/JexusManager.Features.HttpApi/HttpApiSettingsSavedEventHandler.cs new file mode 100644 index 00000000..5505a450 --- /dev/null +++ b/JexusManager.Features.HttpApi/HttpApiSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpApi +{ + public delegate void HttpApiSettingsSavedEventHandler(); +} diff --git a/JexusManager.Features.HttpApi/IHttpApiFeature.cs b/JexusManager.Features.HttpApi/IHttpApiFeature.cs new file mode 100644 index 00000000..663535b3 --- /dev/null +++ b/JexusManager.Features.HttpApi/IHttpApiFeature.cs @@ -0,0 +1,17 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Web.Management.Client; + +namespace JexusManager.Features.HttpApi +{ + internal interface IHttpApiFeature + { + TaskList GetTaskList(); + + bool ShowHelp(); + + HttpApiSettingsSavedEventHandler HttpApiSettingsUpdate { get; } + } +} diff --git a/JexusManager.Features.HttpApi/IpMappingFeature.cs b/JexusManager.Features.HttpApi/IpMappingFeature.cs new file mode 100644 index 00000000..e20910b0 --- /dev/null +++ b/JexusManager.Features.HttpApi/IpMappingFeature.cs @@ -0,0 +1,304 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using JexusManager.Services; +using Microsoft.Web.Administration; +using Microsoft.Web.Management.Client; +using Microsoft.Web.Management.Client.Win32; +using Org.BouncyCastle.Utilities.Encoders; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Windows.Forms; + +namespace JexusManager.Features.HttpApi +{ + internal class IpMappingFeature : HttpApiFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly IpMappingFeature _owner; + + public FeatureTaskList(IpMappingFeature owner) + { + _owner = owner; + } + + private const string LocalhostIssuer = "CN=localhost"; + private readonly string _localMachineIssuer = string.Format("CN={0}", Environment.MachineName); + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + //result.Add(new MethodTaskItem("Import", "Import...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateRequest", "Create Certificate Request...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("Complete", "Complete Certificate Request...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateDomain", "Create Domain Certificate...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateSelf", "Create Self-Signed Certificate...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("View", "View...", string.Empty).SetUsage()); + //if (_owner.SelectedItem.Certificate.HasPrivateKey) + //{ + // try + // { + // var keyInfo = (RSACryptoServiceProvider)_owner.SelectedItem.Certificate.PrivateKey; + // if (keyInfo.CspKeyContainerInfo.Exportable) + // { + // result.Add(new MethodTaskItem("Export", "Export...", string.Empty).SetUsage()); + // if (_owner.SelectedItem.Certificate.Issuer != LocalhostIssuer && _owner.SelectedItem.Certificate.Issuer != _localMachineIssuer) + // { + // result.Add(new MethodTaskItem("Renew", "Renew...", string.Empty).SetUsage()); + // } + // } + // } + // catch (CryptographicException ex) + // { + // if (ex.HResult != -2146893802) + // { + // throw; + // } + // } + //} + + //result.Add(new MethodTaskItem("Remove", "Remove", string.Empty, string.Empty, Resources.remove_16).SetUsage()); + } + + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //if (!this._owner.AutomicRebindEnabled) + //{ + // result.Add(new MethodTaskItem("Enable", "Enable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + //} + + //if (this._owner.AutomicRebindEnabled) + //{ + // result.Add(new MethodTaskItem("Disable", "Disable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + //} + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Import() + { + _owner.Import(); + } + + [Obfuscation(Exclude = true)] + public void View() + { + _owner.View(); + } + + [Obfuscation(Exclude = true)] + public void Export() + { + _owner.Export(); + } + + [Obfuscation(Exclude = true)] + public void Renew() + { + _owner.Renew(); + } + + [Obfuscation(Exclude = true)] + public void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void CreateRequest() + { + _owner.CreateRequest(); + } + + [Obfuscation(Exclude = true)] + public void Complete() + { + _owner.Complete(); + } + + [Obfuscation(Exclude = true)] + public void CreateDomain() + { + _owner.CreateDomain(); + } + + [Obfuscation(Exclude = true)] + public void CreateSelf() + { + _owner.CreateSelf(); + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + } + + public IpMappingFeature(Microsoft.Web.Management.Client.Module module) + : base(module) + { + } + + private FeatureTaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + this.Items = new List(); + var ipMappings = Microsoft.Web.Administration.NativeMethods.QuerySslCertificateInfo(); + foreach (var mapping in ipMappings) + { + this.Items.Add(new IpMappingItem(mapping.IpPort.Address.ToString(), mapping.IpPort.Port.ToString(), mapping.AppId.ToString(), Hex.ToHexString(mapping.Hash), mapping.StoreName, this)); + } + + this.OnHttpApiSettingsSaved(); + } + + public void Import() + { + //var dialog = new ImportCertificateDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove this certificate, and permanently remove it from the certificate store?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != + DialogResult.Yes) + { + return; + } + + // remove certificate and mapping + //using (var process = new Process()) + //{ + // var start = process.StartInfo; + // start.Verb = "runas"; + // start.FileName = "cmd"; + // start.Arguments = string.Format( + // "/c \"\"{2}\" /h:\"{0}\" /s:{1}\"", + // SelectedItem.Certificate.Thumbprint, + // SelectedItem.Store == "Personal" ? "MY" : "WebHosting", + // Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")); + // start.CreateNoWindow = true; + // start.WindowStyle = ProcessWindowStyle.Hidden; + // process.Start(); + // process.WaitForExit(); + + // if (process.ExitCode == 0) + // { + // Items.Remove(SelectedItem); + // SelectedItem = null; + // this.OnHttpApiSettingsSaved(); + // } + //} + } + + protected void OnHttpApiSettingsSaved() + { + this.HttpApiSettingsUpdated?.Invoke(); + } + + public override bool ShowHelp() + { + Process.Start("https://msdn.microsoft.com/en-us/library/windows/desktop/cc307243(v=vs.85).aspx"); + return false; + } + + private void Disable() + { + } + + private void Enable() + { + } + + private void CreateSelf() + { + //var dialog = new SelfCertificateDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + private void CreateDomain() + { + } + + private void Complete() + { + //var dialog = new CompleteRequestDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + private void CreateRequest() + { + //var wizard = new CertificateRequestWizard(this.Module); + //wizard.ShowDialog(); + } + + private void Export() + { + //var dialog = new ExportCertificateDialog(SelectedItem.Certificate, Module); + //dialog.ShowDialog(); + } + + private void Renew() + { + //var wizard = new CertificateRenewWizard(SelectedItem.Certificate, Module); + //wizard.ShowDialog(); + } + + private void View() + { + } + + public bool AutomicRebindEnabled { get; set; } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + return null; + } + + public HttpApiSettingsSavedEventHandler HttpApiSettingsUpdated { get; set; } + + public override string Name + { + get + { + return "IP Mappings"; + } + } + } +} diff --git a/JexusManager.Features.HttpApi/IpMappingItem.cs b/JexusManager.Features.HttpApi/IpMappingItem.cs new file mode 100644 index 00000000..2ccddea4 --- /dev/null +++ b/JexusManager.Features.HttpApi/IpMappingItem.cs @@ -0,0 +1,45 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Web.Administration; + +namespace JexusManager.Features.HttpApi +{ + internal class IpMappingItem : IItem + { + public IpMappingItem(string address, string port, string appId, string hash, string store, IpMappingFeature feature) + { + this.Address = address; + this.Port = port; + this.AppId = appId; + this.Hash = hash; + this.Store = store; + this.Feature = feature; + } + + public string Hash { get; set; } + public string Address { get; set; } + public string Port { get; set; } + public string AppId { get; set; } + public string Store { get; set; } + public IpMappingFeature Feature { get; private set; } + + public string Flag { get; set; } + public ConfigurationElement Element { get; set; } + + public void Apply() + { } + + public bool Equals(IpMappingItem other) + { + return Match(other); + } + + public bool Match(IpMappingItem other) + { + return other != null && other.Hash == this.Hash && other.Store == this.Store && other.Address == this.Address + && other.Port == this.Port && other.AppId == this.AppId; + } + } +} diff --git a/JexusManager.Features.HttpApi/JexusManager.Features.HttpApi.csproj b/JexusManager.Features.HttpApi/JexusManager.Features.HttpApi.csproj new file mode 100644 index 00000000..8ce2c1c8 --- /dev/null +++ b/JexusManager.Features.HttpApi/JexusManager.Features.HttpApi.csproj @@ -0,0 +1,123 @@ + + + + + Debug + AnyCPU + {2B03E7FE-3DBD-4914-AD8C-4CB7007A9EED} + Library + Properties + JexusManager.Features.HttpApi + JexusManager.Features.HttpApi + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll + True + + + + + + + + + + + + + + + Helper.cs + + + NativeMethods.cs + + + + + + + + + + + + + UserControl + + + HttpApiPage.cs + + + + + True + True + Resources.resx + + + + + HttpApiPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.HttpApi/JexusManager.snk b/JexusManager.Features.HttpApi/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.HttpApi/JexusManager.snk differ diff --git a/JexusManager.Features.HttpApi/Properties/AssemblyInfo.cs b/JexusManager.Features.HttpApi/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..2df47ee7 --- /dev/null +++ b/JexusManager.Features.HttpApi/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.HttpApi")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.HttpApi")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("2b03e7fe-3dbd-4914-ad8c-4cb7007a9eed")] + +// 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")] diff --git a/JexusManager.Features.HttpApi/Properties/Resources.Designer.cs b/JexusManager.Features.HttpApi/Properties/Resources.Designer.cs new file mode 100644 index 00000000..fd6c5b9c --- /dev/null +++ b/JexusManager.Features.HttpApi/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.HttpApi.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.HttpApi.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap http_36 { + get { + object obj = ResourceManager.GetObject("http_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.HttpApi/Properties/Resources.resx b/JexusManager.Features.HttpApi/Properties/Resources.resx new file mode 100644 index 00000000..bbdff26a --- /dev/null +++ b/JexusManager.Features.HttpApi/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\http_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.HttpApi/ReservedUrlsFeature.cs b/JexusManager.Features.HttpApi/ReservedUrlsFeature.cs new file mode 100644 index 00000000..c8186f0b --- /dev/null +++ b/JexusManager.Features.HttpApi/ReservedUrlsFeature.cs @@ -0,0 +1,303 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using JexusManager.Services; +using Microsoft.Web.Administration; +using Microsoft.Web.Management.Client; +using Microsoft.Web.Management.Client.Win32; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Windows.Forms; + +namespace JexusManager.Features.HttpApi +{ + internal class ReservedUrlsFeature : HttpApiFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly ReservedUrlsFeature _owner; + + public FeatureTaskList(ReservedUrlsFeature owner) + { + _owner = owner; + } + + private const string LocalhostIssuer = "CN=localhost"; + private readonly string _localMachineIssuer = string.Format("CN={0}", Environment.MachineName); + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + //result.Add(new MethodTaskItem("Import", "Import...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateRequest", "Create Certificate Request...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("Complete", "Complete Certificate Request...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateDomain", "Create Domain Certificate...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateSelf", "Create Self-Signed Certificate...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("View", "View...", string.Empty).SetUsage()); + //if (_owner.SelectedItem.Certificate.HasPrivateKey) + //{ + // try + // { + // var keyInfo = (RSACryptoServiceProvider)_owner.SelectedItem.Certificate.PrivateKey; + // if (keyInfo.CspKeyContainerInfo.Exportable) + // { + // result.Add(new MethodTaskItem("Export", "Export...", string.Empty).SetUsage()); + // if (_owner.SelectedItem.Certificate.Issuer != LocalhostIssuer && _owner.SelectedItem.Certificate.Issuer != _localMachineIssuer) + // { + // result.Add(new MethodTaskItem("Renew", "Renew...", string.Empty).SetUsage()); + // } + // } + // } + // catch (CryptographicException ex) + // { + // if (ex.HResult != -2146893802) + // { + // throw; + // } + // } + //} + + //result.Add(new MethodTaskItem("Remove", "Remove", string.Empty, string.Empty, Resources.remove_16).SetUsage()); + } + + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //if (!this._owner.AutomicRebindEnabled) + //{ + // result.Add(new MethodTaskItem("Enable", "Enable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + //} + + //if (this._owner.AutomicRebindEnabled) + //{ + // result.Add(new MethodTaskItem("Disable", "Disable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + //} + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Import() + { + _owner.Import(); + } + + [Obfuscation(Exclude = true)] + public void View() + { + _owner.View(); + } + + [Obfuscation(Exclude = true)] + public void Export() + { + _owner.Export(); + } + + [Obfuscation(Exclude = true)] + public void Renew() + { + _owner.Renew(); + } + + [Obfuscation(Exclude = true)] + public void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void CreateRequest() + { + _owner.CreateRequest(); + } + + [Obfuscation(Exclude = true)] + public void Complete() + { + _owner.Complete(); + } + + [Obfuscation(Exclude = true)] + public void CreateDomain() + { + _owner.CreateDomain(); + } + + [Obfuscation(Exclude = true)] + public void CreateSelf() + { + _owner.CreateSelf(); + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + } + + public ReservedUrlsFeature(Microsoft.Web.Management.Client.Module module) + : base(module) + { + } + + private FeatureTaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + this.Items = new List(); + var httpNamespaceAcls = Microsoft.Web.Administration.NativeMethods.QueryHttpNamespaceAcls(); + foreach (var mapping in httpNamespaceAcls) + { + this.Items.Add(new ReservedUrlsItem(mapping.UrlPrefix, mapping.SecurityDescriptor, this)); + } + + this.OnHttpApiSettingsSaved(); + } + + public void Import() + { + //var dialog = new ImportCertificateDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove this certificate, and permanently remove it from the certificate store?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != + DialogResult.Yes) + { + return; + } + + // remove certificate and mapping + //using (var process = new Process()) + //{ + // var start = process.StartInfo; + // start.Verb = "runas"; + // start.FileName = "cmd"; + // start.Arguments = string.Format( + // "/c \"\"{2}\" /h:\"{0}\" /s:{1}\"", + // SelectedItem.Certificate.Thumbprint, + // SelectedItem.Store == "Personal" ? "MY" : "WebHosting", + // Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")); + // start.CreateNoWindow = true; + // start.WindowStyle = ProcessWindowStyle.Hidden; + // process.Start(); + // process.WaitForExit(); + + // if (process.ExitCode == 0) + // { + // Items.Remove(SelectedItem); + // SelectedItem = null; + // this.OnHttpApiSettingsSaved(); + // } + //} + } + + protected void OnHttpApiSettingsSaved() + { + this.HttpApiSettingsUpdated?.Invoke(); + } + + public override bool ShowHelp() + { + Process.Start("https://msdn.microsoft.com/en-us/library/windows/desktop/cc307243(v=vs.85).aspx"); + return false; + } + + private void Disable() + { + } + + private void Enable() + { + } + + private void CreateSelf() + { + //var dialog = new SelfCertificateDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + private void CreateDomain() + { + } + + private void Complete() + { + //var dialog = new CompleteRequestDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + private void CreateRequest() + { + //var wizard = new CertificateRequestWizard(this.Module); + //wizard.ShowDialog(); + } + + private void Export() + { + //var dialog = new ExportCertificateDialog(SelectedItem.Certificate, Module); + //dialog.ShowDialog(); + } + + private void Renew() + { + //var wizard = new CertificateRenewWizard(SelectedItem.Certificate, Module); + //wizard.ShowDialog(); + } + + private void View() + { + } + + public bool AutomicRebindEnabled { get; set; } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + return null; + } + + public HttpApiSettingsSavedEventHandler HttpApiSettingsUpdated { get; set; } + + public override string Name + { + get + { + return "Reserved URLs"; + } + } + } +} diff --git a/JexusManager.Features.HttpApi/ReservedUrlsItem.cs b/JexusManager.Features.HttpApi/ReservedUrlsItem.cs new file mode 100644 index 00000000..18161c53 --- /dev/null +++ b/JexusManager.Features.HttpApi/ReservedUrlsItem.cs @@ -0,0 +1,39 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Web.Administration; + +namespace JexusManager.Features.HttpApi +{ + internal class ReservedUrlsItem : IItem + { + public string UrlPrefix { get; set; } + public string SecurityDescriptor { get; set; } + + public ReservedUrlsItem(string urlPrefix, string securityDescriptor, ReservedUrlsFeature feature) + { + SecurityDescriptor = securityDescriptor; + UrlPrefix = urlPrefix; + Feature = feature; + } + + public ReservedUrlsFeature Feature { get; private set; } + + public string Flag { get; set; } + public ConfigurationElement Element { get; set; } + + public void Apply() + { } + + public bool Equals(ReservedUrlsItem other) + { + return Match(other) && other.SecurityDescriptor == this.SecurityDescriptor; + } + + public bool Match(ReservedUrlsItem other) + { + return other != null && other.UrlPrefix == this.UrlPrefix; + } + } +} diff --git a/JexusManager.Features.HttpApi/Resources/http_36.png b/JexusManager.Features.HttpApi/Resources/http_36.png new file mode 100644 index 00000000..4ac1e182 Binary files /dev/null and b/JexusManager.Features.HttpApi/Resources/http_36.png differ diff --git a/JexusManager.Features.HttpApi/SniMappingFeature.cs b/JexusManager.Features.HttpApi/SniMappingFeature.cs new file mode 100644 index 00000000..b5395c76 --- /dev/null +++ b/JexusManager.Features.HttpApi/SniMappingFeature.cs @@ -0,0 +1,319 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.HttpApi +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + using Services; + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Org.BouncyCastle.Utilities.Encoders; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class SniMappingFeature : HttpApiFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly SniMappingFeature _owner; + + public FeatureTaskList(SniMappingFeature owner) + { + _owner = owner; + } + + private const string LocalhostIssuer = "CN=localhost"; + private readonly string _localMachineIssuer = string.Format("CN={0}", Environment.MachineName); + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + //result.Add(new MethodTaskItem("Import", "Import...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateRequest", "Create Certificate Request...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("Complete", "Complete Certificate Request...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateDomain", "Create Domain Certificate...", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("CreateSelf", "Create Self-Signed Certificate...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //result.Add(new MethodTaskItem("View", "View...", string.Empty).SetUsage()); + //if (_owner.SelectedItem.Certificate.HasPrivateKey) + //{ + // try + // { + // var keyInfo = (RSACryptoServiceProvider)_owner.SelectedItem.Certificate.PrivateKey; + // if (keyInfo.CspKeyContainerInfo.Exportable) + // { + // result.Add(new MethodTaskItem("Export", "Export...", string.Empty).SetUsage()); + // if (_owner.SelectedItem.Certificate.Issuer != LocalhostIssuer && _owner.SelectedItem.Certificate.Issuer != _localMachineIssuer) + // { + // result.Add(new MethodTaskItem("Renew", "Renew...", string.Empty).SetUsage()); + // } + // } + // } + // catch (CryptographicException ex) + // { + // if (ex.HResult != -2146893802) + // { + // throw; + // } + // } + //} + + //result.Add(new MethodTaskItem("Remove", "Remove", string.Empty, string.Empty, Resources.remove_16).SetUsage()); + } + + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + //if (!this._owner.AutomicRebindEnabled) + //{ + // result.Add(new MethodTaskItem("Enable", "Enable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + //} + + //if (this._owner.AutomicRebindEnabled) + //{ + // result.Add(new MethodTaskItem("Disable", "Disable Automatic Rebind of Renewed Certificate", string.Empty).SetUsage()); + //} + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Import() + { + _owner.Import(); + } + + [Obfuscation(Exclude = true)] + public void View() + { + _owner.View(); + } + + [Obfuscation(Exclude = true)] + public void Export() + { + _owner.Export(); + } + + [Obfuscation(Exclude = true)] + public void Renew() + { + _owner.Renew(); + } + + [Obfuscation(Exclude = true)] + public void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void CreateRequest() + { + _owner.CreateRequest(); + } + + [Obfuscation(Exclude = true)] + public void Complete() + { + _owner.Complete(); + } + + [Obfuscation(Exclude = true)] + public void CreateDomain() + { + _owner.CreateDomain(); + } + + [Obfuscation(Exclude = true)] + public void CreateSelf() + { + _owner.CreateSelf(); + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + } + + public SniMappingFeature(Module module) + : base(module) + { + } + + private FeatureTaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public override void Load() + { + this.Items = new List(); + var sniMappings = NativeMethods.QuerySslSniInfo(); + foreach (var mapping in sniMappings) + { + this.Items.Add(new SniMappingItem(mapping.Host, mapping.Port.ToString(), mapping.AppId.ToString(), Hex.ToHexString(mapping.Hash), mapping.StoreName, this)); + } + + this.OnHttpApiSettingsSaved(); + } + + public void Import() + { + //var dialog = new ImportCertificateDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove this certificate, and permanently remove it from the certificate store?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != + DialogResult.Yes) + { + return; + } + + // remove certificate and mapping + //using (var process = new Process()) + //{ + // var start = process.StartInfo; + // start.Verb = "runas"; + // start.FileName = "cmd"; + // start.Arguments = string.Format( + // "/c \"\"{2}\" /h:\"{0}\" /s:{1}\"", + // SelectedItem.Certificate.Thumbprint, + // SelectedItem.Store == "Personal" ? "MY" : "WebHosting", + // Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")); + // start.CreateNoWindow = true; + // start.WindowStyle = ProcessWindowStyle.Hidden; + // process.Start(); + // process.WaitForExit(); + + // if (process.ExitCode == 0) + // { + // Items.Remove(SelectedItem); + // SelectedItem = null; + // this.OnHttpApiSettingsSaved(); + // } + //} + } + + protected void OnHttpApiSettingsSaved() + { + this.HttpApiSettingsUpdated?.Invoke(); + } + + public override bool ShowHelp() + { + Process.Start("https://msdn.microsoft.com/en-us/library/windows/desktop/cc307243(v=vs.85).aspx"); + return false; + } + + private void Disable() + { + } + + private void Enable() + { + } + + private void CreateSelf() + { + //var dialog = new SelfCertificateDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + private void CreateDomain() + { + } + + private void Complete() + { + //var dialog = new CompleteRequestDialog(Module); + //dialog.ShowDialog(); + + //Items.Add(new CertificatesItem(dialog.Item, dialog.Store, this)); + this.OnHttpApiSettingsSaved(); + } + + private void CreateRequest() + { + //var wizard = new CertificateRequestWizard(this.Module); + //wizard.ShowDialog(); + } + + private void Export() + { + //var dialog = new ExportCertificateDialog(SelectedItem.Certificate, Module); + //dialog.ShowDialog(); + } + + private void Renew() + { + //var wizard = new CertificateRenewWizard(SelectedItem.Certificate, Module); + //wizard.ShowDialog(); + } + + private void View() + { + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + return null; + } + + public bool AutomicRebindEnabled { get; set; } + + + public HttpApiSettingsSavedEventHandler HttpApiSettingsUpdated { get; set; } + + public override string Name + { + get + { + return "SNI Mappings"; + } + } + } +} diff --git a/JexusManager.Features.HttpApi/SniMappingItem.cs b/JexusManager.Features.HttpApi/SniMappingItem.cs new file mode 100644 index 00000000..b3555195 --- /dev/null +++ b/JexusManager.Features.HttpApi/SniMappingItem.cs @@ -0,0 +1,45 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpApi +{ + using Microsoft.Web.Administration; + + internal class SniMappingItem : IItem + { + public SniMappingItem(string host, string port, string appId, string hash, string store, SniMappingFeature feature) + { + this.Host = host; + this.Port = port; + this.AppId = appId; + this.Hash = hash; + this.Store = store; + this.Feature = feature; + } + + public string Hash { get; set; } + public string Host { get; set; } + public string Port { get; set; } + public string AppId { get; set; } + public string Store { get; set; } + public SniMappingFeature Feature { get; private set; } + + public string Flag { get; set; } + public ConfigurationElement Element { get; set; } + + public void Apply() + { } + + public bool Equals(SniMappingItem other) + { + return Match(other); + } + + public bool Match(SniMappingItem other) + { + return other != null && other.Hash == this.Hash && other.Store == this.Store && other.Host == this.Host + && other.Port == this.Port && other.AppId == this.AppId; + } + } +} diff --git a/JexusManager.Features.HttpApi/packages.config b/JexusManager.Features.HttpApi/packages.config new file mode 100644 index 00000000..730a601e --- /dev/null +++ b/JexusManager.Features.HttpApi/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/EditDialog.Designer.cs b/JexusManager.Features.HttpErrors/EditDialog.Designer.cs new file mode 100644 index 00000000..ba0facdf --- /dev/null +++ b/JexusManager.Features.HttpErrors/EditDialog.Designer.cs @@ -0,0 +1,224 @@ +namespace JexusManager.Features.HttpErrors +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class EditDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.label1 = new System.Windows.Forms.Label(); + this.rbCustom = new System.Windows.Forms.RadioButton(); + this.rbDetailed = new System.Windows.Forms.RadioButton(); + this.rbRemote = new System.Windows.Forms.RadioButton(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbType = new System.Windows.Forms.ComboBox(); + this.btnSelect = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.rbRemote); + this.groupBox1.Controls.Add(this.rbDetailed); + this.groupBox1.Controls.Add(this.rbCustom); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(399, 152); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Error Responses"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(7, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(217, 13); + this.label1.TabIndex = 0; + this.label1.Text = "When the server encounters an error, return:"; + // + // rbCustom + // + this.rbCustom.AutoSize = true; + this.rbCustom.Location = new System.Drawing.Point(10, 48); + this.rbCustom.Name = "rbCustom"; + this.rbCustom.Size = new System.Drawing.Size(116, 17); + this.rbCustom.TabIndex = 1; + this.rbCustom.TabStop = true; + this.rbCustom.Text = "Custom error pages"; + this.rbCustom.UseVisualStyleBackColor = true; + // + // rbDetailed + // + this.rbDetailed.AutoSize = true; + this.rbDetailed.Location = new System.Drawing.Point(10, 71); + this.rbDetailed.Name = "rbDetailed"; + this.rbDetailed.Size = new System.Drawing.Size(93, 17); + this.rbDetailed.TabIndex = 2; + this.rbDetailed.TabStop = true; + this.rbDetailed.Text = "Detailed errors"; + this.rbDetailed.UseVisualStyleBackColor = true; + // + // rbRemote + // + this.rbRemote.AutoSize = true; + this.rbRemote.Location = new System.Drawing.Point(10, 94); + this.rbRemote.Name = "rbRemote"; + this.rbRemote.Size = new System.Drawing.Size(290, 30); + this.rbRemote.TabIndex = 3; + this.rbRemote.TabStop = true; + this.rbRemote.Text = "Detailed errors for local requests and custom error pages\r\nfor remote requests"; + this.rbRemote.UseVisualStyleBackColor = true; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.btnSelect); + this.groupBox2.Controls.Add(this.cbType); + this.groupBox2.Controls.Add(this.label3); + this.groupBox2.Controls.Add(this.txtPath); + this.groupBox2.Controls.Add(this.label2); + this.groupBox2.Location = new System.Drawing.Point(12, 182); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(399, 155); + this.groupBox2.TabIndex = 1; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Default Page"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(7, 27); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(32, 13); + this.label2.TabIndex = 0; + this.label2.Text = "Path:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(10, 52); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(268, 20); + this.txtPath.TabIndex = 1; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(7, 88); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(59, 13); + this.label3.TabIndex = 2; + this.label3.Text = "Path Type:"; + // + // cbType + // + this.cbType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbType.FormattingEnabled = true; + this.cbType.Items.AddRange(new object[] { + "File", + "Execute URL", + "Redirect"}); + this.cbType.Location = new System.Drawing.Point(10, 115); + this.cbType.Name = "cbType"; + this.cbType.Size = new System.Drawing.Size(238, 21); + this.cbType.TabIndex = 3; + // + // btnSelect + // + this.btnSelect.Location = new System.Drawing.Point(284, 50); + this.btnSelect.Name = "btnSelect"; + this.btnSelect.Size = new System.Drawing.Size(34, 23); + this.btnSelect.TabIndex = 4; + this.btnSelect.Text = "..."; + this.btnSelect.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(236, 351); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 2; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(332, 351); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // EditDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(439, 386); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Name = "EditDialog"; + this.Text = "Edit Error Pages Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.EditDialog_HelpButtonClicked); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private GroupBox groupBox1; + private RadioButton rbRemote; + private RadioButton rbDetailed; + private RadioButton rbCustom; + private Label label1; + private GroupBox groupBox2; + private Button btnSelect; + private ComboBox cbType; + private Label label3; + private TextBox txtPath; + private Label label2; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.HttpErrors/EditDialog.cs b/JexusManager.Features.HttpErrors/EditDialog.cs new file mode 100644 index 00000000..5b297214 --- /dev/null +++ b/JexusManager.Features.HttpErrors/EditDialog.cs @@ -0,0 +1,68 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + public partial class EditDialog : DialogForm + { + public EditDialog(IServiceProvider serviceProvider, ConfigurationElement element) + : base(serviceProvider) + { + InitializeComponent(); + var mode = (long)element["errorMode"]; + rbCustom.Checked = mode == 1; + rbDetailed.Checked = mode == 2; + rbRemote.Checked = mode == 0; + + var defaultMode = (long)element["defaultResponseMode"]; + cbType.SelectedIndex = (int)defaultMode; + + txtPath.Text = (string)element["defaultPath"]; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnSelect, "Click") + .Subscribe(evt => + { + DialogHelper.ShowBrowseDialog(txtPath); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (rbCustom.Checked) + { + element["errorMode"] = 1L; + } + else if (rbDetailed.Checked) + { + element["errorMode"] = 2L; + } + else + { + element["errorMode"] = 0L; + } + + element["defaultResponseMode"] = (long)cbType.SelectedIndex; + element["defaultPath"] = txtPath.Text; + })); + } + + private void EditDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210481"); + } + } +} diff --git a/JexusManager.Features.HttpErrors/EditDialog.resx b/JexusManager.Features.HttpErrors/EditDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.HttpErrors/EditDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/HttpErrorsFeature.cs b/JexusManager.Features.HttpErrors/HttpErrorsFeature.cs new file mode 100644 index 00000000..c0dca7e7 --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsFeature.cs @@ -0,0 +1,193 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.HttpErrors +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class HttpErrorsFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly HttpErrorsFeature _owner; + + public FeatureTaskList(HttpErrorsFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + if (_owner.SelectedItem.Flag == "Local") + { + result.Add(new MethodTaskItem("Change", "Change Status Code", string.Empty).SetUsage()); + } + + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Edit Feature Settings...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + } + + public HttpErrorsFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + LoadItems(); + } + + public void Add() + { + var dialog = new NewErrorDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected custom error page?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + var dialog = new NewErrorDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(this.SelectedItem); + } + + public void Set() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/httpErrors"); + var dialog = new EditDialog(this.Module, section); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection section = service.GetSection("system.webServer/httpErrors"); + return section.GetCollection(); + } + + protected override void OnSettingsSaved() + { + this.HttpErrorsSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210481"); + return false; + } + + public HttpErrorsSettingsSavedEventHandler HttpErrorsSettingsUpdated { get; set; } + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get + { + return "Error Pages"; + } + } + } +} diff --git a/JexusManager.Features.HttpErrors/HttpErrorsItem.cs b/JexusManager.Features.HttpErrors/HttpErrorsItem.cs new file mode 100644 index 00000000..a6af3de4 --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsItem.cs @@ -0,0 +1,68 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + using Microsoft.Web.Administration; + + internal class HttpErrorsItem : IItem + { + public HttpErrorsItem(ConfigurationElement element) + { + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + this.Element = element; + if (element == null) + { + Prefix = Path = string.Empty; + Response = "File"; + Substatus = -1; + return; + } + + this.Status = (uint)element["statusCode"]; + this.Substatus = (int)element["subStatusCode"]; + this.Path = (string)element["path"]; + this.Prefix = (string)element["prefixLanguageFilePath"]; + this.Response = element.Schema.AttributeSchemas["responseMode"].Format(element["responseMode"]); + } + + public string Prefix { get; set; } + + public uint Status { get; set; } + public int Substatus { get; set; } + public string Path { get; set; } + public string Response { get; set; } + public string Flag { get; set; } + public ConfigurationElement Element { get; set; } + + public string FullPath + { + get { return string.IsNullOrEmpty(this.Prefix) ? this.Path : string.Format("{0}\\\\{1}", this.Prefix, this.Path); } + } + + public string Code + { + get { return this.Substatus == -1 || this.Substatus == 0 ? this.Status.ToString() : string.Format("{0}.{1}", this.Status, this.Substatus); } + } + + public bool Equals(HttpErrorsItem other) + { + return Match(other); + } + + public bool Match(HttpErrorsItem other) + { + return other != null && other.Status == this.Status && other.Substatus == this.Substatus; + } + + public void Apply() + { + this.Element["statusCode"] = this.Status; + this.Element["subStatusCode"] = this.Substatus; + this.Element["prefixLanguageFilePath"] = this.Prefix; + this.Element["path"] = this.Path; + this.Element["responseMode"] = this.Response; + } + } +} diff --git a/JexusManager.Features.HttpErrors/HttpErrorsModule.cs b/JexusManager.Features.HttpErrors/HttpErrorsModule.cs new file mode 100644 index 00000000..2dd838e7 --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class HttpErrorsModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(HttpErrorsPage), "Error Pages", + "Configure pages to return when errors occur", Resources.custom_error_36, + Resources.custom_error_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.HttpErrors/HttpErrorsModuleProvider.cs b/JexusManager.Features.HttpErrors/HttpErrorsModuleProvider.cs new file mode 100644 index 00000000..5178f35d --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + using System; + + using Microsoft.Web.Management.Server; + + public class HttpErrorsModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(HttpErrorsModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.HttpErrors/HttpErrorsPage.Designer.cs b/JexusManager.Features.HttpErrors/HttpErrorsPage.Designer.cs new file mode 100644 index 00000000..c610e7c8 --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsPage.Designer.cs @@ -0,0 +1,298 @@ +namespace JexusManager.Features.HttpErrors +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class HttpErrorsPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chPath = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chPage = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 5; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitContainer1_SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 11; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chStatus, + this.chPath, + this.chPage, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // chStatus + // + this.chStatus.Text = "Status Code"; + this.chStatus.Width = 120; + // + // chPath + // + this.chPath.Text = "Path"; + this.chPath.Width = 150; + // + // chPage + // + this.chPage.Text = "Type"; + this.chPage.Width = 120; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel3, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel3 + // + this.toolStripLabel3.Name = "toolStripLabel3"; + this.toolStripLabel3.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel3.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Category", + "Area"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(796, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure HTTP error responses. The error responses can be cu" + + "stom error pages, or detailed error messages that contain troubleshooting inform" + + "ation."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(126, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Error Pages"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // CustomErrorsPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "HttpErrorsPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chStatus; + private ColumnHeader chPath; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private ColumnHeader chPage; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel3; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + } +} diff --git a/JexusManager.Features.HttpErrors/HttpErrorsPage.cs b/JexusManager.Features.HttpErrors/HttpErrorsPage.cs new file mode 100644 index 00000000..184fe559 --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsPage.cs @@ -0,0 +1,137 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class HttpErrorsPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly HttpErrorsPage _owner; + + public PageTaskList(HttpErrorsPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class CustomErrorsListViewItem : ListViewItem + { + public HttpErrorsItem Item { get; } + private readonly HttpErrorsPage _page; + + public CustomErrorsListViewItem(HttpErrorsItem item, HttpErrorsPage page) + : base(item.Code) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.FullPath)); + this.SubItems.Add(new ListViewSubItem(this, item.Response)); + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private HttpErrorsFeature _feature; + private PageTaskList _taskList; + + public HttpErrorsPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new HttpErrorsFeature(this.Module); + _feature.HttpErrorsSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new CustomErrorsListViewItem(file, this)); + } + + if (_feature.SelectedItem != null) + { + foreach (CustomErrorsListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + this.Refresh(); + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((CustomErrorsListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.HttpErrors/HttpErrorsPage.resx b/JexusManager.Features.HttpErrors/HttpErrorsPage.resx new file mode 100644 index 00000000..876b3bf2 --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsPage.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 281, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/HttpErrorsSettingsSavedEventHandler.cs b/JexusManager.Features.HttpErrors/HttpErrorsSettingsSavedEventHandler.cs new file mode 100644 index 00000000..350a05d8 --- /dev/null +++ b/JexusManager.Features.HttpErrors/HttpErrorsSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + public delegate void HttpErrorsSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/JexusManager.Features.HttpErrors.csproj b/JexusManager.Features.HttpErrors/JexusManager.Features.HttpErrors.csproj new file mode 100644 index 00000000..53011d60 --- /dev/null +++ b/JexusManager.Features.HttpErrors/JexusManager.Features.HttpErrors.csproj @@ -0,0 +1,151 @@ + + + + + Debug + AnyCPU + {76F1FA9F-3E56-4D5C-9324-D69DB260EFA9} + Library + Properties + JexusManager.Features.HttpErrors + JexusManager.Features.HttpErrors + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + ConfigurationAttributeSchemaExtensions.cs + + + Form + + + EditDialog.cs + + + + + + + UserControl + + + HttpErrorsPage.cs + + + + Form + + + LocalErrorDialog.cs + + + Form + + + NewErrorDialog.cs + + + + True + True + Resources.resx + + + + + EditDialog.cs + + + HttpErrorsPage.cs + + + LocalErrorDialog.cs + + + NewErrorDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/JexusManager.snk b/JexusManager.Features.HttpErrors/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.HttpErrors/JexusManager.snk differ diff --git a/JexusManager.Features.HttpErrors/LocalErrorDialog.Designer.cs b/JexusManager.Features.HttpErrors/LocalErrorDialog.Designer.cs new file mode 100644 index 00000000..aa0b4b39 --- /dev/null +++ b/JexusManager.Features.HttpErrors/LocalErrorDialog.Designer.cs @@ -0,0 +1,171 @@ +namespace JexusManager.Features.HttpErrors +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class LocalErrorDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtDirectory = new System.Windows.Forms.TextBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(76, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Directory path:"; + // + // txtDirectory + // + this.txtDirectory.Location = new System.Drawing.Point(15, 25); + this.txtDirectory.Name = "txtDirectory"; + this.txtDirectory.Size = new System.Drawing.Size(300, 20); + this.txtDirectory.TabIndex = 1; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(321, 23); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(85, 23); + this.btnBrowse.TabIndex = 2; + this.btnBrowse.Text = "Browse..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 48); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(164, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Example: d:\\errors, relative\\errors"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 79); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(89, 13); + this.label3.TabIndex = 4; + this.label3.Text = "Relative file path:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 95); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(255, 20); + this.txtPath.TabIndex = 5; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(226, 211); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(322, 211); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 7; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 118); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(148, 13); + this.label4.TabIndex = 8; + this.label4.Text = "Example: \\NotFound\\404.htm"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(12, 161); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(226, 13); + this.label5.TabIndex = 9; + this.label5.Text = "Example: d:\\errors\\EN-US\\NotFound\\404.htm"; + // + // LocalErrorDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(429, 246); + this.Controls.Add(this.label5); + this.Controls.Add(this.label4); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.txtDirectory); + this.Controls.Add(this.label1); + this.Name = "LocalErrorDialog"; + this.Text = "Set Localized Custom Error Path"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.LocalErrorDialog_HelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtDirectory; + private Button btnBrowse; + private Label label2; + private Label label3; + private TextBox txtPath; + private Button btnOK; + private Button btnCancel; + private Label label4; + private Label label5; + } +} diff --git a/JexusManager.Features.HttpErrors/LocalErrorDialog.cs b/JexusManager.Features.HttpErrors/LocalErrorDialog.cs new file mode 100644 index 00000000..e5ddb44a --- /dev/null +++ b/JexusManager.Features.HttpErrors/LocalErrorDialog.cs @@ -0,0 +1,50 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class LocalErrorDialog : DialogForm + { + public LocalErrorDialog(IServiceProvider serviceProvider, HttpErrorsItem item) + : base(serviceProvider) + { + InitializeComponent(); + txtDirectory.Text = item.Prefix; + txtPath.Text = item.Path; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowBrowseDialog(txtDirectory); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + item.Prefix = txtDirectory.Text; + item.Path = txtPath.Text; + DialogResult = DialogResult.OK; + })); + } + + private void LocalErrorDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210481"); + } + } +} diff --git a/JexusManager.Features.HttpErrors/LocalErrorDialog.resx b/JexusManager.Features.HttpErrors/LocalErrorDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.HttpErrors/LocalErrorDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/NewErrorDialog.Designer.cs b/JexusManager.Features.HttpErrors/NewErrorDialog.Designer.cs new file mode 100644 index 00000000..00dc648a --- /dev/null +++ b/JexusManager.Features.HttpErrors/NewErrorDialog.Designer.cs @@ -0,0 +1,285 @@ +namespace JexusManager.Features.HttpErrors +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewErrorDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtStatusCode = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.btnSet = new System.Windows.Forms.Button(); + this.label6 = new System.Windows.Forms.Label(); + this.txtRedirect = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.txtExecute = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.cbLocalize = new System.Windows.Forms.CheckBox(); + this.txtStatic = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.rbRedirect = new System.Windows.Forms.RadioButton(); + this.rbExecute = new System.Windows.Forms.RadioButton(); + this.rbStatic = new System.Windows.Forms.RadioButton(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(67, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Status code:"; + // + // txtStatusCode + // + this.txtStatusCode.Location = new System.Drawing.Point(12, 25); + this.txtStatusCode.Name = "txtStatusCode"; + this.txtStatusCode.Size = new System.Drawing.Size(120, 20); + this.txtStatusCode.TabIndex = 1; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 48); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(113, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Example: 404 or 404.2"; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.btnSet); + this.groupBox1.Controls.Add(this.label6); + this.groupBox1.Controls.Add(this.txtRedirect); + this.groupBox1.Controls.Add(this.label7); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.txtExecute); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.cbLocalize); + this.groupBox1.Controls.Add(this.txtStatic); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.rbRedirect); + this.groupBox1.Controls.Add(this.rbExecute); + this.groupBox1.Controls.Add(this.rbStatic); + this.groupBox1.Location = new System.Drawing.Point(12, 77); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(470, 343); + this.groupBox1.TabIndex = 3; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Response Action"; + // + // btnSet + // + this.btnSet.Location = new System.Drawing.Point(376, 75); + this.btnSet.Name = "btnSet"; + this.btnSet.Size = new System.Drawing.Size(88, 23); + this.btnSet.TabIndex = 12; + this.btnSet.Text = "Set..."; + this.btnSet.UseVisualStyleBackColor = true; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(33, 306); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(220, 13); + this.label6.TabIndex = 11; + this.label6.Text = "Example: http://www.contoso.com/404.aspx"; + // + // txtRedirect + // + this.txtRedirect.Location = new System.Drawing.Point(36, 283); + this.txtRedirect.Name = "txtRedirect"; + this.txtRedirect.Size = new System.Drawing.Size(334, 20); + this.txtRedirect.TabIndex = 10; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(33, 267); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(76, 13); + this.label7.TabIndex = 9; + this.label7.Text = "Absolute URL:"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(33, 207); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(158, 13); + this.label5.TabIndex = 8; + this.label5.Text = "Example: /ErrorPages/404.aspx"; + // + // txtExecute + // + this.txtExecute.Location = new System.Drawing.Point(36, 184); + this.txtExecute.Name = "txtExecute"; + this.txtExecute.Size = new System.Drawing.Size(334, 20); + this.txtExecute.TabIndex = 7; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(33, 168); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(127, 13); + this.label4.TabIndex = 6; + this.label4.Text = "URL (relative to site root):"; + // + // cbLocalize + // + this.cbLocalize.AutoSize = true; + this.cbLocalize.Location = new System.Drawing.Point(36, 104); + this.cbLocalize.Name = "cbLocalize"; + this.cbLocalize.Size = new System.Drawing.Size(245, 17); + this.cbLocalize.TabIndex = 5; + this.cbLocalize.Text = "Try to return the error file in the client language"; + this.cbLocalize.UseVisualStyleBackColor = true; + // + // txtStatic + // + this.txtStatic.Location = new System.Drawing.Point(36, 77); + this.txtStatic.Name = "txtStatic"; + this.txtStatic.Size = new System.Drawing.Size(334, 20); + this.txtStatic.TabIndex = 4; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(33, 61); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(50, 13); + this.label3.TabIndex = 3; + this.label3.Text = "File path:"; + // + // rbRedirect + // + this.rbRedirect.AutoSize = true; + this.rbRedirect.Location = new System.Drawing.Point(15, 235); + this.rbRedirect.Name = "rbRedirect"; + this.rbRedirect.Size = new System.Drawing.Size(158, 17); + this.rbRedirect.TabIndex = 2; + this.rbRedirect.Text = "Respond with a 302 redirect"; + this.rbRedirect.UseVisualStyleBackColor = true; + // + // rbExecute + // + this.rbExecute.AutoSize = true; + this.rbExecute.Checked = true; + this.rbExecute.Location = new System.Drawing.Point(15, 137); + this.rbExecute.Name = "rbExecute"; + this.rbExecute.Size = new System.Drawing.Size(150, 17); + this.rbExecute.TabIndex = 1; + this.rbExecute.TabStop = true; + this.rbExecute.Text = "Execute a URL on the site"; + this.rbExecute.UseVisualStyleBackColor = true; + // + // rbStatic + // + this.rbStatic.AutoSize = true; + this.rbStatic.Location = new System.Drawing.Point(15, 28); + this.rbStatic.Name = "rbStatic"; + this.rbStatic.Size = new System.Drawing.Size(265, 17); + this.rbStatic.TabIndex = 0; + this.rbStatic.Text = "Insert content from static file into the error response"; + this.rbStatic.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(387, 451); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(287, 451); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // NewCustomErrorDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(494, 486); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtStatusCode); + this.Controls.Add(this.label1); + this.Name = "NewErrorDialog"; + this.Text = "Add Custom Error Page"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewCustomErrorDialogHelpButtonClicked); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtStatusCode; + private Label label2; + private GroupBox groupBox1; + private Label label6; + private TextBox txtRedirect; + private Label label7; + private Label label5; + private TextBox txtExecute; + private Label label4; + private CheckBox cbLocalize; + private TextBox txtStatic; + private Label label3; + private RadioButton rbRedirect; + private RadioButton rbExecute; + private RadioButton rbStatic; + private Button btnCancel; + private Button btnOK; + private Button btnSet; + } +} diff --git a/JexusManager.Features.HttpErrors/NewErrorDialog.cs b/JexusManager.Features.HttpErrors/NewErrorDialog.cs new file mode 100644 index 00000000..4b16e28a --- /dev/null +++ b/JexusManager.Features.HttpErrors/NewErrorDialog.cs @@ -0,0 +1,215 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpErrors +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewErrorDialog : DialogForm + { + public NewErrorDialog(IServiceProvider serviceProvider, HttpErrorsItem item, HttpErrorsFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + txtStatusCode.ReadOnly = item != null; + if (item != null) + { + txtStatusCode.Text = item.Code; + } + + Text = item == null ? "Add Custom Error Page" : "Edit Custom Error Page"; + Item = item ?? new HttpErrorsItem(null); + var mode = Item.Response; + rbStatic.Checked = mode == "File"; + rbExecute.Checked = mode == "ExecuteURL"; + rbRedirect.Checked = mode == "Redirect"; + UpdateUI(Item); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtStatusCode, "TextChanged") + .Merge(Observable.FromEventPattern(txtRedirect, "TextChanged")) + .Merge(Observable.FromEventPattern(txtExecute, "TextChanged")) + .Merge(Observable.FromEventPattern(txtStatic, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtStatusCode.Text) && + !(string.IsNullOrWhiteSpace(txtExecute.Text) && string.IsNullOrWhiteSpace(txtStatic.Text) && string.IsNullOrWhiteSpace(txtRedirect.Text)); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + var index = txtStatusCode.Text.IndexOf('.'); + if (index == -1) + { + int statusCode; + var statusParsed = int.TryParse(txtStatusCode.Text, out statusCode); + if (!statusParsed || statusCode < 100 || statusCode > 999) + { + ShowMessage( + string.Format( + "'{0}' is an invalid status code. Status codes must be numbers in the form of 400 or 400.1. Status codes must be between 100 and 999. Sub-status codes must be between 1 and 999.", + txtStatusCode.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + Item.Status = uint.Parse(txtStatusCode.Text); + Item.Substatus = -1; + } + else + { + var status = index == 0 ? string.Empty : txtStatusCode.Text.Substring(0, index); + var substatus = index == txtStatusCode.Text.Length - 1 ? string.Empty : txtStatusCode.Text.Substring(index + 1); + uint statusCode; + var statusParsed = uint.TryParse(status, out statusCode); + int substatusCode; + var substatusParsed = int.TryParse(substatus, out substatusCode); + if (!statusParsed || statusCode < 100 || statusCode > 999 || !substatusParsed || substatusCode < 1 || + substatusCode > 999) + { + ShowMessage( + string.Format( + "'{0}' is an invalid status code. Status codes must be numbers in the form of 400 or 400.1. Status codes must be between 100 and 999. Sub-status codes must be between 1 and 999.", + txtStatusCode.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + Item.Status = statusCode; + Item.Substatus = substatusCode; + } + + if (Item.Response == "Redirect") + { + Item.Path = txtRedirect.Text; + } + else if (Item.Response == "ExecuteURL") + { + if (!Item.Path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) + { + ShowMessage( + "The specific URL is invalid. It must be a relative URL path that begins with \"/\"", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + Item.Path = txtExecute.Text; + } + else + { + Item.Path = txtStatic.Text; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(rbRedirect, "CheckedChanged") + .Merge(Observable.FromEventPattern(rbExecute, "CheckedChanged")) + .Merge(Observable.FromEventPattern(rbStatic, "CheckedChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + if (rbStatic.Checked) + { + Item.Response = "File"; + } + else if (rbExecute.Checked) + { + Item.Response = "ExecuteURL"; + } + else + { + Item.Response = "Redirect"; + } + + UpdateUI(Item); + })); + + container.Add( + Observable.FromEventPattern(btnSet, "Click") + .Subscribe(evt => + { + if (cbLocalize.Checked) + { + var dialog = new LocalErrorDialog(ServiceProvider, Item); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + txtStatic.Text = Item.FullPath; + } + else + { + DialogHelper.ShowBrowseDialog(txtStatic); + } + })); + + container.Add( + Observable.FromEventPattern(cbLocalize, "CheckedChanged") + .Subscribe(evt => + { + btnSet.Text = cbLocalize.Checked ? "Set..." : "Browse..."; + txtStatic.Text = string.Empty; + txtStatic.ReadOnly = cbLocalize.Checked; + })); + } + + private void UpdateUI(HttpErrorsItem item) + { + cbLocalize.Checked = !string.IsNullOrEmpty(item.Prefix); + var mode = item.Response; + if (mode == "File") + { + txtStatic.Text = string.IsNullOrEmpty(item.Prefix) ? item.Path : item.FullPath; + txtStatic.ReadOnly = cbLocalize.Checked; + txtExecute.Text = string.Empty; + txtRedirect.Text = string.Empty; + } + else if (mode == "ExecuteURL") + { + txtStatic.Text = string.Empty; + txtExecute.Text = Item.Path; + txtRedirect.Text = string.Empty; + } + else + { + txtStatic.Text = string.Empty; + txtExecute.Text = string.Empty; + txtRedirect.Text = Item.Path; + } + + txtExecute.Enabled = mode == "ExecuteURL"; + txtRedirect.Enabled = mode == "Redirect"; + cbLocalize.Enabled = btnSet.Enabled = txtStatic.Enabled = mode == "File"; + } + + public HttpErrorsItem Item { get; set; } + + private void NewCustomErrorDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210481"); + } + } +} diff --git a/JexusManager.Features.HttpErrors/NewErrorDialog.resx b/JexusManager.Features.HttpErrors/NewErrorDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.HttpErrors/NewErrorDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/Properties/AssemblyInfo.cs b/JexusManager.Features.HttpErrors/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..c2b8b8ec --- /dev/null +++ b/JexusManager.Features.HttpErrors/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.HttpErrors")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.HttpErrors")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("76f1fa9f-3e56-4d5c-9324-d69db260efa9")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.HttpErrors/Properties/Resources.Designer.cs b/JexusManager.Features.HttpErrors/Properties/Resources.Designer.cs new file mode 100644 index 00000000..485c00ad --- /dev/null +++ b/JexusManager.Features.HttpErrors/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.HttpErrors.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.HttpErrors.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap custom_error_36 { + get { + object obj = ResourceManager.GetObject("custom_error_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.HttpErrors/Properties/Resources.resx b/JexusManager.Features.HttpErrors/Properties/Resources.resx new file mode 100644 index 00000000..c60a8d60 --- /dev/null +++ b/JexusManager.Features.HttpErrors/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\custom_error_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.HttpErrors/Resources/custom_error_36.png b/JexusManager.Features.HttpErrors/Resources/custom_error_36.png new file mode 100644 index 00000000..0fe5c3e5 Binary files /dev/null and b/JexusManager.Features.HttpErrors/Resources/custom_error_36.png differ diff --git a/JexusManager.Features.HttpErrors/packages.config b/JexusManager.Features.HttpErrors/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.HttpErrors/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.HttpRedirect/HttpRedirectFeature.cs b/JexusManager.Features.HttpRedirect/HttpRedirectFeature.cs new file mode 100644 index 00000000..8523459a --- /dev/null +++ b/JexusManager.Features.HttpRedirect/HttpRedirectFeature.cs @@ -0,0 +1,132 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpRedirect +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Resources; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal class HttpRedirectFeature + { + public HttpRedirectFeature(Module module, ServerManager server, Application application) + { + this.Module = module; + this.Server = server; + this.Application = application; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (this.Module as IServiceProvider).GetService(type); + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/httpRedirect"); + this.Enabled = (bool)section["enabled"]; + this.Link = (string)section["destination"]; + this.Exact = (bool)section["exactDestination"]; + this.OnlyRoot = !(bool)section["childOnly"]; + this.Mode = (long)section["httpResponseStatus"]; + + SupportedModes = new List(); + foreach (ConfigurationEnumValue item in section.Schema.AttributeSchemas["httpResponseStatus"].GetEnumValues()) + { + SupportedModes.Add(item.Value); + } + + this.OnHttpRedirectSettingsSaved(); + } + + public List SupportedModes { get; set; } + + public string Directory { get; set; } + + protected void OnHttpRedirectSettingsSaved() + { + this.HttpRedirectSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210508"); + return false; + } + + public HttpRedirectSettingsSavedEventHandler HttpRedirectSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + public ServerManager Server { get; set; } + public Application Application { get; set; } + + public string Name + { + get { return "HTTP Redirect"; } + } + + + public bool Enabled { get; set; } + public string Link { get; set; } + public bool Exact { get; set; } + public bool OnlyRoot { get; set; } + public long Mode { get; set; } + + public void CancelChanges() + { + this.Load(); + } + + public bool ApplyChanges() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/httpRedirect"); + section["enabled"] = this.Enabled; + section["destination"] = this.Link; + section["exactDestination"] = this.Exact; + section["childOnly"] = !this.OnlyRoot; + if (section.Schema.AttributeSchemas["httpResponseStatus"].GetEnumValues().GetName(this.Mode) == null) + { + section["httpResponseStatus"] = 301; + } + else + { + section["httpResponseStatus"] = this.Mode; + } + + service.ServerManager.CommitChanges(); + return true; + } + } +} diff --git a/JexusManager.Features.HttpRedirect/HttpRedirectModule.cs b/JexusManager.Features.HttpRedirect/HttpRedirectModule.cs new file mode 100644 index 00000000..9066c6cc --- /dev/null +++ b/JexusManager.Features.HttpRedirect/HttpRedirectModule.cs @@ -0,0 +1,30 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpRedirect +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class HttpRedirectModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo( + this, + typeof(HttpRedirectPage), + "HTTP Redirect", + "Specify rules for redirecting incoming requests to another file or URL", + Resources.http_redirect_36, + Resources.http_redirect_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.HttpRedirect/HttpRedirectModuleProvider.cs b/JexusManager.Features.HttpRedirect/HttpRedirectModuleProvider.cs new file mode 100644 index 00000000..8e264b54 --- /dev/null +++ b/JexusManager.Features.HttpRedirect/HttpRedirectModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpRedirect +{ + using System; + + using Microsoft.Web.Management.Server; + + public class HttpRedirectModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(HttpRedirectModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.HttpRedirect/HttpRedirectPage.Designer.cs b/JexusManager.Features.HttpRedirect/HttpRedirectPage.Designer.cs new file mode 100644 index 00000000..46f0831c --- /dev/null +++ b/JexusManager.Features.HttpRedirect/HttpRedirectPage.Designer.cs @@ -0,0 +1,317 @@ +namespace JexusManager.Features.HttpRedirect +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class HttpRedirectPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.cbCode = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); + this.cbThis = new System.Windows.Forms.CheckBox(); + this.cbExact = new System.Windows.Forms.CheckBox(); + this.label4 = new System.Windows.Forms.Label(); + this.txtLink = new System.Windows.Forms.TextBox(); + this.cbRedirect = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 5; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.groupBox1); + this.panel2.Controls.Add(this.label4); + this.panel2.Controls.Add(this.txtLink); + this.panel2.Controls.Add(this.cbRedirect); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 11; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.cbCode); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.cbThis); + this.groupBox1.Controls.Add(this.cbExact); + this.groupBox1.Location = new System.Drawing.Point(16, 97); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(529, 171); + this.groupBox1.TabIndex = 7; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Redirect Behavior"; + // + // cbCode + // + this.cbCode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbCode.Enabled = false; + this.cbCode.FormattingEnabled = true; + this.cbCode.Location = new System.Drawing.Point(17, 121); + this.cbCode.Name = "cbCode"; + this.cbCode.Size = new System.Drawing.Size(303, 21); + this.cbCode.TabIndex = 3; + this.cbCode.SelectedIndexChanged += new System.EventHandler(this.CbCodeSelectedIndexChanged); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(14, 105); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(67, 13); + this.label5.TabIndex = 2; + this.label5.Text = "Status code:"; + // + // cbThis + // + this.cbThis.AutoSize = true; + this.cbThis.Enabled = false; + this.cbThis.Location = new System.Drawing.Point(17, 72); + this.cbThis.Name = "cbThis"; + this.cbThis.Size = new System.Drawing.Size(344, 17); + this.cbThis.TabIndex = 1; + this.cbThis.Text = "Only redirect requests to content in this directory (not subdirectories)"; + this.cbThis.UseVisualStyleBackColor = true; + this.cbThis.CheckedChanged += new System.EventHandler(this.CbExactCheckedChanged); + // + // cbExact + // + this.cbExact.AutoSize = true; + this.cbExact.Enabled = false; + this.cbExact.Location = new System.Drawing.Point(17, 35); + this.cbExact.Name = "cbExact"; + this.cbExact.Size = new System.Drawing.Size(375, 17); + this.cbExact.TabIndex = 0; + this.cbExact.Text = "Redirect all requests to exact destination (instead of relative to destination)"; + this.cbExact.UseVisualStyleBackColor = true; + this.cbExact.CheckedChanged += new System.EventHandler(this.CbExactCheckedChanged); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(30, 67); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(201, 13); + this.label4.TabIndex = 6; + this.label4.Text = "Example: http://www.contoso.com/sales"; + // + // txtLink + // + this.txtLink.Enabled = false; + this.txtLink.Location = new System.Drawing.Point(33, 44); + this.txtLink.Name = "txtLink"; + this.txtLink.Size = new System.Drawing.Size(383, 20); + this.txtLink.TabIndex = 5; + this.txtLink.TextChanged += new System.EventHandler(this.TxtLinkTextChanged); + // + // cbRedirect + // + this.cbRedirect.AutoSize = true; + this.cbRedirect.Location = new System.Drawing.Point(16, 21); + this.cbRedirect.Name = "cbRedirect"; + this.cbRedirect.Size = new System.Drawing.Size(197, 17); + this.cbRedirect.TabIndex = 4; + this.cbRedirect.Text = "Redirect requests to this destination:"; + this.cbRedirect.UseVisualStyleBackColor = true; + this.cbRedirect.CheckedChanged += new System.EventHandler(this.CbSslCheckedChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(416, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to specify rules for redirecting incoming requests to another fi" + + "le or URL."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(153, 25); + this.label3.TabIndex = 2; + this.label3.Text = "HTTP Redirect"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // HttpRedirectPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "HttpRedirectPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private CheckBox cbRedirect; + private ContextMenuStrip cmsActionPanel; + private GroupBox groupBox1; + private ComboBox cbCode; + private Label label5; + private CheckBox cbThis; + private CheckBox cbExact; + private Label label4; + private TextBox txtLink; + } +} diff --git a/JexusManager.Features.HttpRedirect/HttpRedirectPage.cs b/JexusManager.Features.HttpRedirect/HttpRedirectPage.cs new file mode 100644 index 00000000..f42740cd --- /dev/null +++ b/JexusManager.Features.HttpRedirect/HttpRedirectPage.cs @@ -0,0 +1,213 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpRedirect +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class HttpRedirectPage : ModuleDialogPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly HttpRedirectPage _owner; + + public PageTaskList(HttpRedirectPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private class CodeItem + { + public long Code { get; set; } + public string Description { get; set; } + + public CodeItem(long code, string description) + { + this.Code = code; + this.Description = description; + } + + public override string ToString() + { + return this.Description; + } + } + + private HttpRedirectFeature _feature; + private bool _hasChanges; + private bool _initialized; + private TaskList _taskList; + + public HttpRedirectPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new HttpRedirectFeature(this.Module, null, null); + _feature.HttpRedirectSettingsUpdated = this.Refresh; + _feature.Load(); + + base.Initialize(navigationData); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override bool ApplyChanges() + { + _feature.Enabled = cbRedirect.Checked; + _feature.Link = txtLink.Text; + _feature.Exact = cbExact.Checked; + _feature.OnlyRoot = cbThis.Checked; + _feature.Mode = ((CodeItem)(cbCode.SelectedItem)).Code; + if (!_feature.ApplyChanges()) + { + return false; + } + + this.ClearChanges(); + return true; + } + + protected override void CancelChanges() + { + _feature.CancelChanges(); + this.ClearChanges(); + } + + protected override bool HasChanges + { + get { return _hasChanges; } + } + + protected override bool CanApplyChanges + { + get { return true; } + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + this.Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + this.Refresh(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + protected override void OnRefresh() + { + if (!_hasChanges) + { + if (!_initialized) + { + var lines = new List + { + new CodeItem(302, "Found(302)"), + new CodeItem(301, "Permanent(301)"), + new CodeItem(307, "Temporary(307)"), + new CodeItem(308, "Permanent Redirect (308)") + }; + foreach (var line in lines) + { + if (_feature.SupportedModes.Contains(line.Code)) + { + cbCode.Items.Add(line); + } + } + } + + cbRedirect.Checked = _feature.Enabled; + txtLink.Text = _feature.Link; + cbExact.Checked = _feature.Exact; + cbThis.Checked = _feature.OnlyRoot; + foreach (CodeItem item in cbCode.Items) + { + if (item.Code == _feature.Mode) + { + cbCode.SelectedItem = item; + } + } + + _initialized = true; + } + + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + } + + private void CbSslCheckedChanged(object sender, EventArgs e) + { + this.InformChanges(); + txtLink.Enabled = cbCode.Enabled = cbExact.Enabled = cbThis.Enabled = cbRedirect.Checked; + } + + private void CbExactCheckedChanged(object sender, EventArgs e) + { + this.InformChanges(); + } + + private void CbCodeSelectedIndexChanged(object sender, EventArgs e) + { + this.InformChanges(); + } + + private void TxtLinkTextChanged(object sender, EventArgs e) + { + this.InformChanges(); + } + } +} diff --git a/JexusManager.Features.HttpRedirect/HttpRedirectPage.resx b/JexusManager.Features.HttpRedirect/HttpRedirectPage.resx new file mode 100644 index 00000000..950be5d2 --- /dev/null +++ b/JexusManager.Features.HttpRedirect/HttpRedirectPage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.HttpRedirect/HttpRedirectSettingsSavedEventHandler.cs b/JexusManager.Features.HttpRedirect/HttpRedirectSettingsSavedEventHandler.cs new file mode 100644 index 00000000..fd47d877 --- /dev/null +++ b/JexusManager.Features.HttpRedirect/HttpRedirectSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.HttpRedirect +{ + public delegate void HttpRedirectSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.HttpRedirect/JexusManager.Features.HttpRedirect.csproj b/JexusManager.Features.HttpRedirect/JexusManager.Features.HttpRedirect.csproj new file mode 100644 index 00000000..7896f56f --- /dev/null +++ b/JexusManager.Features.HttpRedirect/JexusManager.Features.HttpRedirect.csproj @@ -0,0 +1,103 @@ + + + + + Debug + AnyCPU + {C0C1DDE1-6447-4D3E-BC1A-F62CABBDBB53} + Library + Properties + JexusManager.Features.HttpRedirect + JexusManager.Features.HttpRedirect + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + + + + + + + + + + + + + UserControl + + + HttpRedirectPage.cs + + + + + True + True + Resources.resx + + + + + HttpRedirectPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.HttpRedirect/JexusManager.snk b/JexusManager.Features.HttpRedirect/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.HttpRedirect/JexusManager.snk differ diff --git a/JexusManager.Features.HttpRedirect/Properties/AssemblyInfo.cs b/JexusManager.Features.HttpRedirect/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..ebd24a57 --- /dev/null +++ b/JexusManager.Features.HttpRedirect/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.HttpRedirect")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.HttpRedirect")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("c0c1dde1-6447-4d3e-bc1a-f62cabbdbb53")] + +// 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")] diff --git a/JexusManager.Features.HttpRedirect/Properties/Resources.Designer.cs b/JexusManager.Features.HttpRedirect/Properties/Resources.Designer.cs new file mode 100644 index 00000000..e8319f81 --- /dev/null +++ b/JexusManager.Features.HttpRedirect/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.HttpRedirect.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.HttpRedirect.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap http_redirect_36 { + get { + object obj = ResourceManager.GetObject("http_redirect_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.HttpRedirect/Properties/Resources.resx b/JexusManager.Features.HttpRedirect/Properties/Resources.resx new file mode 100644 index 00000000..97b03f61 --- /dev/null +++ b/JexusManager.Features.HttpRedirect/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\http_redirect_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.HttpRedirect/Resources/http_redirect_36.png b/JexusManager.Features.HttpRedirect/Resources/http_redirect_36.png new file mode 100644 index 00000000..94f84884 Binary files /dev/null and b/JexusManager.Features.HttpRedirect/Resources/http_redirect_36.png differ diff --git a/JexusManager.Features.IpSecurity/DynamicDialog.Designer.cs b/JexusManager.Features.IpSecurity/DynamicDialog.Designer.cs new file mode 100644 index 00000000..5ea585f5 --- /dev/null +++ b/JexusManager.Features.IpSecurity/DynamicDialog.Designer.cs @@ -0,0 +1,187 @@ +namespace JexusManager.Features.IpSecurity +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class DynamicDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cbConcurrent = new System.Windows.Forms.CheckBox(); + this.cbInterval = new System.Windows.Forms.CheckBox(); + this.cbLogging = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.txtConcurrent = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtNumer = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPeriod = new System.Windows.Forms.TextBox(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // cbConcurrent + // + this.cbConcurrent.AutoSize = true; + this.cbConcurrent.Location = new System.Drawing.Point(31, 22); + this.cbConcurrent.Name = "cbConcurrent"; + this.cbConcurrent.Size = new System.Drawing.Size(317, 17); + this.cbConcurrent.TabIndex = 0; + this.cbConcurrent.Text = "Deny IP Address based on the number of concurrent requests"; + this.cbConcurrent.UseVisualStyleBackColor = true; + // + // cbInterval + // + this.cbInterval.AutoSize = true; + this.cbInterval.Location = new System.Drawing.Point(31, 141); + this.cbInterval.Name = "cbInterval"; + this.cbInterval.Size = new System.Drawing.Size(362, 17); + this.cbInterval.TabIndex = 1; + this.cbInterval.Text = "Deny IP Address based on the number of requests over a period of time"; + this.cbInterval.UseVisualStyleBackColor = true; + // + // cbLogging + // + this.cbLogging.AutoSize = true; + this.cbLogging.Location = new System.Drawing.Point(31, 298); + this.cbLogging.Name = "cbLogging"; + this.cbLogging.Size = new System.Drawing.Size(154, 17); + this.cbLogging.TabIndex = 2; + this.cbLogging.Text = "Enable Logging Only Mode"; + this.cbLogging.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(56, 56); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(201, 13); + this.label1.TabIndex = 3; + this.label1.Text = "Maximum number of concurrent requests:"; + // + // txtConcurrent + // + this.txtConcurrent.Enabled = false; + this.txtConcurrent.Location = new System.Drawing.Point(59, 81); + this.txtConcurrent.Name = "txtConcurrent"; + this.txtConcurrent.Size = new System.Drawing.Size(140, 20); + this.txtConcurrent.TabIndex = 4; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(56, 173); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(147, 13); + this.label2.TabIndex = 5; + this.label2.Text = "Maximum number of requests:"; + // + // txtNumer + // + this.txtNumer.Enabled = false; + this.txtNumer.Location = new System.Drawing.Point(59, 199); + this.txtNumer.Name = "txtNumer"; + this.txtNumer.Size = new System.Drawing.Size(140, 20); + this.txtNumer.TabIndex = 6; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(56, 235); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(142, 13); + this.label3.TabIndex = 7; + this.label3.Text = "Time Period (in milliseconds):"; + // + // txtPeriod + // + this.txtPeriod.Enabled = false; + this.txtPeriod.Location = new System.Drawing.Point(59, 262); + this.txtPeriod.Name = "txtPeriod"; + this.txtPeriod.Size = new System.Drawing.Size(140, 20); + this.txtPeriod.TabIndex = 8; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(442, 356); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 9; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(346, 356); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 10; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // DynamicDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(549, 391); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.txtPeriod); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtNumer); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtConcurrent); + this.Controls.Add(this.label1); + this.Controls.Add(this.cbLogging); + this.Controls.Add(this.cbInterval); + this.Controls.Add(this.cbConcurrent); + this.Name = "DynamicDialog"; + this.Text = "Dynamic IP Restriction Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.DynamicDialog_HelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private CheckBox cbConcurrent; + private CheckBox cbInterval; + private CheckBox cbLogging; + private Label label1; + private TextBox txtConcurrent; + private Label label2; + private TextBox txtNumer; + private Label label3; + private TextBox txtPeriod; + private Button btnCancel; + private Button btnOK; + } +} diff --git a/JexusManager.Features.IpSecurity/DynamicDialog.cs b/JexusManager.Features.IpSecurity/DynamicDialog.cs new file mode 100644 index 00000000..1be6efb5 --- /dev/null +++ b/JexusManager.Features.IpSecurity/DynamicDialog.cs @@ -0,0 +1,107 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + public partial class DynamicDialog : DialogForm + { + public DynamicDialog(IServiceProvider serviceProvider, ConfigurationSection section) + : base(serviceProvider) + { + InitializeComponent(); + var concurrent = section.ChildElements["denyByConcurrentRequests"]; + cbConcurrent.Checked = (bool)concurrent["enabled"]; + txtConcurrent.Text = concurrent["maxConcurrentRequests"].ToString(); + var rate = section.ChildElements["denyByRequestRate"]; + cbInterval.Checked = (bool)rate["enabled"]; + txtNumer.Text = rate["maxRequests"].ToString(); + txtPeriod.Text = rate["requestIntervalInMilliseconds"].ToString(); + cbLogging.Checked = (bool)section["enableLoggingOnlyMode"]; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + var load = Observable.FromEventPattern(this, "Load"); + + container.Add( + Observable.FromEventPattern(cbInterval, "CheckedChanged") + .Merge(load) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + txtPeriod.Enabled = txtNumer.Enabled = cbInterval.Checked; + })); + + container.Add( + Observable.FromEventPattern(cbConcurrent, "CheckedChanged") + .Merge(load) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + txtConcurrent.Enabled = cbConcurrent.Checked; + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + uint result; + if (!uint.TryParse(txtConcurrent.Text, out result)) + { + ShowMessage( + "Input string was not in a correct format", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + concurrent["enabled"] = cbConcurrent.Checked; + concurrent["maxConcurrentRequests"] = result; + rate["enabled"] = cbInterval.Checked; + if (!uint.TryParse(txtNumer.Text, out result)) + { + // TODO: show validator error. + ShowMessage( + "Input string was not in a correct format", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + rate["maxRequests"] = result; + if (!uint.TryParse(txtNumer.Text, out result)) + { + // TODO: show validator error. + ShowMessage( + "Input string was not in a correct format", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + rate["requestIntervalInMilliseconds"] = result; + section["enableLoggingOnlyMode"] = cbLogging.Checked; + DialogResult = DialogResult.OK; + })); + } + + private void DynamicDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210513"); + } + } +} diff --git a/JexusManager.Features.IpSecurity/DynamicDialog.resx b/JexusManager.Features.IpSecurity/DynamicDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.IpSecurity/DynamicDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.IpSecurity/IpSecurityFeature.cs b/JexusManager.Features.IpSecurity/IpSecurityFeature.cs new file mode 100644 index 00000000..99519c92 --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecurityFeature.cs @@ -0,0 +1,259 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.IpSecurity +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + using Microsoft.Web.Management.Server; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class IpSecurityFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly IpSecurityFeature _owner; + + public FeatureTaskList(IpSecurityFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("AddAllow", "Add Allow Entry...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("AddDeny", "Add Deny Entry...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); // IMPORTANT: this is where IIS Manager was wrong + result.Add(new MethodTaskItem("Set", "Edit Feature Settings...", string.Empty).SetUsage()); + if (_owner.CanRevert) + { + result.Add(new MethodTaskItem("Revert", "Revert To Parent", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("View", "View Ordered List...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Dynamic", "Edit Dynamic Restriction Settings...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddAllow() + { + _owner.AddAllow(); + } + + [Obfuscation(Exclude = true)] + public void AddDeny() + { + _owner.AddDeny(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + + [Obfuscation(Exclude = true)] + public void Revert() + { + _owner.Revert(); + } + + [Obfuscation(Exclude = true)] + public void View() + { + _owner.View(); + } + [Obfuscation(Exclude = true)] + public void Dynamic() + { + _owner.Dynamic(); + } + } + + public IpSecurityFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + CanRevert = service.Scope != ManagementScope.Server; + LoadItems(); + } + + public void AddAllow() + { + var dialog = new NewRestrictionDialog(Module, true, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddDeny() + { + var dialog = new NewRestrictionDialog(Module, false, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected restriction?", Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Revert() + { + if (!CanRevert) + { + throw new InvalidOperationException("Revert operation cannot be done at server level"); + } + + var dialog = (IManagementUIService)GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "Reverting to the parent configuration will result in the loss of all settings in the local configuration file for this feature. Are you sure you want to continue?", + Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + RevertItems(); + } + + public void Set() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/ipSecurity"); + var dialog = new SetRestrictionsDialog(Module, section); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + } + + public void View() + { + } + + public void Dynamic() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/dynamicIpSecurity"); + if (section == null) + { + // TODO: display a friendly error message. + return; + } + + var dialog = new DynamicDialog(Module, section); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection section = service.GetSection("system.webServer/security/ipSecurity", null, false); + return section.GetCollection(); + } + + protected override void OnSettingsSaved() + { + IpSecuritySettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210513"); + return false; + } + + public bool CanRevert { get; private set; } + + public IpSecuritySettingsSavedEventHandler IpSecuritySettingsUpdated { get; set; } + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "IP Address and Domain Restrictions"; } + } + } +} diff --git a/JexusManager.Features.IpSecurity/IpSecurityItem.cs b/JexusManager.Features.IpSecurity/IpSecurityItem.cs new file mode 100644 index 00000000..3a640ef9 --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecurityItem.cs @@ -0,0 +1,49 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + using Microsoft.Web.Administration; + + internal class IpSecurityItem : IItem + { + public IpSecurityItem(ConfigurationElement element) + { + Element = element; + Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + if (element == null) + { + Mask = "255.255.255.255"; + return; + } + + Address = (string)element["ipAddress"]; + Mask = (string)element["subnetMask"]; + this.Allowed = (bool)element["allowed"]; + } + + public ConfigurationElement Element { get; set; } + public string Address { get; set; } + public string Mask { get; set; } + public bool Allowed { get; set; } + public string Flag { get; set; } + + public void Apply() + { + Element["ipAddress"] = Address; + Element["subnetMask"] = Mask; + Element["allowed"] = this.Allowed; + } + + public bool Equals(IpSecurityItem other) + { + return other != null && other.Address == Address && other.Mask == Mask && other.Allowed == this.Allowed; + } + + public bool Match(IpSecurityItem other) + { + return other != null && other.Address == Address && other.Mask == Mask; + } + } +} diff --git a/JexusManager.Features.IpSecurity/IpSecurityModule.cs b/JexusManager.Features.IpSecurity/IpSecurityModule.cs new file mode 100644 index 00000000..07f278e1 --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecurityModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class IpSecurityModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(IpSecurityPage), "IP Address and Domain Restrictions", + "Restrict or grant access to Web content based on IP addresses or domain names", Resources.ip_restriction_36, + Resources.ip_restriction_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.IpSecurity/IpSecurityModuleProvider.cs b/JexusManager.Features.IpSecurity/IpSecurityModuleProvider.cs new file mode 100644 index 00000000..9558dac2 --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecurityModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + using System; + + using Microsoft.Web.Management.Server; + + public class IpSecurityModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(IpSecurityModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.IpSecurity/IpSecurityPage.Designer.cs b/JexusManager.Features.IpSecurity/IpSecurityPage.Designer.cs new file mode 100644 index 00000000..f77beb13 --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecurityPage.Designer.cs @@ -0,0 +1,290 @@ +namespace JexusManager.Features.IpSecurity +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class IpSecurityPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chMode = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chRequestor = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chMode, + this.chRequestor, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // chMode + // + this.chMode.Text = "Mode"; + this.chMode.Width = 80; + // + // chRequestor + // + this.chRequestor.Text = "Requestor"; + this.chRequestor.Width = 150; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(653, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to restrict or grant access to Web content based on IP addresses" + + " or domain names. Set the restrictions in order of priority."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(356, 25); + this.label3.TabIndex = 2; + this.label3.Text = "IP Address and Domain Restrictions"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // IpSecurityPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "IpSecurityPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chMode; + private ColumnHeader chRequestor; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + } +} diff --git a/JexusManager.Features.IpSecurity/IpSecurityPage.cs b/JexusManager.Features.IpSecurity/IpSecurityPage.cs new file mode 100644 index 00000000..01516bd4 --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecurityPage.cs @@ -0,0 +1,129 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class IpSecurityPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly IpSecurityPage _owner; + + public PageTaskList(IpSecurityPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class IpSecurityListViewItem : ListViewItem + { + public IpSecurityItem Item { get; } + private readonly IpSecurityPage _page; + + public IpSecurityListViewItem(IpSecurityItem item, IpSecurityPage page) + : base(item.Allowed ? "Allow" : "Deny") + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Mask == string.Empty ? item.Address : string.Format("{0}({1})", item.Address, item.Mask))); + SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private IpSecurityFeature _feature; + private PageTaskList _taskList; + + public IpSecurityPage() + { + InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new IpSecurityFeature(Module); + _feature.IpSecuritySettingsUpdated = InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new IpSecurityListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + Refresh(); + return; + } + + foreach (IpSecurityListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((IpSecurityListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.IpSecurity/IpSecurityPage.resx b/JexusManager.Features.IpSecurity/IpSecurityPage.resx new file mode 100644 index 00000000..464d127c --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecurityPage.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 497, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.IpSecurity/IpSecuritySettingsSavedEventHandler.cs b/JexusManager.Features.IpSecurity/IpSecuritySettingsSavedEventHandler.cs new file mode 100644 index 00000000..003f3509 --- /dev/null +++ b/JexusManager.Features.IpSecurity/IpSecuritySettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + public delegate void IpSecuritySettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.IpSecurity/JexusManager.Features.IpSecurity.csproj b/JexusManager.Features.IpSecurity/JexusManager.Features.IpSecurity.csproj new file mode 100644 index 00000000..90946f04 --- /dev/null +++ b/JexusManager.Features.IpSecurity/JexusManager.Features.IpSecurity.csproj @@ -0,0 +1,148 @@ + + + + + Debug + AnyCPU + {361E2F47-289F-480F-B976-A6E3F7D96EDC} + Library + Properties + JexusManager.Features.IpSecurity + JexusManager.Features.IpSecurity + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + Form + + + DynamicDialog.cs + + + + + + + UserControl + + + IpSecurityPage.cs + + + + Form + + + NewRestrictionDialog.cs + + + + True + True + Resources.resx + + + Form + + + SetRestrictionsDialog.cs + + + + + DynamicDialog.cs + + + IpSecurityPage.cs + + + NewRestrictionDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SetRestrictionsDialog.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.IpSecurity/JexusManager.snk b/JexusManager.Features.IpSecurity/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.IpSecurity/JexusManager.snk differ diff --git a/JexusManager.Features.IpSecurity/NewRestrictionDialog.Designer.cs b/JexusManager.Features.IpSecurity/NewRestrictionDialog.Designer.cs new file mode 100644 index 00000000..869d5a10 --- /dev/null +++ b/JexusManager.Features.IpSecurity/NewRestrictionDialog.Designer.cs @@ -0,0 +1,170 @@ +namespace JexusManager.Features.IpSecurity +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewRestrictionDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.rbAddress = new System.Windows.Forms.RadioButton(); + this.rbRange = new System.Windows.Forms.RadioButton(); + this.txtAddress = new System.Windows.Forms.TextBox(); + this.txtRange = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtMask = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(327, 336); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(226, 336); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 9); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(280, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Allow access for the following IP address or domain name:"; + // + // rbAddress + // + this.rbAddress.AutoSize = true; + this.rbAddress.Checked = true; + this.rbAddress.Location = new System.Drawing.Point(15, 34); + this.rbAddress.Name = "rbAddress"; + this.rbAddress.Size = new System.Drawing.Size(116, 17); + this.rbAddress.TabIndex = 3; + this.rbAddress.TabStop = true; + this.rbAddress.Text = "Specify IP address:"; + this.rbAddress.UseVisualStyleBackColor = true; + // + // rbRange + // + this.rbRange.AutoSize = true; + this.rbRange.Location = new System.Drawing.Point(15, 92); + this.rbRange.Name = "rbRange"; + this.rbRange.Size = new System.Drawing.Size(108, 17); + this.rbRange.TabIndex = 4; + this.rbRange.Text = "IP address range:"; + this.rbRange.UseVisualStyleBackColor = true; + // + // txtAddress + // + this.txtAddress.Location = new System.Drawing.Point(33, 57); + this.txtAddress.Name = "txtAddress"; + this.txtAddress.Size = new System.Drawing.Size(314, 20); + this.txtAddress.TabIndex = 5; + // + // txtRange + // + this.txtRange.Enabled = false; + this.txtRange.Location = new System.Drawing.Point(33, 125); + this.txtRange.Name = "txtRange"; + this.txtRange.Size = new System.Drawing.Size(314, 20); + this.txtRange.TabIndex = 6; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(33, 164); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(77, 13); + this.label2.TabIndex = 7; + this.label2.Text = "Mask or Prefix:"; + // + // txtMask + // + this.txtMask.Enabled = false; + this.txtMask.Location = new System.Drawing.Point(33, 192); + this.txtMask.Name = "txtMask"; + this.txtMask.Size = new System.Drawing.Size(314, 20); + this.txtMask.TabIndex = 8; + // + // NewRestrictionDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(434, 371); + this.Controls.Add(this.txtMask); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtRange); + this.Controls.Add(this.txtAddress); + this.Controls.Add(this.rbRange); + this.Controls.Add(this.rbAddress); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.HelpButton = true; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "NewRestrictionDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Add Allow Restriction Rule"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private RadioButton rbAddress; + private RadioButton rbRange; + private TextBox txtAddress; + private TextBox txtRange; + private Label label2; + private TextBox txtMask; + } +} diff --git a/JexusManager.Features.IpSecurity/NewRestrictionDialog.cs b/JexusManager.Features.IpSecurity/NewRestrictionDialog.cs new file mode 100644 index 00000000..890c06b6 --- /dev/null +++ b/JexusManager.Features.IpSecurity/NewRestrictionDialog.cs @@ -0,0 +1,135 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Net; + using System.Net.Sockets; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewRestrictionDialog : DialogForm + { + public NewRestrictionDialog(IServiceProvider serviceProvider, bool allowed, IpSecurityFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = allowed ? "Add Allow Restriction Rule" : "Add Deny Restriction Rule"; + txtDescription.Text = allowed + ? "Allow access for the following IP address or domain name:" + : "Deny access for the following IP address or domain name:"; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(rbAddress, "CheckedChanged") + .Merge(Observable.FromEventPattern(rbRange, "CheckedChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + txtAddress.Enabled = rbAddress.Checked; + txtRange.Enabled = rbRange.Checked; + txtMask.Enabled = rbRange.Checked; + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (rbAddress.Checked) + { + IPAddress result; + var passed = IPAddress.TryParse(txtAddress.Text, out result); + if (!passed) + { + MessageBox.Show(string.Format("'{0}' is an invalid IP address.", txtAddress.Text), Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return; + } + + Item = new IpSecurityItem(null); + Item.Address = txtAddress.Text; + Item.Mask = string.Empty; + Item.Allowed = allowed; + if (feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A restriction for this domain name or IP address already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + return; + } + + IPAddress result1; + var passed1 = IPAddress.TryParse(txtRange.Text, out result1); + if (!passed1) + { + ShowMessage( + string.Format("'{0}' is an invalid IP address.", txtRange.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation, + MessageBoxDefaultButton.Button1); + return; + } + + var passed2 = IPAddress.TryParse(txtMask.Text, out result1); + if (!passed2) + { + int value; + var passed3 = int.TryParse(txtMask.Text, out value); + if (!passed3 || + (result1.AddressFamily == AddressFamily.InterNetwork + && (value < 0 || value > 32)) || + (result1.AddressFamily == AddressFamily.InterNetworkV6 + && (value < 0 || value > 128))) + { + ShowMessage( + string.Format( + "'{0}' is an invalid subnet mask. It must be a valid IP address or an integer value between 0-32 for IPv4 ad 0-128 for IPv6.", + txtMask.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation, + MessageBoxDefaultButton.Button1); + return; + } + } + + Item = new IpSecurityItem(null); + Item.Address = txtRange.Text; + Item.Mask = txtMask.Text; + Item.Allowed = allowed; + if (feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A restriction for this domain name or IP address already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + } + + public IpSecurityItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210513"); + } + } +} diff --git a/JexusManager.Features.IpSecurity/NewRestrictionDialog.resx b/JexusManager.Features.IpSecurity/NewRestrictionDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.IpSecurity/NewRestrictionDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.IpSecurity/Properties/AssemblyInfo.cs b/JexusManager.Features.IpSecurity/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..44e4bfa6 --- /dev/null +++ b/JexusManager.Features.IpSecurity/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.IpSecurity")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.IpSecurity")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("361e2f47-289f-480f-b976-a6e3f7d96edc")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.IpSecurity/Properties/Resources.Designer.cs b/JexusManager.Features.IpSecurity/Properties/Resources.Designer.cs new file mode 100644 index 00000000..f6dfe2ed --- /dev/null +++ b/JexusManager.Features.IpSecurity/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.IpSecurity.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.IpSecurity.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ip_restriction_36 { + get { + object obj = ResourceManager.GetObject("ip_restriction_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.IpSecurity/Properties/Resources.resx b/JexusManager.Features.IpSecurity/Properties/Resources.resx new file mode 100644 index 00000000..7b5dda3b --- /dev/null +++ b/JexusManager.Features.IpSecurity/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\ip_restriction_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.IpSecurity/Resources/ip_restriction_36.png b/JexusManager.Features.IpSecurity/Resources/ip_restriction_36.png new file mode 100644 index 00000000..641bbbd6 Binary files /dev/null and b/JexusManager.Features.IpSecurity/Resources/ip_restriction_36.png differ diff --git a/JexusManager.Features.IpSecurity/SetRestrictionsDialog.Designer.cs b/JexusManager.Features.IpSecurity/SetRestrictionsDialog.Designer.cs new file mode 100644 index 00000000..0f83cf0e --- /dev/null +++ b/JexusManager.Features.IpSecurity/SetRestrictionsDialog.Designer.cs @@ -0,0 +1,161 @@ +namespace JexusManager.Features.IpSecurity +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class SetRestrictionsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.cbAccess = new System.Windows.Forms.ComboBox(); + this.cbDomain = new System.Windows.Forms.CheckBox(); + this.cbProxy = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.cbAction = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(242, 201); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(146, 201); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(150, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Access for unspecified clients:"; + // + // cbAccess + // + this.cbAccess.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbAccess.FormattingEnabled = true; + this.cbAccess.Items.AddRange(new object[] { + "Allow", + "Deny"}); + this.cbAccess.Location = new System.Drawing.Point(12, 25); + this.cbAccess.Name = "cbAccess"; + this.cbAccess.Size = new System.Drawing.Size(224, 21); + this.cbAccess.TabIndex = 3; + // + // cbDomain + // + this.cbDomain.AutoSize = true; + this.cbDomain.Location = new System.Drawing.Point(12, 64); + this.cbDomain.Name = "cbDomain"; + this.cbDomain.Size = new System.Drawing.Size(149, 17); + this.cbDomain.TabIndex = 4; + this.cbDomain.Text = "Enable domain restrictions"; + this.cbDomain.UseVisualStyleBackColor = true; + // + // cbProxy + // + this.cbProxy.AutoSize = true; + this.cbProxy.Location = new System.Drawing.Point(12, 96); + this.cbProxy.Name = "cbProxy"; + this.cbProxy.Size = new System.Drawing.Size(118, 17); + this.cbProxy.TabIndex = 5; + this.cbProxy.Text = "Enable Proxy Mode"; + this.cbProxy.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 131); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(95, 13); + this.label2.TabIndex = 6; + this.label2.Text = "Deny Action Type:"; + // + // cbAction + // + this.cbAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbAction.FormattingEnabled = true; + this.cbAction.Items.AddRange(new object[] { + "Unauthorized", + "Forbidden", + "Not Found", + "Abort"}); + this.cbAction.Location = new System.Drawing.Point(12, 147); + this.cbAction.Name = "cbAction"; + this.cbAction.Size = new System.Drawing.Size(178, 21); + this.cbAction.TabIndex = 7; + // + // SetRestrictionsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 236); + this.Controls.Add(this.cbAction); + this.Controls.Add(this.label2); + this.Controls.Add(this.cbProxy); + this.Controls.Add(this.cbDomain); + this.Controls.Add(this.cbAccess); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "SetRestrictionsDialog"; + this.Text = "Edit IP and Domain Restrictions Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.SetRestrictionsDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private ComboBox cbAccess; + private CheckBox cbDomain; + private CheckBox cbProxy; + private Label label2; + private ComboBox cbAction; + } +} diff --git a/JexusManager.Features.IpSecurity/SetRestrictionsDialog.cs b/JexusManager.Features.IpSecurity/SetRestrictionsDialog.cs new file mode 100644 index 00000000..2a99c81f --- /dev/null +++ b/JexusManager.Features.IpSecurity/SetRestrictionsDialog.cs @@ -0,0 +1,96 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IpSecurity +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + public partial class SetRestrictionsDialog : DialogForm + { + public SetRestrictionsDialog(IServiceProvider serviceProvider, ConfigurationSection section) + : base(serviceProvider) + { + InitializeComponent(); + cbDomain.Checked = (bool)section["enableReverseDns"]; + cbAccess.SelectedIndex = (bool)section["allowUnlisted"] ? 0 : 1; + cbProxy.Enabled = section.Schema.AttributeSchemas["enabled"] != null; + if (cbProxy.Enabled) + { + cbProxy.Checked = (bool)section["enableProxyMode"]; + } + + cbAction.Enabled = section.Schema.AttributeSchemas["denyAction"] != null; + if (cbAction.Enabled) + { + var action = (long)section["denyAction"]; + if (action == 0L) + { + cbAction.SelectedIndex = 0; + } + else if (action == 401L) + { + cbAction.SelectedIndex = 1; + } + else if (action == 403L) + { + cbAction.SelectedIndex = 2; + } + else + { + cbAction.SelectedIndex = 3; + } + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + section["enableReverseDns"] = cbDomain.Checked; + section["allowUnlisted"] = cbAccess.SelectedIndex == 0; + if (cbProxy.Enabled) + { + section["enableProxyMode"] = cbProxy.Checked; + } + + if (cbAction.Enabled) + { + if (cbAction.SelectedIndex == 0) + { + section["denyAction"] = 0L; + } + else if (cbAction.SelectedIndex == 1) + { + section["denyAction"] = 401L; + } + else if (cbAction.SelectedIndex == 2) + { + section["denyAction"] = 403L; + } + else + { + section["denyAction"] = 404L; + } + } + + DialogResult = DialogResult.OK; + })); + } + + private void SetRestrictionsDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210513"); + } + } +} diff --git a/JexusManager.Features.IpSecurity/SetRestrictionsDialog.resx b/JexusManager.Features.IpSecurity/SetRestrictionsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.IpSecurity/SetRestrictionsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.IpSecurity/packages.config b/JexusManager.Features.IpSecurity/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.IpSecurity/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionFeature.cs b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionFeature.cs new file mode 100644 index 00000000..4c391a94 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionFeature.cs @@ -0,0 +1,227 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class IsapiCgiRestrictionFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly IsapiCgiRestrictionFeature _owner; + + public FeatureTaskList(IsapiCgiRestrictionFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + if (_owner.SelectedItem.Allowed) + { + result.Add(new MethodTaskItem("Deny", "Deny...", string.Empty).SetUsage()); + } + else + { + result.Add(new MethodTaskItem("Allow", "Allow...", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Edit Feature Settings...", string.Empty).SetUsage()); + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + + [Obfuscation(Exclude = true)] + public void Deny() + { + _owner.Deny(); + } + + [Obfuscation(Exclude = true)] + public void Allow() + { + _owner.Allow(); + } + } + + public IsapiCgiRestrictionFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection section = service.GetSection("system.webServer/security/isapiCgiRestriction", null, false); + return section.GetCollection(); + } + + public void Add() + { + var dialog = new NewRestrictionDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Deny() + { + SetAllowed(false); + } + + public void Allow() + { + this.SetAllowed(true); + } + + private void SetAllowed(bool allowed) + { + this.SelectedItem.Allowed = allowed; + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + service.ServerManager.CommitChanges(); + this.OnSettingsSaved(); + } + + public void Set() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/isapiCgiRestriction"); + var dialog = new SettingsDialog(this.Module, section); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + this.OnSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected restriction?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + var dialog = new NewRestrictionDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + protected override void OnSettingsSaved() + { + this.IsapiCgiRestrictionSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210515"); + return false; + } + + public IsapiCgiRestrictionSettingsSavedEventHandler IsapiCgiRestrictionSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "ISAPI and CGI Restrictions"; } + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionItem.cs b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionItem.cs new file mode 100644 index 00000000..51d4e7a5 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionItem.cs @@ -0,0 +1,54 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiCgiRestriction +{ + using Microsoft.Web.Administration; + + internal class IsapiCgiRestrictionItem : IItem + { + public IsapiCgiRestrictionItem(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inhertied"; + if (element == null) + { + return; + } + + this.Description = (string)element["description"]; + this.Path = (string)element["path"]; + this.Allowed = (bool)element["allowed"]; + } + + public bool Allowed { get; set; } + + public string Path { get; set; } + + public string Description { get; set; } + + public ConfigurationElement Element { get; set; } + + public string Flag { get; set; } + + public bool Equals(IsapiCgiRestrictionItem other) + { + // all properties + return this.Match(other) && other.Description == this.Description; + } + + public void Apply() + { + Element["description"] = Description; + Element["path"] = Path; + Element["allowed"] = Allowed; + } + + public bool Match(IsapiCgiRestrictionItem other) + { + // match combined keys. + return other != null && other.Path == this.Path; + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionModule.cs b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionModule.cs new file mode 100644 index 00000000..09263d40 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class IsapiCgiRestrictionModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(IsapiCgiRestrictionPage), "ISAPI and CGI Restrictions", + "Restrict or enable ISAPI and CGI extensions on the Web server", Resources.isapi_cgi_restriction_36, + Resources.isapi_cgi_restriction_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionModuleProvider.cs b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionModuleProvider.cs new file mode 100644 index 00000000..bef8806f --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System; + + using Microsoft.Web.Management.Server; + + public class IsapiCgiRestrictionModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(IsapiCgiRestrictionModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return scope == ManagementScope.Server; + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.Designer.cs b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.Designer.cs new file mode 100644 index 00000000..4212c02d --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.Designer.cs @@ -0,0 +1,290 @@ +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class IsapiCgiRestrictionPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chDescription = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chRestriction = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chPath = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chDescription, + this.chRestriction, + this.chPath}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + // + // chDescription + // + this.chDescription.Text = "Description"; + this.chDescription.Width = 125; + // + // chRestriction + // + this.chRestriction.Text = "Restriction"; + this.chRestriction.Width = 120; + // + // chPath + // + this.chPath.Text = "Path"; + this.chPath.Width = 400; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(426, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to specify the ISAPI and CGI extensions that can run on the Web " + + "server."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(267, 25); + this.label3.TabIndex = 2; + this.label3.Text = "ISAPI and CGI Restrictions"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // IsapiCgiRestrictionPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "IsapiCgiRestrictionPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chDescription; + private ColumnHeader chRestriction; + private ColumnHeader chPath; + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.cs b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.cs new file mode 100644 index 00000000..6720f156 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.cs @@ -0,0 +1,130 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class IsapiCgiRestrictionPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly IsapiCgiRestrictionPage _owner; + + public PageTaskList(IsapiCgiRestrictionPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class IsapiCgiRestrictionListViewItem : ListViewItem + { + public IsapiCgiRestrictionItem Item { get; } + + private readonly IsapiCgiRestrictionPage _page; + + public IsapiCgiRestrictionListViewItem(IsapiCgiRestrictionItem item, IsapiCgiRestrictionPage page) + : base(item.Description) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Allowed ? "Allowed" : "Not Allowed")); + this.SubItems.Add(new ListViewSubItem(this, item.Path)); + } + } + + private IsapiCgiRestrictionFeature _feature; + private PageTaskList _taskList; + + public IsapiCgiRestrictionPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new IsapiCgiRestrictionFeature(this.Module); + _feature.IsapiCgiRestrictionSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new IsapiCgiRestrictionListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (IsapiCgiRestrictionListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((IsapiCgiRestrictionListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.resx b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.resx new file mode 100644 index 00000000..7a2db0ac --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 497, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionSettingsSavedEventHandler.cs b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionSettingsSavedEventHandler.cs new file mode 100644 index 00000000..c0245317 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/IsapiCgiRestrictionSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiCgiRestriction +{ + public delegate void IsapiCgiRestrictionSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.IsapiCgiRestriction/JexusManager.Features.IsapiCgiRestriction.csproj b/JexusManager.Features.IsapiCgiRestriction/JexusManager.Features.IsapiCgiRestriction.csproj new file mode 100644 index 00000000..2a4acccb --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/JexusManager.Features.IsapiCgiRestriction.csproj @@ -0,0 +1,139 @@ + + + + + Debug + AnyCPU + {1571B862-8CD9-4314-9CCF-B1339D50298B} + Library + Properties + JexusManager.Features.IsapiCgiRestriction + JexusManager.Features.IsapiCgiRestriction + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + + UserControl + + + IsapiCgiRestrictionPage.cs + + + + Form + + + NewRestrictionDialog.cs + + + + True + True + Resources.resx + + + Form + + + SettingsDialog.cs + + + + + IsapiCgiRestrictionPage.cs + + + NewRestrictionDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsDialog.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiCgiRestriction/JexusManager.snk b/JexusManager.Features.IsapiCgiRestriction/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.IsapiCgiRestriction/JexusManager.snk differ diff --git a/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.Designer.cs b/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.Designer.cs new file mode 100644 index 00000000..6f68a523 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.Designer.cs @@ -0,0 +1,149 @@ +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewRestrictionDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.cbAllowed = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(292, 166); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(186, 166); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 58); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(63, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Description:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(12, 74); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(375, 20); + this.txtName.TabIndex = 6; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(12, 25); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(338, 20); + this.txtPath.TabIndex = 8; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(94, 13); + this.label3.TabIndex = 11; + this.label3.Text = "ISAPI or CGI path:"; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(359, 23); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(28, 23); + this.btnBrowse.TabIndex = 15; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // cbAllowed + // + this.cbAllowed.AutoSize = true; + this.cbAllowed.Location = new System.Drawing.Point(12, 100); + this.cbAllowed.Name = "cbAllowed"; + this.cbAllowed.Size = new System.Drawing.Size(176, 17); + this.cbAllowed.TabIndex = 16; + this.cbAllowed.Text = "Allow extension path to execute"; + this.cbAllowed.UseVisualStyleBackColor = true; + // + // NewRestrictionDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(399, 201); + this.Controls.Add(this.cbAllowed); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.label3); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.txtName); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewRestrictionDialog"; + this.Text = "Add ISAPI or CGI Restriction"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtName; + private TextBox txtPath; + private Label label3; + private Button btnBrowse; + private CheckBox cbAllowed; + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.cs b/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.cs new file mode 100644 index 00000000..db07eff0 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.cs @@ -0,0 +1,78 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewRestrictionDialog : DialogForm + { + public NewRestrictionDialog(IServiceProvider serviceProvider, IsapiCgiRestrictionItem existing, IsapiCgiRestrictionFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add ISAPI or CGI Restriction" : "Edit ISAPI or CGI Restriction"; + Item = existing ?? new IsapiCgiRestrictionItem(null); + if (existing != null) + { + txtPath.Text = Item.Path; + txtName.Text = Item.Description; + cbAllowed.Checked = Item.Allowed; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.Path = txtPath.Text; + Item.Description = txtName.Text; + Item.Allowed = cbAllowed.Checked; + if (feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "This restriction already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtPath, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowBrowseDialog(txtPath); + })); + } + + public IsapiCgiRestrictionItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210515"); + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.resx b/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/NewRestrictionDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiCgiRestriction/Properties/AssemblyInfo.cs b/JexusManager.Features.IsapiCgiRestriction/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..3bbe4b97 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.IsapiCgiRestriction")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.IsapiCgiRestriction")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("1571b862-8cd9-4314-9ccf-b1339d50298b")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.IsapiCgiRestriction/Properties/Resources.Designer.cs b/JexusManager.Features.IsapiCgiRestriction/Properties/Resources.Designer.cs new file mode 100644 index 00000000..d460028c --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.IsapiCgiRestriction.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.IsapiCgiRestriction.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap isapi_cgi_restriction_36 { + get { + object obj = ResourceManager.GetObject("isapi_cgi_restriction_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/Properties/Resources.resx b/JexusManager.Features.IsapiCgiRestriction/Properties/Resources.resx new file mode 100644 index 00000000..3da76608 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\isapi_cgi_restriction_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiCgiRestriction/Resources/isapi_cgi_restriction_36.png b/JexusManager.Features.IsapiCgiRestriction/Resources/isapi_cgi_restriction_36.png new file mode 100644 index 00000000..e20252ac Binary files /dev/null and b/JexusManager.Features.IsapiCgiRestriction/Resources/isapi_cgi_restriction_36.png differ diff --git a/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.Designer.cs b/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.Designer.cs new file mode 100644 index 00000000..abdef53d --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.Designer.cs @@ -0,0 +1,104 @@ +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class SettingsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cbCgi = new System.Windows.Forms.CheckBox(); + this.cbIsapi = new System.Windows.Forms.CheckBox(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // cbCgi + // + this.cbCgi.AutoSize = true; + this.cbCgi.Location = new System.Drawing.Point(12, 12); + this.cbCgi.Name = "cbCgi"; + this.cbCgi.Size = new System.Drawing.Size(171, 17); + this.cbCgi.TabIndex = 1; + this.cbCgi.Text = "Allow unspecified CGI modules"; + this.cbCgi.UseVisualStyleBackColor = true; + // + // cbIsapi + // + this.cbIsapi.AutoSize = true; + this.cbIsapi.Location = new System.Drawing.Point(12, 49); + this.cbIsapi.Name = "cbIsapi"; + this.cbIsapi.Size = new System.Drawing.Size(180, 17); + this.cbIsapi.TabIndex = 2; + this.cbIsapi.Text = "Allow unspecified ISAPI modules"; + this.cbIsapi.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(227, 96); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(131, 96); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // SettingsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(334, 131); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.cbIsapi); + this.Controls.Add(this.cbCgi); + this.Name = "SettingsDialog"; + this.Text = "Edit ISAPI and CGI Restrictions Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.PermissionsDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private CheckBox cbCgi; + private CheckBox cbIsapi; + private Button btnCancel; + private Button btnOK; + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.cs b/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.cs new file mode 100644 index 00000000..2e3934d9 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.cs @@ -0,0 +1,44 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiCgiRestriction +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + internal partial class SettingsDialog : DialogForm + { + public SettingsDialog(IServiceProvider serviceProvider, ConfigurationElement element) + : base(serviceProvider) + { + InitializeComponent(); + cbCgi.Checked = (bool)element["notListedCgisAllowed"]; + cbIsapi.Checked = (bool)element["notListedIsapisAllowed"]; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + element["notListedIsapisAllowed"] = cbIsapi.Checked; + element["notListedCgisAllowed"] = cbCgi.Checked; + DialogResult = DialogResult.OK; + })); + } + + private void PermissionsDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210515"); + } + } +} diff --git a/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.resx b/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/SettingsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiCgiRestriction/packages.config b/JexusManager.Features.IsapiCgiRestriction/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.IsapiCgiRestriction/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersFeature.cs b/JexusManager.Features.IsapiFilters/IsapiFiltersFeature.cs new file mode 100644 index 00000000..818af8c6 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersFeature.cs @@ -0,0 +1,304 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.IsapiFilters +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Linq; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + using Microsoft.Web.Management.Server; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class IsapiFiltersFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly IsapiFiltersFeature _owner; + + public FeatureTaskList(IsapiFiltersFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (_owner.IsInOrder) + { + result.Add(GetMoveUpTaskItem(_owner.CanMoveUp)); + result.Add(GetMoveDownTaskItem(_owner.CanMoveDown)); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Unorder", "View Unordered List...", string.Empty).SetUsage()); + } + else + { + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Rename", "Rename", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + if (_owner.CanRevert) + { + result.Add(new MethodTaskItem("Revert", "Revert to Parent", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("InOrder", "View Ordered List...", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Rename() + { + _owner.Rename(); + } + + [Obfuscation(Exclude = true)] + public override void MoveUp() + { + _owner.MoveUp(); + } + + [Obfuscation(Exclude = true)] + public override void MoveDown() + { + _owner.MoveDown(); + } + + [Obfuscation(Exclude = true)] + public void InOrder() + { + _owner.InOrder(); + } + + [Obfuscation(Exclude = true)] + public void Unorder() + { + _owner.Unorder(); + } + + [Obfuscation(Exclude = true)] + public void Revert() + { + _owner.Revert(); + } + } + + public IsapiFiltersFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + this.CanRevert = service.Scope != ManagementScope.Server; + this.IsInOrder = false; + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection section = service.GetSection("system.webServer/isapiFilters", null, false); + return section.GetCollection(); + } + + public void Add() + { + var dialog = new NewFilterDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected authorization rule?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + var dialog = new NewFilterDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + public void Rename() + { + } + + public void MoveUp() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + MoveUpItem(); + } + + public void MoveDown() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + MoveDownItem(); + } + + public void InOrder() + { + this.IsInOrder = true; + this.OnSettingsSaved(); + } + + public void Unorder() + { + this.IsInOrder = false; + this.OnSettingsSaved(); + } + + public void Revert() + { + if (!CanRevert) + { + throw new InvalidOperationException("Revert operation cannot be done at server level"); + } + + var dialog = (IManagementUIService)GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "Reverting to the parent configuration will result in the loss of all settings in the local configuration file for this feature. Are you sure you want to continue?", + Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + RevertItems(); + } + + protected override void OnSettingsSaved() + { + this.IsapiFiltersSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210462"); + return false; + } + + public bool IsInOrder { get; private set; } + + public bool CanRevert { get; private set; } + + public IsapiFiltersSettingsSavedEventHandler IsapiFiltersSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "ISAPI Filters"; } + } + } +} diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersItem.cs b/JexusManager.Features.IsapiFilters/IsapiFiltersItem.cs new file mode 100644 index 00000000..c7aa3ac6 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersItem.cs @@ -0,0 +1,81 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiFilters +{ + using System.Collections.Generic; + using System.Linq; + using System.Text; + + using Microsoft.Web.Administration; + + internal class IsapiFiltersItem : IItem + { + public IsapiFiltersItem(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + if (element == null) + { + Path = string.Empty; + PreConditions = new List(0); + return; + } + + this.Name = (string)element["name"]; + this.Path = (string)element["path"]; + var content = (string)element["preCondition"]; + this.PreConditions = content.Split(',').ToList(); + EnableCache = (bool)element["enableCache"]; + } + + public List PreConditions { get; set; } + + public bool EnableCache { get; set; } + + public string Path { get; set; } + + public string Name { get; set; } + + public ConfigurationElement Element { get; set; } + + public string Flag { get; set; } + + public bool Equals(IsapiFiltersItem other) + { + // all properties + return this.Match(other) && other.Path == this.Path; + } + + public void Apply() + { + Element["name"] = Name; + Element["path"] = Path; + Element["preCondition"] = Combine(PreConditions); + Element["enableCache"] = EnableCache; + } + + private static string Combine(List preConditions) + { + if (preConditions.Count == 0) + { + return string.Empty; + } + + var result = new StringBuilder(preConditions[0]); + for (int index = 1; index < preConditions.Count; index++) + { + result.AppendFormat(",{0}", preConditions[index]); + } + + return result.ToString(); + } + + public bool Match(IsapiFiltersItem other) + { + // match combined keys. + return other != null && other.Name == this.Name; + } + } +} diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersModule.cs b/JexusManager.Features.IsapiFilters/IsapiFiltersModule.cs new file mode 100644 index 00000000..accba651 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiFilters +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class IsapiFiltersModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(IsapiFiltersPage), "ISAPI Filters", + "Specify ISAPI filters that modify IIS functionality", Resources.isapi_filters_36, + Resources.isapi_filters_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersModuleProvider.cs b/JexusManager.Features.IsapiFilters/IsapiFiltersModuleProvider.cs new file mode 100644 index 00000000..9b0146d3 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiFilters +{ + using System; + + using Microsoft.Web.Management.Server; + + public class IsapiFiltersModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(IsapiFiltersModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersPage.Designer.cs b/JexusManager.Features.IsapiFilters/IsapiFiltersPage.Designer.cs new file mode 100644 index 00000000..4b790544 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersPage.Designer.cs @@ -0,0 +1,290 @@ +namespace JexusManager.Features.IsapiFilters +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class IsapiFiltersPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chExecutable = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chExecutable, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 120; + // + // chExecutable + // + this.chExecutable.Text = "Executable"; + this.chExecutable.Width = 180; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(421, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure ISAPI filters that process requests made to the Web" + + " server."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(129, 25); + this.label3.TabIndex = 2; + this.label3.Text = "ISAPI Filters"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // IsapiFiltersPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "IsapiFiltersPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chName; + private ColumnHeader chExecutable; + } +} diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersPage.cs b/JexusManager.Features.IsapiFilters/IsapiFiltersPage.cs new file mode 100644 index 00000000..02004e27 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersPage.cs @@ -0,0 +1,131 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiFilters +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class IsapiFiltersPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly IsapiFiltersPage _owner; + + public PageTaskList(IsapiFiltersPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class IsapiFiltersListViewItem : ListViewItem + { + public IsapiFiltersItem Item { get; } + + private readonly IsapiFiltersPage _page; + + public IsapiFiltersListViewItem(IsapiFiltersItem item, IsapiFiltersPage page) + : base(item.Name) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Path)); + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private IsapiFiltersFeature _feature; + private PageTaskList _taskList; + + public IsapiFiltersPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new IsapiFiltersFeature(this.Module); + _feature.IsapiFiltersSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + toolStrip2.Visible = !_feature.IsInOrder; + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new IsapiFiltersListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (IsapiFiltersListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((IsapiFiltersListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersPage.resx b/JexusManager.Features.IsapiFilters/IsapiFiltersPage.resx new file mode 100644 index 00000000..7a2db0ac --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 497, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiFilters/IsapiFiltersSettingsSavedEventHandler.cs b/JexusManager.Features.IsapiFilters/IsapiFiltersSettingsSavedEventHandler.cs new file mode 100644 index 00000000..026e00d0 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/IsapiFiltersSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiFilters +{ + public delegate void IsapiFiltersSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.IsapiFilters/JexusManager.Features.IsapiFilters.csproj b/JexusManager.Features.IsapiFilters/JexusManager.Features.IsapiFilters.csproj new file mode 100644 index 00000000..55399720 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/JexusManager.Features.IsapiFilters.csproj @@ -0,0 +1,130 @@ + + + + + Debug + AnyCPU + {DBA682A8-0579-4F41-A852-18235A9A4A93} + Library + Properties + JexusManager.Features.IsapiFilters + JexusManager.Features.IsapiFilters + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + + UserControl + + + IsapiFiltersPage.cs + + + + Form + + + NewFilterDialog.cs + + + + True + True + Resources.resx + + + + + IsapiFiltersPage.cs + + + NewFilterDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiFilters/JexusManager.snk b/JexusManager.Features.IsapiFilters/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.IsapiFilters/JexusManager.snk differ diff --git a/JexusManager.Features.IsapiFilters/NewFilterDialog.Designer.cs b/JexusManager.Features.IsapiFilters/NewFilterDialog.Designer.cs new file mode 100644 index 00000000..fcd7e36e --- /dev/null +++ b/JexusManager.Features.IsapiFilters/NewFilterDialog.Designer.cs @@ -0,0 +1,136 @@ +namespace JexusManager.Features.IsapiFilters +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewFilterDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(317, 186); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(216, 186); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 9); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(61, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Filter name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(269, 20); + this.txtName.TabIndex = 6; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 82); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(360, 20); + this.txtPath.TabIndex = 8; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 66); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(63, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Executable:"; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(381, 80); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(28, 23); + this.btnBrowse.TabIndex = 10; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // NewFilterDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(424, 221); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.txtName); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewFilterDialog"; + this.Text = "Add ISAPI Filter"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtName; + private TextBox txtPath; + private Label label1; + private Button btnBrowse; + } +} diff --git a/JexusManager.Features.IsapiFilters/NewFilterDialog.cs b/JexusManager.Features.IsapiFilters/NewFilterDialog.cs new file mode 100644 index 00000000..3c1a0dee --- /dev/null +++ b/JexusManager.Features.IsapiFilters/NewFilterDialog.cs @@ -0,0 +1,79 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.IsapiFilters +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewFilterDialog : DialogForm + { + public NewFilterDialog(IServiceProvider serviceProvider, IsapiFiltersItem existing, IsapiFiltersFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add ISAPI Filter" : "Edit ISAPI Filter"; + txtName.ReadOnly = existing != null; + Item = existing ?? new IsapiFiltersItem(null); + if (existing != null) + { + txtPath.Text = Item.Path; + txtName.Text = Item.Name; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.Path = txtPath.Text; + Item.Name = txtName.Text; + if (!txtName.ReadOnly && feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A filter with this name already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtPath, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowBrowseDialog(txtPath); + })); + } + + public IsapiFiltersItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210516"); + } + } +} diff --git a/JexusManager.Features.IsapiFilters/NewFilterDialog.resx b/JexusManager.Features.IsapiFilters/NewFilterDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/NewFilterDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiFilters/Properties/AssemblyInfo.cs b/JexusManager.Features.IsapiFilters/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5ab51de3 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.IsapiFilters")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.IsapiFilters")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("dba682a8-0579-4f41-a852-18235a9a4a93")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.IsapiFilters/Properties/Resources.Designer.cs b/JexusManager.Features.IsapiFilters/Properties/Resources.Designer.cs new file mode 100644 index 00000000..9403a4fd --- /dev/null +++ b/JexusManager.Features.IsapiFilters/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.IsapiFilters.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.IsapiFilters.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap isapi_filters_36 { + get { + object obj = ResourceManager.GetObject("isapi_filters_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.IsapiFilters/Properties/Resources.resx b/JexusManager.Features.IsapiFilters/Properties/Resources.resx new file mode 100644 index 00000000..3e4bffd0 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\isapi_filters_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.IsapiFilters/Resources/isapi_filters_36.png b/JexusManager.Features.IsapiFilters/Resources/isapi_filters_36.png new file mode 100644 index 00000000..809750b2 Binary files /dev/null and b/JexusManager.Features.IsapiFilters/Resources/isapi_filters_36.png differ diff --git a/JexusManager.Features.IsapiFilters/packages.config b/JexusManager.Features.IsapiFilters/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.IsapiFilters/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Jexus/JexusFeature.cs b/JexusManager.Features.Jexus/JexusFeature.cs new file mode 100644 index 00000000..90a54b43 --- /dev/null +++ b/JexusManager.Features.Jexus/JexusFeature.cs @@ -0,0 +1,164 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Jexus +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Resources; + using System.Text; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal class JexusFeature + { + public JexusFeature(Module module) + { + this.Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (this.Module as IServiceProvider).GetService(type); + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + if (service.ServerManager.Mode != WorkingMode.Jexus) + { + this.IsFeatureEnabled = false; + return; + } + + this.IsFeatureEnabled = true; + var settings = service.Server == null ? service.Application.GetExtra() : service.Server.GetExtra(); + var text = new StringBuilder(); + foreach (var key in settings.Keys) + { + foreach (var item in settings[key]) + { + text.AppendFormat("{0}={1}", key, item).AppendLine(); + } + } + + Contents = text.ToString(); + this.OnJexusSettingsSaved(); + } + + public string Contents { get; set; } + + protected void OnJexusSettingsSaved() + { + this.JexusSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("https://jexus.codeplex.com/wikipage?title=Configuration%20Files"); + return false; + } + + public JexusSettingsSavedEventHandler JexusSettingsUpdated { get; set; } + public string Description { get; } + + public bool IsFeatureEnabled { get; private set; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + + public string Name + { + get + { + return "Jexus Specific"; + } + } + + public void CancelChanges() + { + this.Load(); + } + + public bool ApplyChanges() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + if (service.Server == null) + { + service.Application.GetExtra().Clear(); + } + else + { + service.Server.GetExtra().Clear(); + } + + var reader = new StringReader(Contents); + string line; + while ((line = reader.ReadLine()) != null) + { + var index = line.IndexOf('='); + if (index == -1) + { + continue; + } + + var key = line.Substring(0, index).Trim(); + if (key.Length == 0) + { + continue; + } + + var value = line.Substring(index + 1).Trim(); + if (service.Server == null) + { + var extra = service.Application.GetExtra(); + if (extra.ContainsKey(key)) + { + extra[key].Add(value); + } + else + { + extra.Add(key, new List { value }); + } + } + else + { + var extra = service.Server.GetExtra(); + if (extra.ContainsKey(key)) + { + extra[key].Add(value); + } + else + { + extra.Add(key, new List { value }); + } + } + } + + AsyncHelper.RunSync(() => service.ServerManager.CommitChangesAsync()); + return true; + } + } +} diff --git a/JexusManager.Features.Jexus/JexusManager.Features.Jexus.csproj b/JexusManager.Features.Jexus/JexusManager.Features.Jexus.csproj new file mode 100644 index 00000000..85c8c013 --- /dev/null +++ b/JexusManager.Features.Jexus/JexusManager.Features.Jexus.csproj @@ -0,0 +1,103 @@ + + + + + Debug + AnyCPU + {EA333EE8-6551-4B5B-A733-119C5560608D} + Library + Properties + JexusManager.Features.Jexus + JexusManager.Features.Jexus + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + + + + + + + + + + + + + UserControl + + + JexusPage.cs + + + + + True + True + Resources.resx + + + + + JexusPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Jexus/JexusManager.snk b/JexusManager.Features.Jexus/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Jexus/JexusManager.snk differ diff --git a/JexusManager.Features.Jexus/JexusModule.cs b/JexusManager.Features.Jexus/JexusModule.cs new file mode 100644 index 00000000..b010a1a9 --- /dev/null +++ b/JexusManager.Features.Jexus/JexusModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Jexus +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class JexusModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(JexusPage), "Jexus Specific", + "Configure Jexus specific settings", + Resources.jexus_36, Resources.jexus_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Jexus/JexusModuleProvider.cs b/JexusManager.Features.Jexus/JexusModuleProvider.cs new file mode 100644 index 00000000..8b2a184e --- /dev/null +++ b/JexusManager.Features.Jexus/JexusModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Jexus +{ + using System; + + using Microsoft.Web.Management.Server; + + public class JexusModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(JexusModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Jexus/JexusPage.Designer.cs b/JexusManager.Features.Jexus/JexusPage.Designer.cs new file mode 100644 index 00000000..f8ef96c4 --- /dev/null +++ b/JexusManager.Features.Jexus/JexusPage.Designer.cs @@ -0,0 +1,222 @@ +namespace JexusManager.Features.Jexus +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class JexusPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.txtSettings = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 5; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.txtSettings); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 11; + // + // txtSettings + // + this.txtSettings.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.txtSettings.Location = new System.Drawing.Point(16, 18); + this.txtSettings.Multiline = true; + this.txtSettings.Name = "txtSettings"; + this.txtSettings.Size = new System.Drawing.Size(529, 505); + this.txtSettings.TabIndex = 3; + this.txtSettings.TextChanged += new System.EventHandler(this.txtSettings_TextChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(251, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure Jexus specific settings."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(151, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Jexus Specific"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // JexusPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "JexusPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private TextBox txtSettings; + } +} diff --git a/JexusManager.Features.Jexus/JexusPage.cs b/JexusManager.Features.Jexus/JexusPage.cs new file mode 100644 index 00000000..194a1be7 --- /dev/null +++ b/JexusManager.Features.Jexus/JexusPage.cs @@ -0,0 +1,146 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Jexus +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class JexusPage : ModuleDialogPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly JexusPage _owner; + + public PageTaskList(JexusPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private JexusFeature _feature; + private bool _hasChanges; + private bool _initialized; + private TaskList _taskList; + + public JexusPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new JexusFeature(this.Module); + _feature.JexusSettingsUpdated = this.Refresh; + _feature.Load(); + txtSettings.Enabled = _feature.IsFeatureEnabled; + base.Initialize(navigationData); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void txtSettings_TextChanged(object sender, EventArgs e) + { + _feature.Contents = txtSettings.Text; + this.InformChanges(); + } + + protected override bool ApplyChanges() + { + if (!_feature.ApplyChanges()) + { + return false; + } + + this.ClearChanges(); + return true; + } + + protected override void CancelChanges() + { + _feature.CancelChanges(); + this.ClearChanges(); + } + + protected override bool HasChanges + { + get { return _hasChanges; } + } + + protected override bool CanApplyChanges + { + get { return true; } + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + this.Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + this.Refresh(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + protected override void OnRefresh() + { + if (!_hasChanges) + { + txtSettings.Text = _feature.Contents; + _initialized = true; + } + + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + } + } +} diff --git a/JexusManager.Features.Jexus/JexusPage.resx b/JexusManager.Features.Jexus/JexusPage.resx new file mode 100644 index 00000000..950be5d2 --- /dev/null +++ b/JexusManager.Features.Jexus/JexusPage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Jexus/JexusSettingsSavedEventHandler.cs b/JexusManager.Features.Jexus/JexusSettingsSavedEventHandler.cs new file mode 100644 index 00000000..b6a2a6f9 --- /dev/null +++ b/JexusManager.Features.Jexus/JexusSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Jexus +{ + public delegate void JexusSettingsSavedEventHandler(); +} diff --git a/JexusManager.Features.Jexus/Properties/AssemblyInfo.cs b/JexusManager.Features.Jexus/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..bebda1e7 --- /dev/null +++ b/JexusManager.Features.Jexus/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Jexus")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Jexus")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("ea333ee8-6551-4b5b-a733-119c5560608d")] + +// 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")] diff --git a/JexusManager.Features.Jexus/Properties/Resources.Designer.cs b/JexusManager.Features.Jexus/Properties/Resources.Designer.cs new file mode 100644 index 00000000..3205b0c7 --- /dev/null +++ b/JexusManager.Features.Jexus/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Jexus.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Jexus.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap jexus_36 { + get { + object obj = ResourceManager.GetObject("jexus_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Jexus/Properties/Resources.resx b/JexusManager.Features.Jexus/Properties/Resources.resx new file mode 100644 index 00000000..30e7d5f4 --- /dev/null +++ b/JexusManager.Features.Jexus/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\jexus_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Jexus/Resources/jexus_36.png b/JexusManager.Features.Jexus/Resources/jexus_36.png new file mode 100644 index 00000000..a008100e Binary files /dev/null and b/JexusManager.Features.Jexus/Resources/jexus_36.png differ diff --git a/JexusManager.Features.Logging/AddFieldDialog.Designer.cs b/JexusManager.Features.Logging/AddFieldDialog.Designer.cs new file mode 100644 index 00000000..73d532d2 --- /dev/null +++ b/JexusManager.Features.Logging/AddFieldDialog.Designer.cs @@ -0,0 +1,153 @@ +namespace JexusManager.Features.Logging +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AddFieldDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.cbType = new System.Windows.Forms.ComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.cbSource = new System.Windows.Forms.ComboBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(22, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(63, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Field Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(25, 36); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(351, 20); + this.txtName.TabIndex = 1; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(22, 76); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(71, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Source Type:"; + // + // cbType + // + this.cbType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbType.FormattingEnabled = true; + this.cbType.Items.AddRange(new object[] { + "Request Header", + "Response Header", + "Server Variable"}); + this.cbType.Location = new System.Drawing.Point(25, 92); + this.cbType.Name = "cbType"; + this.cbType.Size = new System.Drawing.Size(351, 21); + this.cbType.TabIndex = 3; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(22, 137); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(44, 13); + this.label3.TabIndex = 4; + this.label3.Text = "Source:"; + // + // cbSource + // + this.cbSource.FormattingEnabled = true; + this.cbSource.Location = new System.Drawing.Point(25, 153); + this.cbSource.Name = "cbSource"; + this.cbSource.Size = new System.Drawing.Size(351, 21); + this.cbSource.TabIndex = 5; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(226, 251); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(322, 251); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 7; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // AddFieldDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(429, 286); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.cbSource); + this.Controls.Add(this.label3); + this.Controls.Add(this.cbType); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label1); + this.Name = "AddFieldDialog"; + this.Text = "Add Custom Field"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AddFieldDialog_HelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtName; + private Label label2; + private ComboBox cbType; + private Label label3; + private ComboBox cbSource; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.Logging/AddFieldDialog.cs b/JexusManager.Features.Logging/AddFieldDialog.cs new file mode 100644 index 00000000..122b8a97 --- /dev/null +++ b/JexusManager.Features.Logging/AddFieldDialog.cs @@ -0,0 +1,184 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Logging +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + public partial class AddFieldDialog : DialogForm + { + public CustomLogField Custom { get; private set; } + + public AddFieldDialog(IServiceProvider serviceProvider, CustomLogField custom, SiteLogFile logFile) + : base(serviceProvider) + { + Custom = custom; + InitializeComponent(); + if (custom != null) + { + txtName.Text = custom.LogFieldName; + cbType.SelectedIndex = (int)custom.SourceType; + cbSource.Text = custom.SourceName; + } + else + { + cbType.SelectedIndex = 0; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(cbSource, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) && !string.IsNullOrWhiteSpace(cbSource.Text); + })); + + container.Add( + Observable.FromEventPattern(cbType, "SelectedIndexChanged") + .Subscribe(evt => + { + cbSource.Items.Clear(); + if (cbType.SelectedIndex == 0) + { + cbSource.Items.AddRange(new object[] + { + "Accept", + "Accept_Charset", + "Accept_Encoding", + "Authorization", + "Cache-Control", + "Connection", + "Content-Length", + "Content-MD5", + "Content-Type", + "Date", + "Expect", + "From", + "Host", + "If-Match", + "If-Modified-Since", + "If-None-Match", + "If-Range", + "If-Unmodified-Since", + "Max-Forwards", + "Pragma", + "Proxy-Authorization", + "Range", + "Referer", + "TE", + "Upgrade", + "User-Agent", + "Via", + "Warning" + }); + } + else if (cbType.SelectedIndex == 1) + { + cbSource.Items.AddRange(new object[] + { + "Accept-Range", + "Content-Type", + "ETag", + "Last-Modified", + "Server" + }); + } + else if (cbType.SelectedIndex == 2) + { + cbSource.Items.AddRange(new object[] + { + "ALL_HTTP", + "ALL_RAW", + "APPL_MD_PATH", + "APPL_PHYSICAL_PATH", + "AUTH_PASSWORD", + "AUTH_TYPE", + "AUTH_USER", + "CERT_COOKIE", + "CERT_FLAGS", + "CERT_ISSUER", + "CERT_KEYSIZE", + "CERT_SECRETKEYSIZE", + "CERT_SERIALNUMBER", + "CERT_SERVER_ISSUER", + "CERT_SERVER_SUBJECT", + "CERT_SUBJECT", + "CONTENT_LENGTH", + "CONTENT_TYPE", + "GATEWAY_INTERFACE", + "HTTP_ACCEPT", + "HTTP_ACCEPT_ENCODING", + "HTTP_ACCEPT_LANGUAGE", + "HTTP_CONNECTION", + "HTTP_COOKIE", + "HTTP_HOST", + "HTTP_METHOD", + "HTTP_REFERER", + "HTTP_URL", + "HTTP_USER_AGENT", + "HTTP_VERSION", + "HTTPS", + "HTTPS_KEYSIZE", + "HTTPS_SECRETKEYSIZE", + "HTTPS_SERVER_ISSUER", + "HTTPS_SERVER_SUBJECT", + "INSTANCE_ID", + "INSTANCE_META_PATH", + "LOCAL_ADDR", + "LOGON_USER", + "PATH_INFO", + "PATH_TRANSLATED", + "QUERY_STRING", + "REMOTE_ADDR", + "REMOTE_HOST", + "REMOTE_PORT", + "REMOTE_USER", + "REQUEST_METHOD", + "SCRIPT_NAME", + "SERVER_NAME", + "SERVER_PORT", + "SERVER_PORT_SECURE", + "SERVER_PROTOCOL", + "SERVER_SOFTWARE", + "UNMAPPED_REMOTE_USER", + "URL" + }); + } + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (Custom == null) + { + Custom = logFile.CustomLogFields.CreateElement(); + logFile.CustomLogFields.Add(Custom); + } + + Custom.LogFieldName = txtName.Text; + Custom.SourceType = (CustomLogFieldSourceType)Enum.ToObject(typeof(CustomLogFieldSourceType), cbType.SelectedIndex); + Custom.SourceName = cbSource.Text; + DialogResult = DialogResult.OK; + })); + } + + private void AddFieldDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210517#W3CLoggingFields"); + } + } +} diff --git a/JexusManager.Features.Logging/AddFieldDialog.resx b/JexusManager.Features.Logging/AddFieldDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Logging/AddFieldDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Logging/FieldsDialog.Designer.cs b/JexusManager.Features.Logging/FieldsDialog.Designer.cs new file mode 100644 index 00000000..bbf2d116 --- /dev/null +++ b/JexusManager.Features.Logging/FieldsDialog.Designer.cs @@ -0,0 +1,204 @@ +namespace JexusManager.Features.Logging +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class FieldsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lvStandard = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.label1 = new System.Windows.Forms.Label(); + this.lvCustom = new System.Windows.Forms.ListView(); + this.chField = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chSource = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.label2 = new System.Windows.Forms.Label(); + this.btnAdd = new System.Windows.Forms.Button(); + this.btnRemove = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnEdit = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lvStandard + // + this.lvStandard.CheckBoxes = true; + this.lvStandard.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName}); + this.lvStandard.FullRowSelect = true; + this.lvStandard.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.lvStandard.Location = new System.Drawing.Point(12, 36); + this.lvStandard.Name = "lvStandard"; + this.lvStandard.Size = new System.Drawing.Size(545, 235); + this.lvStandard.TabIndex = 0; + this.lvStandard.UseCompatibleStateImageBehavior = false; + this.lvStandard.View = System.Windows.Forms.View.Details; + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 350; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(9, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(83, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Standard Fields:"; + // + // lvCustom + // + this.lvCustom.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chField, + this.chType, + this.chSource}); + this.lvCustom.FullRowSelect = true; + this.lvCustom.Location = new System.Drawing.Point(12, 315); + this.lvCustom.Name = "lvCustom"; + this.lvCustom.Size = new System.Drawing.Size(545, 200); + this.lvCustom.TabIndex = 2; + this.lvCustom.UseCompatibleStateImageBehavior = false; + this.lvCustom.View = System.Windows.Forms.View.Details; + // + // chField + // + this.chField.Text = "Log Field"; + this.chField.Width = 175; + // + // chType + // + this.chType.Text = "Source Type"; + this.chType.Width = 175; + // + // chSource + // + this.chSource.Text = "Source"; + this.chSource.Width = 175; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(9, 299); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(75, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Custom Fields:"; + // + // btnAdd + // + this.btnAdd.Location = new System.Drawing.Point(12, 521); + this.btnAdd.Name = "btnAdd"; + this.btnAdd.Size = new System.Drawing.Size(100, 23); + this.btnAdd.TabIndex = 4; + this.btnAdd.Text = "Add Field..."; + this.btnAdd.UseVisualStyleBackColor = true; + // + // btnRemove + // + this.btnRemove.Enabled = false; + this.btnRemove.Location = new System.Drawing.Point(118, 521); + this.btnRemove.Name = "btnRemove"; + this.btnRemove.Size = new System.Drawing.Size(105, 23); + this.btnRemove.TabIndex = 5; + this.btnRemove.Text = "Remove Field"; + this.btnRemove.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(366, 567); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 6; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(462, 567); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 7; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnEdit + // + this.btnEdit.Enabled = false; + this.btnEdit.Location = new System.Drawing.Point(467, 521); + this.btnEdit.Name = "btnEdit"; + this.btnEdit.Size = new System.Drawing.Size(90, 23); + this.btnEdit.TabIndex = 8; + this.btnEdit.Text = "Edit Field..."; + this.btnEdit.UseVisualStyleBackColor = true; + // + // FieldsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(569, 602); + this.Controls.Add(this.btnEdit); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnRemove); + this.Controls.Add(this.btnAdd); + this.Controls.Add(this.label2); + this.Controls.Add(this.lvCustom); + this.Controls.Add(this.label1); + this.Controls.Add(this.lvStandard); + this.Name = "FieldsDialog"; + this.Text = "W3C Logging Fields"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.FieldsDialog_HelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private ListView lvStandard; + private Label label1; + private ListView lvCustom; + private ColumnHeader chField; + private ColumnHeader chType; + private ColumnHeader chSource; + private Label label2; + private Button btnAdd; + private Button btnRemove; + private Button btnOK; + private Button btnCancel; + private Button btnEdit; + private ColumnHeader chName; + } +} diff --git a/JexusManager.Features.Logging/FieldsDialog.cs b/JexusManager.Features.Logging/FieldsDialog.cs new file mode 100644 index 00000000..5ae256ce --- /dev/null +++ b/JexusManager.Features.Logging/FieldsDialog.cs @@ -0,0 +1,179 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Logging +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reflection; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + public partial class FieldsDialog : DialogForm + { + private class StandardListViewItem : ListViewItem + { + public StandardListViewItem(LogExtFileFlags flag) + { + Text = ToString(flag); + Flag = flag; + } + + public LogExtFileFlags Flag { get; } + + private static string ToString(LogExtFileFlags flag) + { + FieldInfo fi = typeof(LogExtFileFlags).GetField(flag.ToString()); + DescriptionAttribute dna = + (DescriptionAttribute)Attribute.GetCustomAttribute( + fi, typeof(DescriptionAttribute)); + return dna.Description; + } + } + + private class CustomListViewItem : ListViewItem + { + public CustomLogField Custom { get; } + + public CustomListViewItem(CustomLogField custom) + { + Custom = custom; + Text = custom.LogFieldName; + SubItems.Add(ToString(custom.SourceType)); + SubItems.Add(custom.SourceName); + } + + private static string ToString(CustomLogFieldSourceType type) + { + switch (type) + { + case CustomLogFieldSourceType.RequestHeader: + return "Request Header"; + case CustomLogFieldSourceType.ResponseHeader: + return "Response Header"; + case CustomLogFieldSourceType.ServerVariable: + return "Server Variable"; + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + + public void Update() + { + Text = Custom.LogFieldName; + SubItems[0].Text = ToString(Custom.SourceType); + SubItems[1].Text = Custom.SourceName; + } + } + + public FieldsDialog(IServiceProvider serviceProvider, SiteLogFile logFile) + : base(serviceProvider) + { + InitializeComponent(); + if (logFile.CustomLogFields != null) + { + foreach (CustomLogField custom in logFile.CustomLogFields) + { + lvCustom.Items.Add(new CustomListViewItem(custom)); + } + } + + foreach (LogExtFileFlags flag in Enum.GetValues(typeof(LogExtFileFlags))) + { + lvStandard.Items.Add(new StandardListViewItem(flag) + { + Checked = (logFile.LogExtFileFlags & flag) == flag + }); + } + + btnAdd.Enabled = logFile.CustomLogFields != null; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(lvCustom, "SelectedIndexChanged") + .Subscribe(evt => + { + btnRemove.Enabled = lvCustom.SelectedItems.Count > 0; + btnEdit.Enabled = lvCustom.SelectedItems.Count == 1; + })); + + container.Add( + Observable.FromEventPattern(lvStandard, "ItemChecked") + .Subscribe(evt => + { + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(btnAdd, "Click") + .Subscribe(evt => + { + var dialog = new AddFieldDialog(ServiceProvider, null, logFile); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + lvCustom.Items.Add(new CustomListViewItem(dialog.Custom)); + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(btnEdit, "Click") + .Subscribe(evt => + { + var selected = (CustomListViewItem)lvCustom.SelectedItems[0]; + var dialog = new AddFieldDialog(ServiceProvider, selected.Custom, logFile); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + selected.Update(); + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(btnRemove, "Click") + .Subscribe(evt => + { + foreach (CustomListViewItem item in lvCustom.SelectedItems) + { + item.Remove(); + item.Custom.Delete(); + } + + btnOK.Enabled = true; + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + logFile.LogExtFileFlags = 0; + foreach (StandardListViewItem item in lvStandard.Items) + { + if (item.Checked) + { + logFile.LogExtFileFlags |= item.Flag; + } + } + + DialogResult = DialogResult.OK; + })); + } + + private void FieldsDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210517#W3CLoggingFields"); + } + } +} diff --git a/JexusManager.Features.Logging/FieldsDialog.resx b/JexusManager.Features.Logging/FieldsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Logging/FieldsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Logging/JexusManager.Features.Logging.csproj b/JexusManager.Features.Logging/JexusManager.Features.Logging.csproj new file mode 100644 index 00000000..9ed1a2cb --- /dev/null +++ b/JexusManager.Features.Logging/JexusManager.Features.Logging.csproj @@ -0,0 +1,141 @@ + + + + + Debug + AnyCPU + {5C2AE18D-D829-481B-8FCA-8AD0F69E68C8} + Library + Properties + JexusManager.Features.Logging + JexusManager.Features.Logging + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + Helper.cs + + + Form + + + AddFieldDialog.cs + + + Form + + + FieldsDialog.cs + + + + + + UserControl + + + LoggingPage.cs + + + + + True + True + Resources.resx + + + + + AddFieldDialog.cs + + + FieldsDialog.cs + + + LoggingPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Logging/JexusManager.snk b/JexusManager.Features.Logging/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Logging/JexusManager.snk differ diff --git a/JexusManager.Features.Logging/LoggingFeature.cs b/JexusManager.Features.Logging/LoggingFeature.cs new file mode 100644 index 00000000..a84fff71 --- /dev/null +++ b/JexusManager.Features.Logging/LoggingFeature.cs @@ -0,0 +1,293 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Logging +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Resources; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class LoggingFeature + { + private sealed class FeatureTaskList : TaskList + { + private readonly LoggingFeature _owner; + + public FeatureTaskList(LoggingFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (!_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Enable", "Enable", string.Empty).SetUsage()); + } + + if (_owner.IsEnabled) + { + result.Add(new MethodTaskItem("Disable", "Disable", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("View", "View Log Files...", string.Empty).SetUsage()); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Enable() + { + _owner.Enable(); + } + + [Obfuscation(Exclude = true)] + public void Disable() + { + _owner.Disable(); + } + + [Obfuscation(Exclude = true)] + public void View() + { + _owner.View(); + } + } + + public LoggingFeature(Module module) + { + Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), "", false); + } + + protected object GetService(Type type) + { + return (Module as IServiceProvider).GetService(type); + } + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.applicationHost/log"); + Mode = (long)section.Attributes["centralLogFileMode"].Value; + Encoding = (bool)section.Attributes["logInUTF8"].Value ? 0 : 1; + + if (service.Server != null) + { + var section2 = service.GetSection("system.applicationHost/sites"); + var element = section2.ChildElements["siteDefaults"].ChildElements["logFile"]; + LogFormat = (long)element.Attributes["logFormat"].Value; + Directory = element.Attributes["directory"].Value.ToString(); + if (element.Schema.AttributeSchemas["logTargetW3C"] != null) + { + LogTargetW3C = (long)element.Attributes["logTargetW3C"].Value; + } + else + { + this.LogTargetW3C = -1; + } + + LocalTimeRollover = (bool)element.Attributes["localTimeRollover"].Value; + TruncateSize = (long)element.Attributes["truncateSize"].Value; + Period = (long)element.Attributes["period"].Value; + } + else + { + var logFile = service.Application.GetSite().LogFile; + LogFormat = (long)logFile.LogFormat; + Directory = logFile.Directory; + if (logFile.Schema.AttributeSchemas["logTargetW3C"] != null) + { + LogTargetW3C = (long)logFile.LogTargetW3C; + } + else + { + this.LogTargetW3C = -1; + } + + LocalTimeRollover = logFile.LocalTimeRollover; + TruncateSize = logFile.TruncateSize; + Period = (long)logFile.Period; + } + + CanBrowse = service.Application == null || service.Application.IsRoot(); + CanEncoding = service.Server != null; + + ConfigurationSection httpLoggingSection1 = service.GetSection("system.webServer/httpLogging", null, false); + var dontLog = (bool)httpLoggingSection1["dontLog"]; + SetEnabled(!dontLog); + } + + public long Period { get; set; } + + public long TruncateSize { get; set; } + + public bool LocalTimeRollover { get; set; } + + public long LogTargetW3C { get; set; } + + public bool CanBrowse { get; set; } + + public long LogFormat { get; set; } + + public string Directory { get; set; } + + public int Encoding { get; set; } + + public long Mode { get; set; } + + private void Enable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + ConfigurationSection httpLoggingSection1 = service.GetSection("system.webServer/httpLogging", null, false); + httpLoggingSection1["dontLog"] = false; + SetEnabled(true); + } + + private void Disable() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + ConfigurationSection httpLoggingSection1 = service.GetSection("system.webServer/httpLogging", null, false); + httpLoggingSection1["dontLog"] = true; + SetEnabled(false); + } + + private void View() + { + var path = Directory.ExpandIisExpressEnvironmentVariables(); + if (System.IO.Directory.Exists(path)) + { + Process.Start(path); + return; + } + + var service = (IManagementUIService)GetService(typeof(IManagementUIService)); + service.ShowMessage("The specific log directory is invalid.", Name, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + internal void SelectFields() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var dialog = new FieldsDialog(Module, service.Application.GetSite().LogFile); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + } + + protected void OnLoggingSettingsSaved() + { + LoggingSettingsUpdated?.Invoke(); + } + + public void SetEnabled(bool enabled) + { + IsEnabled = enabled; + OnLoggingSettingsSaved(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210517"); + return false; + } + + public LoggingSettingsSavedEventHandler LoggingSettingsUpdated { get; set; } + public string Description { get; } + public bool IsEnabled { get; private set; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + + public string Name + { + get { return "Logging"; } + } + + public bool CanEncoding { get; set; } + + public void CancelChanges() + { + Load(); + } + + public bool ApplyChanges() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + if (service.Server != null) + { + var section = service.GetSection("system.applicationHost/log"); + section.Attributes["centralLogFileMode"].Value = Mode; + section.Attributes["logInUTF8"].Value = Encoding == 0; + + var section2 = service.GetSection("system.applicationHost/sites"); + var element = section2.ChildElements["siteDefaults"].ChildElements["logFile"]; + element.Attributes["logFormat"].Value = LogFormat; + element.Attributes["directory"].Value = Directory; + if (element.Schema.AttributeSchemas["logTargetW3C"] != null) + { + element.Attributes["logTargetW3C"].Value = LogTargetW3C; + } + + element.Attributes["localTimeRollover"].Value = LocalTimeRollover; + element.Attributes["truncateSize"].Value = TruncateSize; + element.Attributes["period"].Value = Period; + } + else + { + var logFile = service.Application.GetSite().LogFile; + logFile.LogFormat = (LogFormat)LogFormat; + logFile.Directory = Directory; + if (logFile.Schema.AttributeSchemas["logTargetW3C"] != null) + { + logFile.LogTargetW3C = (LogTargetW3C)LogTargetW3C; + } + + logFile.LocalTimeRollover = LocalTimeRollover; + logFile.TruncateSize = TruncateSize; + logFile.Period = (LoggingRolloverPeriod)Period; + } + + return true; + } + } +} diff --git a/JexusManager.Features.Logging/LoggingModule.cs b/JexusManager.Features.Logging/LoggingModule.cs new file mode 100644 index 00000000..b527e71f --- /dev/null +++ b/JexusManager.Features.Logging/LoggingModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Logging +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class LoggingModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(LoggingPage), "Logging", + "Configure how IIS logs requests on the Web server", + Resources.logging_36, Resources.logging_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Logging/LoggingModuleProvider.cs b/JexusManager.Features.Logging/LoggingModuleProvider.cs new file mode 100644 index 00000000..c205c192 --- /dev/null +++ b/JexusManager.Features.Logging/LoggingModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Logging +{ + using System; + + using Microsoft.Web.Management.Server; + + public class LoggingModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(LoggingModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Logging/LoggingPage.Designer.cs b/JexusManager.Features.Logging/LoggingPage.Designer.cs new file mode 100644 index 00000000..f29bcdc8 --- /dev/null +++ b/JexusManager.Features.Logging/LoggingPage.Designer.cs @@ -0,0 +1,546 @@ +namespace JexusManager.Features.Logging +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class LoggingPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.cbLocalTime = new System.Windows.Forms.CheckBox(); + this.rbNoFile = new System.Windows.Forms.RadioButton(); + this.txtSize = new System.Windows.Forms.TextBox(); + this.rbSize = new System.Windows.Forms.RadioButton(); + this.cbSchedule = new System.Windows.Forms.ComboBox(); + this.rbSchedule = new System.Windows.Forms.RadioButton(); + this.label9 = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.rbBoth = new System.Windows.Forms.RadioButton(); + this.rbEvent = new System.Windows.Forms.RadioButton(); + this.rbFile = new System.Windows.Forms.RadioButton(); + this.label8 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.cbLogType = new System.Windows.Forms.ComboBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.cbEncoding = new System.Windows.Forms.ComboBox(); + this.label7 = new System.Windows.Forms.Label(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.btnSelect = new System.Windows.Forms.Button(); + this.cbFormat = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.label2); + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 4; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitContainer1_SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.AutoScroll = true; + this.panel2.Controls.Add(this.groupBox3); + this.panel2.Controls.Add(this.groupBox2); + this.panel2.Controls.Add(this.label4); + this.panel2.Controls.Add(this.cbLogType); + this.panel2.Controls.Add(this.groupBox1); + this.panel2.Location = new System.Drawing.Point(10, 69); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 7; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.cbLocalTime); + this.groupBox3.Controls.Add(this.rbNoFile); + this.groupBox3.Controls.Add(this.txtSize); + this.groupBox3.Controls.Add(this.rbSize); + this.groupBox3.Controls.Add(this.cbSchedule); + this.groupBox3.Controls.Add(this.rbSchedule); + this.groupBox3.Controls.Add(this.label9); + this.groupBox3.Location = new System.Drawing.Point(6, 429); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(517, 219); + this.groupBox3.TabIndex = 7; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Log File Rollover"; + // + // cbLocalTime + // + this.cbLocalTime.AutoSize = true; + this.cbLocalTime.Location = new System.Drawing.Point(9, 189); + this.cbLocalTime.Name = "cbLocalTime"; + this.cbLocalTime.Size = new System.Drawing.Size(218, 17); + this.cbLocalTime.TabIndex = 6; + this.cbLocalTime.Text = "Use local time for file naming and rollover"; + this.cbLocalTime.UseVisualStyleBackColor = true; + this.cbLocalTime.CheckedChanged += new System.EventHandler(this.cbLocalTime_CheckedChanged); + // + // rbNoFile + // + this.rbNoFile.AutoSize = true; + this.rbNoFile.Location = new System.Drawing.Point(9, 154); + this.rbNoFile.Name = "rbNoFile"; + this.rbNoFile.Size = new System.Drawing.Size(151, 17); + this.rbNoFile.TabIndex = 5; + this.rbNoFile.TabStop = true; + this.rbNoFile.Text = "Do not create new log files"; + this.rbNoFile.UseVisualStyleBackColor = true; + this.rbNoFile.CheckedChanged += new System.EventHandler(this.rbSchedule_CheckedChanged); + // + // txtSize + // + this.txtSize.Location = new System.Drawing.Point(24, 128); + this.txtSize.Name = "txtSize"; + this.txtSize.Size = new System.Drawing.Size(110, 20); + this.txtSize.TabIndex = 4; + // + // rbSize + // + this.rbSize.AutoSize = true; + this.rbSize.Location = new System.Drawing.Point(9, 105); + this.rbSize.Name = "rbSize"; + this.rbSize.Size = new System.Drawing.Size(154, 17); + this.rbSize.TabIndex = 3; + this.rbSize.TabStop = true; + this.rbSize.Text = "Maximum file size (in bytes):"; + this.rbSize.UseVisualStyleBackColor = true; + this.rbSize.CheckedChanged += new System.EventHandler(this.rbSchedule_CheckedChanged); + // + // cbSchedule + // + this.cbSchedule.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbSchedule.FormattingEnabled = true; + this.cbSchedule.Items.AddRange(new object[] { + "Hourly", + "Daily", + "Weekly", + "Monthly"}); + this.cbSchedule.Location = new System.Drawing.Point(24, 78); + this.cbSchedule.Name = "cbSchedule"; + this.cbSchedule.Size = new System.Drawing.Size(175, 21); + this.cbSchedule.TabIndex = 2; + // + // rbSchedule + // + this.rbSchedule.AutoSize = true; + this.rbSchedule.Location = new System.Drawing.Point(9, 55); + this.rbSchedule.Name = "rbSchedule"; + this.rbSchedule.Size = new System.Drawing.Size(73, 17); + this.rbSchedule.TabIndex = 1; + this.rbSchedule.TabStop = true; + this.rbSchedule.Text = "Schedule:"; + this.rbSchedule.UseVisualStyleBackColor = true; + this.rbSchedule.CheckedChanged += new System.EventHandler(this.rbSchedule_CheckedChanged); + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(21, 29); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(268, 13); + this.label9.TabIndex = 0; + this.label9.Text = "Select the method that IIS uses to create a new log file."; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.rbBoth); + this.groupBox2.Controls.Add(this.rbEvent); + this.groupBox2.Controls.Add(this.rbFile); + this.groupBox2.Controls.Add(this.label8); + this.groupBox2.Location = new System.Drawing.Point(6, 265); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(517, 147); + this.groupBox2.TabIndex = 6; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Log Event Destination"; + // + // rbBoth + // + this.rbBoth.AutoSize = true; + this.rbBoth.Location = new System.Drawing.Point(9, 110); + this.rbBoth.Name = "rbBoth"; + this.rbBoth.Size = new System.Drawing.Size(159, 17); + this.rbBoth.TabIndex = 3; + this.rbBoth.TabStop = true; + this.rbBoth.Text = "Both log file and ETW event"; + this.rbBoth.UseVisualStyleBackColor = true; + this.rbBoth.CheckedChanged += new System.EventHandler(this.rbFile_CheckedChanged); + // + // rbEvent + // + this.rbEvent.AutoSize = true; + this.rbEvent.Location = new System.Drawing.Point(9, 83); + this.rbEvent.Name = "rbEvent"; + this.rbEvent.Size = new System.Drawing.Size(102, 17); + this.rbEvent.TabIndex = 2; + this.rbEvent.TabStop = true; + this.rbEvent.Text = "ETW event only"; + this.rbEvent.UseVisualStyleBackColor = true; + this.rbEvent.CheckedChanged += new System.EventHandler(this.rbFile_CheckedChanged); + // + // rbFile + // + this.rbFile.AutoSize = true; + this.rbFile.Location = new System.Drawing.Point(9, 56); + this.rbFile.Name = "rbFile"; + this.rbFile.Size = new System.Drawing.Size(81, 17); + this.rbFile.TabIndex = 1; + this.rbFile.TabStop = true; + this.rbFile.Text = "Log file only"; + this.rbFile.UseVisualStyleBackColor = true; + this.rbFile.CheckedChanged += new System.EventHandler(this.rbFile_CheckedChanged); + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(13, 28); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(254, 13); + this.label8.TabIndex = 0; + this.label8.Text = "Select the destination where IIS will write log events."; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(3, 6); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(81, 13); + this.label4.TabIndex = 3; + this.label4.Text = "One log file per:"; + // + // cbLogType + // + this.cbLogType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbLogType.Enabled = false; + this.cbLogType.FormattingEnabled = true; + this.cbLogType.Items.AddRange(new object[] { + "Server", + "Site"}); + this.cbLogType.Location = new System.Drawing.Point(6, 22); + this.cbLogType.Name = "cbLogType"; + this.cbLogType.Size = new System.Drawing.Size(121, 21); + this.cbLogType.TabIndex = 4; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.cbEncoding); + this.groupBox1.Controls.Add(this.label7); + this.groupBox1.Controls.Add(this.btnBrowse); + this.groupBox1.Controls.Add(this.txtPath); + this.groupBox1.Controls.Add(this.label6); + this.groupBox1.Controls.Add(this.btnSelect); + this.groupBox1.Controls.Add(this.cbFormat); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Location = new System.Drawing.Point(6, 50); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(517, 188); + this.groupBox1.TabIndex = 5; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Log File"; + // + // cbEncoding + // + this.cbEncoding.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbEncoding.Enabled = false; + this.cbEncoding.FormattingEnabled = true; + this.cbEncoding.Items.AddRange(new object[] { + "UTF-8", + "ANSI"}); + this.cbEncoding.Location = new System.Drawing.Point(9, 152); + this.cbEncoding.Name = "cbEncoding"; + this.cbEncoding.Size = new System.Drawing.Size(158, 21); + this.cbEncoding.TabIndex = 7; + this.cbEncoding.SelectedIndexChanged += new System.EventHandler(this.cbEncoding_SelectedIndexChanged); + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(6, 136); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(55, 13); + this.label7.TabIndex = 6; + this.label7.Text = "Encoding:"; + // + // btnBrowse + // + this.btnBrowse.Enabled = false; + this.btnBrowse.Location = new System.Drawing.Point(379, 95); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(95, 23); + this.btnBrowse.TabIndex = 5; + this.btnBrowse.Text = "Browse..."; + this.btnBrowse.UseVisualStyleBackColor = true; + this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click); + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(9, 97); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(364, 20); + this.txtPath.TabIndex = 4; + this.txtPath.TextChanged += new System.EventHandler(this.txtPath_TextChanged); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(6, 81); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(52, 13); + this.label6.TabIndex = 3; + this.label6.Text = "Directory:"; + // + // btnSelect + // + this.btnSelect.Enabled = false; + this.btnSelect.Location = new System.Drawing.Point(173, 42); + this.btnSelect.Name = "btnSelect"; + this.btnSelect.Size = new System.Drawing.Size(95, 23); + this.btnSelect.TabIndex = 2; + this.btnSelect.Text = "Select Fields..."; + this.btnSelect.UseVisualStyleBackColor = true; + this.btnSelect.Click += new System.EventHandler(this.btnSelect_Click); + // + // cbFormat + // + this.cbFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbFormat.Enabled = false; + this.cbFormat.FormattingEnabled = true; + this.cbFormat.Items.AddRange(new object[] { + "IIS", + "NCSA", + "W3C", + "Custom"}); + this.cbFormat.Location = new System.Drawing.Point(9, 44); + this.cbFormat.Name = "cbFormat"; + this.cbFormat.Size = new System.Drawing.Size(158, 21); + this.cbFormat.TabIndex = 1; + this.cbFormat.SelectedIndexChanged += new System.EventHandler(this.cbFormat_SelectedIndexChanged); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(6, 28); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(42, 13); + this.label5.TabIndex = 0; + this.label5.Text = "Format:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(7, 48); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(338, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure how IIS logs requests on the Web server."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 6; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(89, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Logging"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, -17); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // LoggingPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "LoggingPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private GroupBox groupBox1; + private ComboBox cbEncoding; + private Label label7; + private Button btnBrowse; + private TextBox txtPath; + private Label label6; + private Button btnSelect; + private ComboBox cbFormat; + private Label label5; + private ComboBox cbLogType; + private Label label4; + private Panel panel2; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private GroupBox groupBox3; + private CheckBox cbLocalTime; + private RadioButton rbNoFile; + private TextBox txtSize; + private RadioButton rbSize; + private ComboBox cbSchedule; + private RadioButton rbSchedule; + private Label label9; + private GroupBox groupBox2; + private RadioButton rbBoth; + private RadioButton rbEvent; + private RadioButton rbFile; + private Label label8; + } +} diff --git a/JexusManager.Features.Logging/LoggingPage.cs b/JexusManager.Features.Logging/LoggingPage.cs new file mode 100644 index 00000000..77fa4851 --- /dev/null +++ b/JexusManager.Features.Logging/LoggingPage.cs @@ -0,0 +1,274 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Logging +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class LoggingPage : ModuleDialogPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly LoggingPage _owner; + + public PageTaskList(LoggingPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private PageTaskList _taskList; + private LoggingFeature _feature; + private bool _hasChanges; + private bool _initialized; + + public LoggingPage() + { + InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new LoggingFeature(Module); + _feature.LoggingSettingsUpdated = Refresh; + _feature.Load(); + } + + protected override bool ShowHelp() + { + return _feature.ShowHelp(); + } + + private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void btnBrowse_Click(object sender, EventArgs e) + { + DialogHelper.ShowBrowseDialog(txtPath); + _feature.Directory = txtPath.Text; + InformChanges(); + } + + private void cbFormat_SelectedIndexChanged(object sender, EventArgs e) + { + btnSelect.Enabled = cbFormat.SelectedIndex == cbFormat.Items.Count - 1; + _feature.LogFormat = cbFormat.SelectedIndex; + InformChanges(); + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + Refresh(); + } + + protected override void OnRefresh() + { + if (!_hasChanges) + { + cbLogType.SelectedIndex = (int)_feature.Mode; + cbEncoding.SelectedIndex = _feature.Encoding; + cbFormat.SelectedIndex = (int)_feature.LogFormat; + txtPath.Text = _feature.Directory; + txtPath.Enabled = _feature.CanBrowse && _feature.IsEnabled; + btnSelect.Enabled = _feature.IsEnabled; + btnBrowse.Enabled = _feature.CanBrowse && _feature.IsEnabled; + cbFormat.Enabled = _feature.CanBrowse && _feature.IsEnabled; + cbEncoding.Enabled = _feature.CanEncoding; + + rbFile.Enabled = rbEvent.Enabled = rbBoth.Enabled = _feature.LogTargetW3C == -1; + if (_feature.LogTargetW3C != -1) + { + switch (_feature.LogTargetW3C) + { + case 1: + rbFile.Checked = true; + break; + case 2: + rbEvent.Checked = true; + break; + default: + rbBoth.Checked = true; + break; + } + } + + if (_feature.Period == 0) + { + rbSize.Checked = true; + cbSchedule.Enabled = false; + txtSize.Text = _feature.TruncateSize.ToString(); + } + else + { + rbSchedule.Checked = true; + txtSize.Enabled = false; + switch (_feature.Period) + { + case 2: + cbSchedule.SelectedIndex = 2; + break; + case 1: + cbSchedule.SelectedIndex = 1; + break; + case 4: + cbSchedule.SelectedIndex = 0; + break; + case 3: + cbSchedule.SelectedIndex = 3; + break; + } + } + + cbLocalTime.Checked = _feature.LocalTimeRollover; + + _initialized = true; + } + + Tasks.Fill(tsActionPanel, cmsActionPanel); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + private void txtPath_TextChanged(object sender, EventArgs e) + { + _feature.Directory = txtPath.Text; + InformChanges(); + } + + protected override bool HasChanges + { + get { return _hasChanges; } + } + + protected override bool CanApplyChanges + { + get { return true; } + } + + private void cbEncoding_SelectedIndexChanged(object sender, EventArgs e) + { + _feature.Encoding = cbEncoding.SelectedIndex; + InformChanges(); + } + + protected override bool ApplyChanges() + { + if (!_feature.ApplyChanges()) + { + return false; + } + + ClearChanges(); + return true; + } + + protected override void CancelChanges() + { + _feature.CancelChanges(); + ClearChanges(); + } + + private void btnSelect_Click(object sender, EventArgs e) + { + _feature.SelectFields(); + } + + private void rbFile_CheckedChanged(object sender, EventArgs e) + { + if (rbFile.Checked) + { + _feature.LogTargetW3C = 1; + } + else if (rbEvent.Checked) + { + _feature.LogTargetW3C = 2; + } + else + { + _feature.LogTargetW3C = 3; + } + } + + private void rbSchedule_CheckedChanged(object sender, EventArgs e) + { + if (rbSchedule.Checked) + { + switch (cbSchedule.SelectedIndex) + { + case 0: + _feature.Period = 4; + break; + case 1: + _feature.Period = 1; + break; + case 2: + _feature.Period = 2; + break; + case 3: + _feature.Period = 3; + break; + } + } + else if (rbSize.Checked) + { + _feature.Period = 0; + _feature.TruncateSize = long.Parse(txtSize.Text); + } + } + + private void cbLocalTime_CheckedChanged(object sender, EventArgs e) + { + _feature.LocalTimeRollover = cbLocalTime.Checked; + } + } +} diff --git a/JexusManager.Features.Logging/LoggingPage.resx b/JexusManager.Features.Logging/LoggingPage.resx new file mode 100644 index 00000000..5849981e --- /dev/null +++ b/JexusManager.Features.Logging/LoggingPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Logging/LoggingSettingsSavedEventHandler.cs b/JexusManager.Features.Logging/LoggingSettingsSavedEventHandler.cs new file mode 100644 index 00000000..d34e4924 --- /dev/null +++ b/JexusManager.Features.Logging/LoggingSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Logging +{ + public delegate void LoggingSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Logging/Properties/AssemblyInfo.cs b/JexusManager.Features.Logging/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..1e24a619 --- /dev/null +++ b/JexusManager.Features.Logging/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Logging")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Logging")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("5c2ae18d-d829-481b-8fca-8ad0f69e68c8")] + +// 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")] diff --git a/JexusManager.Features.Logging/Properties/Resources.Designer.cs b/JexusManager.Features.Logging/Properties/Resources.Designer.cs new file mode 100644 index 00000000..bf8a19c1 --- /dev/null +++ b/JexusManager.Features.Logging/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Logging.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Logging.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap logging_36 { + get { + object obj = ResourceManager.GetObject("logging_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Logging/Properties/Resources.resx b/JexusManager.Features.Logging/Properties/Resources.resx new file mode 100644 index 00000000..4ee996c0 --- /dev/null +++ b/JexusManager.Features.Logging/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\logging_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Logging/Resources/logging_36.png b/JexusManager.Features.Logging/Resources/logging_36.png new file mode 100644 index 00000000..903e25a0 Binary files /dev/null and b/JexusManager.Features.Logging/Resources/logging_36.png differ diff --git a/JexusManager.Features.Logging/packages.config b/JexusManager.Features.Logging/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.Logging/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.MimeMap/JexusManager.Features.MimeMap.csproj b/JexusManager.Features.MimeMap/JexusManager.Features.MimeMap.csproj new file mode 100644 index 00000000..c9d8f5cf --- /dev/null +++ b/JexusManager.Features.MimeMap/JexusManager.Features.MimeMap.csproj @@ -0,0 +1,130 @@ + + + + + Debug + AnyCPU + {FD1A63C6-458F-496B-9923-80ECA082C7EC} + Library + Properties + JexusManager.Features.MimeMap + JexusManager.Features.MimeMap + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + + UserControl + + + MimeMapPage.cs + + + + Form + + + NewMapItemDialog.cs + + + + True + True + Resources.resx + + + + + MimeMapPage.cs + + + NewMapItemDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.MimeMap/JexusManager.snk b/JexusManager.Features.MimeMap/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.MimeMap/JexusManager.snk differ diff --git a/JexusManager.Features.MimeMap/MimeMapFeature.cs b/JexusManager.Features.MimeMap/MimeMapFeature.cs new file mode 100644 index 00000000..3331d07d --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapFeature.cs @@ -0,0 +1,170 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.MimeMap +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class MimeMapFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly MimeMapFeature _owner; + + public FeatureTaskList(MimeMapFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + } + + public MimeMapFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationSection section = service.GetSection("system.webServer/staticContent"); + this.CanRevert = section.CanRevert(); + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection section = service.GetSection("system.webServer/staticContent"); + return section.GetCollection(); + } + + public void Add() + { + var dialog = new NewMapItemDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected authorization rule?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + var dialog = new NewMapItemDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + protected override void OnSettingsSaved() + { + this.MimeMapSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210520"); + return false; + } + + public bool CanRevert { get; private set; } + + public MimeMapSettingsSavedEventHandler MimeMapSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "MIME Types"; } + } + } +} diff --git a/JexusManager.Features.MimeMap/MimeMapItem.cs b/JexusManager.Features.MimeMap/MimeMapItem.cs new file mode 100644 index 00000000..cd1317cc --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapItem.cs @@ -0,0 +1,50 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.MimeMap +{ + using Microsoft.Web.Administration; + + internal class MimeMapItem : IItem + { + public MimeMapItem(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inhertied"; + if (element == null) + { + return; + } + + this.FileExtension = (string)element["fileExtension"]; + this.MimeType = (string)element["mimeType"]; + } + + public string MimeType { get; set; } + + public string FileExtension { get; set; } + + public ConfigurationElement Element { get; set; } + + public string Flag { get; set; } + + public bool Equals(MimeMapItem other) + { + // all properties + return this.Match(other) && other.MimeType == this.MimeType; + } + + public void Apply() + { + this.Element["fileExtension"] = this.FileExtension; + this.Element["mimeType"] = this.MimeType; + } + + public bool Match(MimeMapItem other) + { + // match combined keys. + return other != null && other.FileExtension == this.FileExtension; + } + } +} diff --git a/JexusManager.Features.MimeMap/MimeMapModule.cs b/JexusManager.Features.MimeMap/MimeMapModule.cs new file mode 100644 index 00000000..e4bd60af --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.MimeMap +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class MimeMapModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(MimeMapPage), "MIME Types", + "Configure extensions and associated content types that are served as static files", Resources.mime_map_36, + Resources.mime_map_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.MimeMap/MimeMapModuleProvider.cs b/JexusManager.Features.MimeMap/MimeMapModuleProvider.cs new file mode 100644 index 00000000..091f198c --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.MimeMap +{ + using System; + + using Microsoft.Web.Management.Server; + + public class MimeMapModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(MimeMapModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.MimeMap/MimeMapPage.Designer.cs b/JexusManager.Features.MimeMap/MimeMapPage.Designer.cs new file mode 100644 index 00000000..e3d54338 --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapPage.Designer.cs @@ -0,0 +1,290 @@ +namespace JexusManager.Features.MimeMap +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class MimeMapPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chExtension = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chMime = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chExtension, + this.chMime, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); + // + // chExtension + // + this.chExtension.Text = "Extension"; + this.chExtension.Width = 120; + // + // chMime + // + this.chMime.Text = "MIME Type"; + this.chMime.Width = 120; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(649, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to manage the list of file name extensions and associated conten" + + "t types that are served as static files by the Web server."; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(132, 25); + this.label3.TabIndex = 2; + this.label3.Text = "MIME Types"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // MimeMapPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "MimeMapPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chExtension; + private ColumnHeader chMime; + } +} diff --git a/JexusManager.Features.MimeMap/MimeMapPage.cs b/JexusManager.Features.MimeMap/MimeMapPage.cs new file mode 100644 index 00000000..a705c7e4 --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapPage.cs @@ -0,0 +1,130 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.MimeMap +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class MimeMapPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly MimeMapPage _owner; + + public PageTaskList(MimeMapPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class MimeMapListViewItem : ListViewItem + { + public MimeMapItem Item { get; } + + private readonly MimeMapPage _page; + + public MimeMapListViewItem(MimeMapItem item, MimeMapPage page) + : base(item.FileExtension) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.MimeType)); + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private MimeMapFeature _feature; + private PageTaskList _taskList; + + public MimeMapPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new MimeMapFeature(this.Module); + _feature.MimeMapSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new MimeMapListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (MimeMapListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void listView1_SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((MimeMapListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.MimeMap/MimeMapPage.resx b/JexusManager.Features.MimeMap/MimeMapPage.resx new file mode 100644 index 00000000..464d127c --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapPage.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 497, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.MimeMap/MimeMapSettingsSavedEventHandler.cs b/JexusManager.Features.MimeMap/MimeMapSettingsSavedEventHandler.cs new file mode 100644 index 00000000..2ed136af --- /dev/null +++ b/JexusManager.Features.MimeMap/MimeMapSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.MimeMap +{ + public delegate void MimeMapSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.MimeMap/NewMapItemDialog.cs b/JexusManager.Features.MimeMap/NewMapItemDialog.cs new file mode 100644 index 00000000..cb9efef5 --- /dev/null +++ b/JexusManager.Features.MimeMap/NewMapItemDialog.cs @@ -0,0 +1,71 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.MimeMap +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewMapItemDialog : DialogForm + { + public NewMapItemDialog(IServiceProvider serviceProvider, MimeMapItem existing, MimeMapFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = string.Format("{0} MIME Type", existing == null ? "Add" : "Edit"); + Item = existing ?? new MimeMapItem(null); + if (existing != null) + { + txtExtension.Text = Item.FileExtension; + txtType.Text = Item.MimeType; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.FileExtension = txtExtension.Text; + Item.MimeType = txtType.Text; + if (feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "This MIME map already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtExtension, "TextChanged") + .Merge(Observable.FromEventPattern(txtType, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtExtension.Text) + && !string.IsNullOrWhiteSpace(txtType.Text); + })); + } + + public MimeMapItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210520"); + } + } +} diff --git a/JexusManager.Features.MimeMap/NewMapItemDialog.designer.cs b/JexusManager.Features.MimeMap/NewMapItemDialog.designer.cs new file mode 100644 index 00000000..1b233746 --- /dev/null +++ b/JexusManager.Features.MimeMap/NewMapItemDialog.designer.cs @@ -0,0 +1,125 @@ +namespace JexusManager.Features.MimeMap +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewMapItemDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtExtension = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtType = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(227, 131); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(126, 131); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 9); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(103, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "File name extension:"; + // + // txtExtension + // + this.txtExtension.Enabled = false; + this.txtExtension.Location = new System.Drawing.Point(15, 25); + this.txtExtension.Name = "txtExtension"; + this.txtExtension.Size = new System.Drawing.Size(133, 20); + this.txtExtension.TabIndex = 6; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 64); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(61, 13); + this.label2.TabIndex = 7; + this.label2.Text = "MIME type:"; + // + // txtType + // + this.txtType.Location = new System.Drawing.Point(15, 80); + this.txtType.Name = "txtType"; + this.txtType.Size = new System.Drawing.Size(307, 20); + this.txtType.TabIndex = 8; + // + // NewMapItemDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(334, 166); + this.Controls.Add(this.txtType); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtExtension); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewMapItemDialog"; + this.Text = "Add MIME Type"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtExtension; + private Label label2; + private TextBox txtType; + } +} diff --git a/JexusManager.Features.MimeMap/NewMapItemDialog.resx b/JexusManager.Features.MimeMap/NewMapItemDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.MimeMap/NewMapItemDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.MimeMap/Properties/AssemblyInfo.cs b/JexusManager.Features.MimeMap/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..704151e2 --- /dev/null +++ b/JexusManager.Features.MimeMap/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.MimeMap")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.MimeMap")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("fd1a63c6-458f-496b-9923-80eca082c7ec")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.MimeMap/Properties/Resources.Designer.cs b/JexusManager.Features.MimeMap/Properties/Resources.Designer.cs new file mode 100644 index 00000000..7d0b5793 --- /dev/null +++ b/JexusManager.Features.MimeMap/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.MimeMap.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.MimeMap.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap mime_map_36 { + get { + object obj = ResourceManager.GetObject("mime_map_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.MimeMap/Properties/Resources.resx b/JexusManager.Features.MimeMap/Properties/Resources.resx new file mode 100644 index 00000000..bf830da3 --- /dev/null +++ b/JexusManager.Features.MimeMap/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\mime_map_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.MimeMap/Resources/mime_map_36.png b/JexusManager.Features.MimeMap/Resources/mime_map_36.png new file mode 100644 index 00000000..621d9509 Binary files /dev/null and b/JexusManager.Features.MimeMap/Resources/mime_map_36.png differ diff --git a/JexusManager.Features.MimeMap/packages.config b/JexusManager.Features.MimeMap/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.MimeMap/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Modules/GlobalModule.cs b/JexusManager.Features.Modules/GlobalModule.cs new file mode 100644 index 00000000..9798ad47 --- /dev/null +++ b/JexusManager.Features.Modules/GlobalModule.cs @@ -0,0 +1,48 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using Microsoft.Web.Administration; + + internal class GlobalModule : IItem + { + public GlobalModule(ConfigurationElement element) + { + if (element == null) + { + return; + } + + this.Name = (string)element["name"]; + this.Image = (string)element["image"]; + this.Element = element; + } + + public string Name { get; set; } + + public string Image { get; set; } + public bool Loaded { get; set; } + + public bool Equals(GlobalModule other) + { + return Match(other) && other.Image == this.Image; + } + + public string Flag { get; set; } + + public void Apply() + { + this.Element["name"] = this.Name; + this.Element["image"] = this.Image; + } + + public ConfigurationElement Element { get; set; } + + public bool Match(GlobalModule other) + { + return other != null && other.Name == this.Name; + } + } +} diff --git a/JexusManager.Features.Modules/JexusManager.Features.Modules.csproj b/JexusManager.Features.Modules/JexusManager.Features.Modules.csproj new file mode 100644 index 00000000..95e5692c --- /dev/null +++ b/JexusManager.Features.Modules/JexusManager.Features.Modules.csproj @@ -0,0 +1,151 @@ + + + + + Debug + AnyCPU + {8BFAB6BC-57C1-44FC-88E2-B3CE5B75BE9F} + Library + Properties + JexusManager.Features.Modules + JexusManager.Features.Modules + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + + + UserControl + + + ModulesPage.cs + + + + Form + + + NativeModulesDialog.cs + + + Form + + + NewModuleDialog.cs + + + Form + + + NewNativeDialog.cs + + + + True + True + Resources.resx + + + + + ModulesPage.cs + + + NativeModulesDialog.cs + + + NewModuleDialog.cs + + + NewNativeDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Modules/JexusManager.snk b/JexusManager.Features.Modules/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Modules/JexusManager.snk differ diff --git a/JexusManager.Features.Modules/ModulesFeature.cs b/JexusManager.Features.Modules/ModulesFeature.cs new file mode 100644 index 00000000..bbbc6ba6 --- /dev/null +++ b/JexusManager.Features.Modules/ModulesFeature.cs @@ -0,0 +1,383 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.Modules +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + using Microsoft.Web.Management.Server; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class ModulesFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly ModulesFeature _owner; + + public FeatureTaskList(ModulesFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + if (_owner.IsInOrder) + { + result.Add(GetMoveUpTaskItem(_owner.CanMoveUp)); + result.Add(GetMoveDownTaskItem(_owner.CanMoveDown)); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Unorder", "View Unordered List...", string.Empty).SetUsage()); + } + else + { + result.Add(new MethodTaskItem("AddManaged", "Add Managed Module...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Add", "Configure Native Modules...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage(_owner.SelectedItem.IsManaged)); + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + if (_owner.CanRevert) + { + result.Add(new MethodTaskItem("Revert", "Revert to Parent", string.Empty).SetUsage()); + } + + result.Add(new MethodTaskItem("InOrder", "View Ordered List...", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddManaged() + { + _owner.AddManaged(); + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Rename() + { + _owner.Rename(); + } + + [Obfuscation(Exclude = true)] + public override void MoveUp() + { + _owner.MoveUp(); + } + + [Obfuscation(Exclude = true)] + public override void MoveDown() + { + _owner.MoveDown(); + } + + [Obfuscation(Exclude = true)] + public void InOrder() + { + _owner.InOrder(); + } + + [Obfuscation(Exclude = true)] + public void Unorder() + { + _owner.Unorder(); + } + + [Obfuscation(Exclude = true)] + public void Revert() + { + _owner.Revert(); + } + } + + public ModulesFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + this.GlobalModules = new List(); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + + ConfigurationSection globalSection = service.GetSection("system.webServer/globalModules", null, false); + ConfigurationElementCollection globalCollection = globalSection.GetCollection(); + foreach (ConfigurationElement addElement in globalCollection) + { + this.GlobalModules.Add(new GlobalModule(addElement)); + } + + this.CanRevert = service.Scope != ManagementScope.Server; + this.IsInOrder = false; + LoadItems(); + } + + public override void LoadItems() + { + this.Items.Clear(); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationElementCollection collection = this.GetCollection(service); + foreach (ConfigurationElement addElement in collection) + { + var item = new ModulesItem(addElement); + item.Load(this); + this.Items.Add(item); + } + + this.OnSettingsSaved(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + // server level modules are in "" location. + ConfigurationSection section = service.Scope == ManagementScope.Server ? service.GetSection("system.webServer/modules", string.Empty) : service.GetSection("system.webServer/modules", null, false); + return section.GetCollection(); + } + + public List GlobalModules { get; set; } + + public void Add() + { + var dialog = new NativeModulesDialog(this.Module, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + foreach (var item in dialog.Items) + { + this.Items.Add(item); + this.SelectedItem = item; + // server level modules are in "" location. + ConfigurationSection section = service.Scope == ManagementScope.Server ? service.GetSection("system.webServer/modules", string.Empty) : service.GetSection("system.webServer/modules"); + ConfigurationElementCollection collection = section.GetCollection(); + item.AppendTo(collection); + } + + // TODO: how to add item? + service.ServerManager.CommitChanges(); + this.OnSettingsSaved(); + } + + public void AddManaged() + { + var dialog = new NewModuleDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddGlobal(GlobalModule item) + { + this.GlobalModules.Add(item); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationSection globalSection = service.GetSection("system.webServer/globalModules", null, false); + ConfigurationElementCollection globalCollection = globalSection.GetCollection(); + item.AppendTo(globalCollection); + service.ServerManager.CommitChanges(); + } + + public void RemoveGlobal(GlobalModule item) + { + this.GlobalModules.Remove(item); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationSection globalSection = service.GetSection("system.webServer/globalModules", null, false); + ConfigurationElementCollection globalCollection = globalSection.GetCollection(); + globalCollection.Remove(item.Element); + service.ServerManager.CommitChanges(); + } + + public void Remove() + { + // TODO: add multiple support + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected module or modules?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + var dialog = new NewModuleDialog(this.Module, this.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + public void Rename() + { + } + + public void MoveUp() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + MoveUpItem(); + } + + public void MoveDown() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + MoveDownItem(); + } + + public void InOrder() + { + this.IsInOrder = true; + this.OnSettingsSaved(); + } + + public void Unorder() + { + this.IsInOrder = false; + this.OnSettingsSaved(); + } + + public void Revert() + { + if (!CanRevert) + { + throw new InvalidOperationException("Revert operation cannot be done at server level"); + } + + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "Reverting to the parent configuration will result in the loss of all settings in the local configuration file for this feature. Are you sure you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + RevertItems(); + } + + protected override void OnSettingsSaved() + { + this.ModulesSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210521"); + return false; + } + + public bool IsInOrder { get; private set; } + + public bool CanRevert { get; private set; } + + public ModulesSettingsSavedEventHandler ModulesSettingsUpdated { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "Modules"; } + } + } +} diff --git a/JexusManager.Features.Modules/ModulesItem.cs b/JexusManager.Features.Modules/ModulesItem.cs new file mode 100644 index 00000000..f0258f8b --- /dev/null +++ b/JexusManager.Features.Modules/ModulesItem.cs @@ -0,0 +1,153 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using System.Collections.Generic; + using System.Linq; + using System.Text; + + using Microsoft.Web.Administration; + + internal class ModulesItem : IItem + { + public ModulesItem(ConfigurationElement element) + { + this.Element = element; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inhertied"; + if (element == null) + { + PreConditions = new List(); + Type = string.Empty; + return; + } + + this.Name = (string)element["name"]; + this.Type = (string)element["type"]; + var content = (string)element["preCondition"]; + this.PreConditions = content.Split(',').ToList(); + + IsLocked = element.GetIsLocked(); + if (!string.IsNullOrWhiteSpace(this.Type)) + { + this.IsManaged = true; + } + } + + public ModulesItem Load(ModulesFeature feature) + { + if (IsManaged) + { + return this; + } + + foreach (var item in feature.GlobalModules) + { + if (item.Name == this.Name) + { + this.Type = item.Image; + item.Loaded = true; + this.GlobalModule = item; + this.IsManaged = false; + break; + } + } + + return this; + } + + public string IsLocked { get; set; } + + public GlobalModule GlobalModule { get; set; } + + public List PreConditions { get; set; } + + public string Type { get; set; } + + public string Name { get; set; } + + public ConfigurationElement Element { get; set; } + + public string Flag { get; set; } + + public bool IsManaged { get; set; } + + public bool ForManagedOnly + { + get + { + return this.PreConditions.Contains("managedHandler"); + } + + set + { + var current = this.PreConditions.Contains("managedHandler"); + if (value == current) + { + return; + } + + if (value) + { + this.PreConditions.Add("managedHandler"); + } + else + { + this.PreConditions.Remove("managedHandler"); + } + } + } + + public bool Equals(ModulesItem other) + { + // all properties + return this.Match(other) && other.Type == this.Type; + } + + public void Apply() + { + Element["name"] = Name; + if (IsManaged) + { + Element["type"] = Type; + } + + Element["preCondition"] = Combine(PreConditions); + Element.SetIsLocked(IsLocked); + } + + private static string Combine(List preConditions) + { + if (preConditions.Count == 0) + { + return string.Empty; + } + + var result = new StringBuilder(preConditions[0]); + for (int index = 1; index < preConditions.Count; index++) + { + result.AppendFormat(",{0}", preConditions[index]); + } + + return result.ToString(); + } + + public bool Match(ModulesItem other) + { + // match combined keys. + return other != null && other.Name == this.Name; + } + + public void Unload() + { + if (GlobalModule == null) + { + return; + } + + this.GlobalModule.Loaded = false; + this.GlobalModule = null; + } + } +} diff --git a/JexusManager.Features.Modules/ModulesModule.cs b/JexusManager.Features.Modules/ModulesModule.cs new file mode 100644 index 00000000..c4f38f30 --- /dev/null +++ b/JexusManager.Features.Modules/ModulesModule.cs @@ -0,0 +1,25 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using System; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + using Properties; + + internal class ModulesModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)this.GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(ModulesPage), "Modules", + "Configure native and managed code modules that process requests on the Web server", Resources.modules_36, + Resources.modules_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.Modules/ModulesModuleProvider.cs b/JexusManager.Features.Modules/ModulesModuleProvider.cs new file mode 100644 index 00000000..445c1a4f --- /dev/null +++ b/JexusManager.Features.Modules/ModulesModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using System; + + using Microsoft.Web.Management.Server; + + public class ModulesModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(this.Name, typeof(ModulesModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.Modules/ModulesPage.Designer.cs b/JexusManager.Features.Modules/ModulesPage.Designer.cs new file mode 100644 index 00000000..627d381b --- /dev/null +++ b/JexusManager.Features.Modules/ModulesPage.Designer.cs @@ -0,0 +1,298 @@ +namespace JexusManager.Features.Modules +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class ModulesPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chCode = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chModule = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chCode, + this.chModule, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 180; + // + // chCode + // + this.chCode.Text = "Code"; + this.chCode.Width = 180; + // + // chModule + // + this.chModule.Text = "Module Type"; + this.chModule.Width = 120; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(551, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure the native and managed code modules that process re" + + "quests made to the Web server."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(94, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Modules"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // ModulesPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "ModulesPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private ColumnHeader chName; + private ColumnHeader chCode; + private ColumnHeader chModule; + } +} diff --git a/JexusManager.Features.Modules/ModulesPage.cs b/JexusManager.Features.Modules/ModulesPage.cs new file mode 100644 index 00000000..b72201c9 --- /dev/null +++ b/JexusManager.Features.Modules/ModulesPage.cs @@ -0,0 +1,132 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class ModulesPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly ModulesPage _owner; + + public PageTaskList(ModulesPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class ModulesListViewItem : ListViewItem + { + public ModulesItem Item { get; } + + private readonly ModulesPage _page; + + public ModulesListViewItem(ModulesItem item, ModulesPage page) + : base(item.Name) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.IsManaged ? item.Type : item.GlobalModule.Image)); + this.SubItems.Add(new ListViewSubItem(this, item.IsManaged ? "Managed" : "Native")); + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private ModulesFeature _feature; + private PageTaskList _taskList; + + public ModulesPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new ModulesFeature(this.Module); + _feature.ModulesSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + toolStrip2.Visible = !_feature.IsInOrder; + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new ModulesListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (ModulesListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((ModulesListViewItem)listView1.SelectedItems[0]).Item + : null; + // TODO: optimize refresh when null to not null (vice versa) + this.Refresh(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.Modules/ModulesPage.resx b/JexusManager.Features.Modules/ModulesPage.resx new file mode 100644 index 00000000..464d127c --- /dev/null +++ b/JexusManager.Features.Modules/ModulesPage.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 602, 17 + + + 497, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Modules/ModulesSettingsSavedEventHandler.cs b/JexusManager.Features.Modules/ModulesSettingsSavedEventHandler.cs new file mode 100644 index 00000000..74b1cdcc --- /dev/null +++ b/JexusManager.Features.Modules/ModulesSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + public delegate void ModulesSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Modules/NativeModulesDialog.Designer.cs b/JexusManager.Features.Modules/NativeModulesDialog.Designer.cs new file mode 100644 index 00000000..6d793c5a --- /dev/null +++ b/JexusManager.Features.Modules/NativeModulesDialog.Designer.cs @@ -0,0 +1,155 @@ +namespace JexusManager.Features.Modules +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class NativeModulesDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lvModules = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.label1 = new System.Windows.Forms.Label(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnRegister = new System.Windows.Forms.Button(); + this.btnEdit = new System.Windows.Forms.Button(); + this.btnRemove = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lvModules + // + this.lvModules.CheckBoxes = true; + this.lvModules.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName}); + this.lvModules.FullRowSelect = true; + this.lvModules.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.lvModules.HideSelection = false; + this.lvModules.Location = new System.Drawing.Point(12, 25); + this.lvModules.MultiSelect = false; + this.lvModules.Name = "lvModules"; + this.lvModules.Size = new System.Drawing.Size(300, 270); + this.lvModules.TabIndex = 1; + this.lvModules.UseCompatibleStateImageBehavior = false; + this.lvModules.View = System.Windows.Forms.View.Details; + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 350; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(237, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Select one or more registered modules to enable:"; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(347, 301); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(251, 301); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 4; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnRegister + // + this.btnRegister.Location = new System.Drawing.Point(318, 25); + this.btnRegister.Name = "btnRegister"; + this.btnRegister.Size = new System.Drawing.Size(124, 23); + this.btnRegister.TabIndex = 5; + this.btnRegister.Text = "Register..."; + this.btnRegister.UseVisualStyleBackColor = true; + // + // btnEdit + // + this.btnEdit.Enabled = false; + this.btnEdit.Location = new System.Drawing.Point(318, 54); + this.btnEdit.Name = "btnEdit"; + this.btnEdit.Size = new System.Drawing.Size(124, 23); + this.btnEdit.TabIndex = 6; + this.btnEdit.Text = "Edit..."; + this.btnEdit.UseVisualStyleBackColor = true; + // + // btnRemove + // + this.btnRemove.Enabled = false; + this.btnRemove.Location = new System.Drawing.Point(318, 83); + this.btnRemove.Name = "btnRemove"; + this.btnRemove.Size = new System.Drawing.Size(124, 23); + this.btnRemove.TabIndex = 7; + this.btnRemove.Text = "Remove"; + this.btnRemove.UseVisualStyleBackColor = true; + // + // NativeModulesDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(454, 336); + this.Controls.Add(this.btnRemove); + this.Controls.Add(this.btnEdit); + this.Controls.Add(this.btnRegister); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.label1); + this.Controls.Add(this.lvModules); + this.Name = "NativeModulesDialog"; + this.Text = "Configure Native Modules"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NativeModulesDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private ListView lvModules; + private ColumnHeader chName; + private Label label1; + private Button btnCancel; + private Button btnOK; + private Button btnRegister; + private Button btnEdit; + private Button btnRemove; + } +} diff --git a/JexusManager.Features.Modules/NativeModulesDialog.cs b/JexusManager.Features.Modules/NativeModulesDialog.cs new file mode 100644 index 00000000..a758417c --- /dev/null +++ b/JexusManager.Features.Modules/NativeModulesDialog.cs @@ -0,0 +1,127 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class NativeModulesDialog : DialogForm + { + public NativeModulesDialog(IServiceProvider serviceProvider, ModulesFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + if (feature.CanRevert) + { + btnRegister.Visible = btnEdit.Visible = btnRemove.Visible = false; + lvModules.Width += 130; + } + + foreach (var global in feature.GlobalModules) + { + if (feature.Items.Any(module => module.Name == global.Name)) + { + continue; + } + + var item = lvModules.Items.Add(global.Name); + item.Tag = global; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Items = new List(); + foreach (ListViewItem item in lvModules.Items) + { + if (!item.Checked) + { + continue; + } + + var module = (GlobalModule)item.Tag; + module.Loaded = true; + Items.Add(new ModulesItem(null) { Name = module.Name }.Load(feature)); + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(lvModules, "SelectedIndexChanged") + .Subscribe(evt => + { + btnEdit.Enabled = btnRemove.Enabled = lvModules.SelectedItems.Count > 0; + })); + + container.Add( + Observable.FromEventPattern(btnRegister, "Click") + .Subscribe(evt => + { + var dialog = new NewNativeDialog(ServiceProvider, null); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var item = lvModules.Items.Add(dialog.Item.Name); + item.Tag = dialog.Item; + feature.AddGlobal(dialog.Item); + })); + + container.Add( + Observable.FromEventPattern(btnEdit, "Click") + .Subscribe(evt => + { + GlobalModule item = (GlobalModule)lvModules.SelectedItems[0].Tag; + var dialog = new NewNativeDialog(ServiceProvider, item); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + dialog.Item.Apply(); + lvModules.SelectedItems[0].Text = dialog.Item.Name; + })); + + container.Add( + Observable.FromEventPattern(btnRemove, "Click") + .Subscribe(evt => + { + var dialog = (IManagementUIService)GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected native module registration?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + GlobalModule item = (GlobalModule)lvModules.SelectedItems[0].Tag; + feature.RemoveGlobal(item); + lvModules.SelectedItems[0].Remove(); + })); + } + + public List Items { get; set; } + + private void NativeModulesDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210521"); + } + } +} diff --git a/JexusManager.Features.Modules/NativeModulesDialog.resx b/JexusManager.Features.Modules/NativeModulesDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Modules/NativeModulesDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Modules/NewModuleDialog.Designer.cs b/JexusManager.Features.Modules/NewModuleDialog.Designer.cs new file mode 100644 index 00000000..d8c3594b --- /dev/null +++ b/JexusManager.Features.Modules/NewModuleDialog.Designer.cs @@ -0,0 +1,137 @@ +namespace JexusManager.Features.Modules +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewModuleDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.txtDescription = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtPath = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.cbManagedHandler = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(317, 186); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(216, 186); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // txtDescription + // + this.txtDescription.AutoSize = true; + this.txtDescription.Location = new System.Drawing.Point(12, 9); + this.txtDescription.Name = "txtDescription"; + this.txtDescription.Size = new System.Drawing.Size(38, 13); + this.txtDescription.TabIndex = 2; + this.txtDescription.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(269, 20); + this.txtName.TabIndex = 6; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(15, 82); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(360, 21); + this.txtPath.TabIndex = 8; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 66); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(34, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Type:"; + // + // cbManagedHandler + // + this.cbManagedHandler.AutoSize = true; + this.cbManagedHandler.Location = new System.Drawing.Point(15, 130); + this.cbManagedHandler.Name = "cbManagedHandler"; + this.cbManagedHandler.Size = new System.Drawing.Size(361, 17); + this.cbManagedHandler.TabIndex = 10; + this.cbManagedHandler.Text = "Invoke only for requests to ASP.NET applications or managed handlers"; + this.cbManagedHandler.UseVisualStyleBackColor = true; + // + // NewModuleDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(424, 221); + this.Controls.Add(this.cbManagedHandler); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.txtName); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewModuleDialog"; + this.Text = "Add Managed Module"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRestrictionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label txtDescription; + private TextBox txtName; + private ComboBox txtPath; + private Label label1; + private CheckBox cbManagedHandler; + } +} diff --git a/JexusManager.Features.Modules/NewModuleDialog.cs b/JexusManager.Features.Modules/NewModuleDialog.cs new file mode 100644 index 00000000..ec550dda --- /dev/null +++ b/JexusManager.Features.Modules/NewModuleDialog.cs @@ -0,0 +1,74 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewModuleDialog : DialogForm + { + public NewModuleDialog(IServiceProvider serviceProvider, ModulesItem existing, ModulesFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add Managed Module" : "Edit Managed Module"; + txtName.ReadOnly = existing != null; + Item = existing ?? new ModulesItem(null); + if (existing != null) + { + txtPath.Text = Item.Type; + txtName.Text = Item.Name; + cbManagedHandler.Checked = Item.ForManagedOnly; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.Type = txtPath.Text; + Item.Name = txtName.Text; + Item.ForManagedOnly = cbManagedHandler.Checked; + if (!txtName.ReadOnly && feature.Items.Any(item => item.Match(Item))) + { + ShowMessage( + "A module with this name already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtPath, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + } + + public ModulesItem Item { get; set; } + + private void NewRestrictionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210521"); + } + } +} diff --git a/JexusManager.Features.Modules/NewModuleDialog.resx b/JexusManager.Features.Modules/NewModuleDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Modules/NewModuleDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Modules/NewNativeDialog.Designer.cs b/JexusManager.Features.Modules/NewNativeDialog.Designer.cs new file mode 100644 index 00000000..e7323573 --- /dev/null +++ b/JexusManager.Features.Modules/NewNativeDialog.Designer.cs @@ -0,0 +1,133 @@ +namespace JexusManager.Features.Modules +{ + internal sealed partial class NewNativeDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(327, 136); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(231, 136); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(38, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(12, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(260, 20); + this.txtName.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 57); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(32, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Path:"; + // + // txtPath + // + this.txtPath.Location = new System.Drawing.Point(12, 73); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(372, 20); + this.txtPath.TabIndex = 5; + // + // btnBrowse + // + this.btnBrowse.Location = new System.Drawing.Point(390, 71); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(30, 23); + this.btnBrowse.TabIndex = 6; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + // + // NewNativeDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(434, 171); + this.Controls.Add(this.btnBrowse); + this.Controls.Add(this.txtPath); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewNativeDialog"; + this.Text = "Register Native Module"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtName; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txtPath; + private System.Windows.Forms.Button btnBrowse; + } +} diff --git a/JexusManager.Features.Modules/NewNativeDialog.cs b/JexusManager.Features.Modules/NewNativeDialog.cs new file mode 100644 index 00000000..5dd210ee --- /dev/null +++ b/JexusManager.Features.Modules/NewNativeDialog.cs @@ -0,0 +1,64 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Modules +{ + using System; + using System.Reactive.Disposables; + using System.Reactive.Linq; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewNativeDialog : DialogForm + { + public NewNativeDialog(IServiceProvider serviceProvider, GlobalModule existing) + : base(serviceProvider) + { + InitializeComponent(); + if (existing != null) + { + txtName.Text = existing.Name; + txtPath.Text = existing.Image; + this.Text = "Edit Native Module Registration"; + txtName.SelectAll(); + this.Item = existing; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnBrowse, "Click") + .Subscribe(evt => + { + DialogHelper.ShowFileDialog(txtPath, "(*.dll)|*.dll|All Files (*.*)|*.*"); + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtPath, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtPath.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (Item == null) + { + Item = new GlobalModule(null); + } + + this.Item.Name = txtName.Text; + this.Item.Image = txtPath.Text; + })); + } + + public GlobalModule Item { get; set; } + } +} diff --git a/JexusManager.Features.Modules/NewNativeDialog.resx b/JexusManager.Features.Modules/NewNativeDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Modules/NewNativeDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Modules/Properties/AssemblyInfo.cs b/JexusManager.Features.Modules/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..e1c33f8b --- /dev/null +++ b/JexusManager.Features.Modules/Properties/AssemblyInfo.cs @@ -0,0 +1,51 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.Modules")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.Modules")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("8bfab6bc-57c1-44fc-88e2-b3ce5b75be9f")] + +// 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")] + +[assembly: InternalsVisibleTo("JexusManager.Features.Handlers, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.Modules/Properties/Resources.Designer.cs b/JexusManager.Features.Modules/Properties/Resources.Designer.cs new file mode 100644 index 00000000..8207b510 --- /dev/null +++ b/JexusManager.Features.Modules/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.Modules.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.Modules.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap modules_36 { + get { + object obj = ResourceManager.GetObject("modules_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.Modules/Properties/Resources.resx b/JexusManager.Features.Modules/Properties/Resources.resx new file mode 100644 index 00000000..a8f57377 --- /dev/null +++ b/JexusManager.Features.Modules/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\modules_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.Modules/Resources/modules_36.png b/JexusManager.Features.Modules/Resources/modules_36.png new file mode 100644 index 00000000..512d5b7c Binary files /dev/null and b/JexusManager.Features.Modules/Resources/modules_36.png differ diff --git a/JexusManager.Features.Modules/packages.config b/JexusManager.Features.Modules/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.Modules/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/AppliesToItem.cs b/JexusManager.Features.RequestFiltering/AppliesToItem.cs new file mode 100644 index 00000000..78d58169 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/AppliesToItem.cs @@ -0,0 +1,42 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class AppliesToItem + { + public ConfigurationElement Child { get; set; } + + public AppliesToItem(ConfigurationElement child) + { + this.Child = child; + if (child == null) + { + return; + } + + this.FileExtension = (string)child["fileExtension"]; + } + + public string FileExtension { get; set; } + + public void Apply() + { + Child["fileExtension"] = FileExtension; + } + + public void AppendTo(ConfigurationElementCollection appliesToCollection) + { + if (Child == null) + { + Child = appliesToCollection.CreateElement(); + appliesToCollection.Add(Child); + } + + Apply(); + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/DenyStringsItem.cs b/JexusManager.Features.RequestFiltering/DenyStringsItem.cs new file mode 100644 index 00000000..4d3aa2d3 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/DenyStringsItem.cs @@ -0,0 +1,42 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class DenyStringsItem + { + public ConfigurationElement Child { get; set; } + + public DenyStringsItem(ConfigurationElement child) + { + this.Child = child; + if (child == null) + { + return; + } + + this.DenyString = (string)child["string"]; + } + + public string DenyString { get; set; } + + public void Apply() + { + Child["string"] = DenyString; + } + + public void AppendTo(ConfigurationElementCollection denyStringsCollection) + { + if (Child == null) + { + Child = denyStringsCollection.CreateElement(); + denyStringsCollection.Add(Child); + } + + Apply(); + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/FileExtensionsFeature.cs b/JexusManager.Features.RequestFiltering/FileExtensionsFeature.cs new file mode 100644 index 00000000..f2a3f55f --- /dev/null +++ b/JexusManager.Features.RequestFiltering/FileExtensionsFeature.cs @@ -0,0 +1,139 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class FileExtensionsFeature : RequestFilteringFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly FileExtensionsFeature _owner; + + public FeatureTaskList(FileExtensionsFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + + result.Add(new MethodTaskItem("AddExtension", "Allow File Name Extension...", string.Empty).SetUsage()); + result.Add( + new MethodTaskItem("AddDenyExtension", "Deny File Name Extension...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddExtension() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public void AddDenyExtension() + { + _owner.AddDeny(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + } + + public FileExtensionsFeature(Module module) + : base(module) + { + } + + private TaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Add() + { + this.CreateExtension(true); + } + + public void AddDeny() + { + this.CreateExtension(false); + } + + private void CreateExtension(bool allowed) + { + var dialog = new NewExtensionDialog(this.Module, allowed); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected file extension?", this.Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public override void Load() + { + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection requestFilteringSection = service.GetSection("system.webServer/security/requestFiltering"); + return requestFilteringSection.GetCollection("fileExtensions"); + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526"); + return true; + } + + public override string Name + { + get + { + return "File Name Extensions"; + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/FileExtensionsItem.cs b/JexusManager.Features.RequestFiltering/FileExtensionsItem.cs new file mode 100644 index 00000000..1db92f91 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/FileExtensionsItem.cs @@ -0,0 +1,48 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class FileExtensionsItem : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(FileExtensionsItem other) + { + return other != null && other.Extension == Extension; + } + + public FileExtensionsItem(ConfigurationElement element) + { + this.Element = element; + Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + if (element == null) + { + return; + } + + Extension = (string)element["fileExtension"]; + Allowed = (bool)element["allowed"]; + } + + public string Flag { get; set; } + + public bool Allowed { get; set; } + + public string Extension { get; set; } + + public void Apply() + { + Element["fileExtension"] = Extension; + Element["allowed"] = Allowed; + } + + public bool Equals(FileExtensionsItem other) + { + return Match(other) && other.Allowed == Allowed; + } + } +} diff --git a/JexusManager.Features.RequestFiltering/FilteringRulesFeature.cs b/JexusManager.Features.RequestFiltering/FilteringRulesFeature.cs new file mode 100644 index 00000000..b8a442c1 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/FilteringRulesFeature.cs @@ -0,0 +1,141 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class FilteringRulesFeature : RequestFilteringFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly FilteringRulesFeature _owner; + + public FeatureTaskList(FilteringRulesFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + + result.Add(new MethodTaskItem("AddRule", "Add Filtering Rule...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem("EditRule", "Edit Filtering Rule...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddRule() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public void EditRule() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + } + + public FilteringRulesFeature(Module module) + : base(module) + { + } + + private TaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Add() + { + var dialog = new NewRuleDialog(this.Module, null); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Edit() + { + var dialog = new NewRuleDialog(this.Module, this.SelectedItem); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + this.EditItem(newItem); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected rule?", this.Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public override void Load() + { + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection requestFilteringSection = + service.GetSection("system.webServer/security/requestFiltering"); + return requestFilteringSection.GetCollection("filteringRules"); + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Rules"); + return true; + } + + public override string Name + { + get + { + return "Rules"; + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/FilteringRulesItem.cs b/JexusManager.Features.RequestFiltering/FilteringRulesItem.cs new file mode 100644 index 00000000..3254e52e --- /dev/null +++ b/JexusManager.Features.RequestFiltering/FilteringRulesItem.cs @@ -0,0 +1,148 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections.Generic; + using System.Linq; + using System.Text; + + using Microsoft.Web.Administration; + + internal class FilteringRulesItem : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(FilteringRulesItem other) + { + return other != null && other.Name == Name; + } + + public string Name { get; set; } + + public string ScanString + { + get + { + var result = new StringBuilder("Header"); + if (ScanUrl) + { + result.Insert(0, "Url, "); + } + + if (ScanQueryString) + { + result.Insert(0, "Query string, "); + } + + return result.ToString(); + } + } + + public string AppliesToString + { + get + { + return Combine(Extensions.Select(item => item.FileExtension).ToList()); + } + } + + private static string Combine(List preConditions) + { + if (preConditions.Count == 0) + { + return string.Empty; + } + + var result = new StringBuilder(preConditions[0]); + for (int index = 1; index < preConditions.Count; index++) + { + result.AppendFormat(", {0}", preConditions[index]); + } + + return result.ToString(); + } + + public string DenyStringsString + { + get + { + return Combine(DenyStrings.Select(item => item.DenyString).ToList()); + } + } + + public bool ScanUrl { get; set; } + public bool ScanQueryString { get; set; } + public List Headers { get; set; } + public List Extensions { get; set; } + public List DenyStrings { get; set; } + public string Flag { get; set; } + + public FilteringRulesItem(ConfigurationElement element) + { + this.Element = element; + Headers = new List(); + Extensions = new List(); + DenyStrings = new List(); + Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + if (element == null) + { + return; + } + + Name = (string)element["name"]; + ScanQueryString = (bool)element["scanQueryString"]; + ScanUrl = (bool)element["scanUrl"]; + foreach (ConfigurationElement child in element.GetCollection("scanHeaders")) + { + Headers.Add(new ScanHeadersItem(child)); + } + + foreach (ConfigurationElement child in element.GetCollection("appliesTo")) + { + Extensions.Add(new AppliesToItem(child)); + } + + foreach (ConfigurationElement child in element.GetCollection("denyStrings")) + { + DenyStrings.Add(new DenyStringsItem(child)); + } + } + + public void Apply() + { + Element["name"] = Name; + Element["scanQueryString"] = ScanQueryString; + Element["scanUrl"] = ScanUrl; + var collection = Element.GetCollection("scanHeaders"); + collection.Clear(); + collection.Delete(); + foreach (var header in Headers) + { + header.AppendTo(collection); + } + + var appliesToCollection = Element.GetCollection("appliesTo"); + appliesToCollection.Clear(); + appliesToCollection.Delete(); + foreach (var header in Extensions) + { + header.AppendTo(appliesToCollection); + } + + var denyStringsCollection = Element.GetCollection("denyStrings"); + denyStringsCollection.Clear(); + denyStringsCollection.Delete(); + foreach (var header in DenyStrings) + { + header.AppendTo(denyStringsCollection); + } + } + + public bool Equals(FilteringRulesItem other) + { + return Match(other); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/HeadersFeature.cs b/JexusManager.Features.RequestFiltering/HeadersFeature.cs new file mode 100644 index 00000000..075a25aa --- /dev/null +++ b/JexusManager.Features.RequestFiltering/HeadersFeature.cs @@ -0,0 +1,126 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class HeadersFeature : RequestFilteringFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly HeadersFeature _owner; + + public FeatureTaskList(HeadersFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + + { + result.Add(new MethodTaskItem("AddHeader", "Add Header...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddHeader() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + } + + public HeadersFeature(Module module) + : base(module) + { + } + + private TaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + public void Add() + { + var dialog = new NewHeaderDialog(this.Module); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected header?", this.Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public override void Load() + { + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection requestFilteringSection = + service.GetSection("system.webServer/security/requestFiltering"); + + + ConfigurationElement childElement = requestFilteringSection.ChildElements["requestLimits"]; + return childElement.GetCollection("headerLimits"); + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Headers"); + return true; + } + + public override string Name + { + get + { + return "Headers"; + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/HeadersItem.cs b/JexusManager.Features.RequestFiltering/HeadersItem.cs new file mode 100644 index 00000000..c27fa7f6 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/HeadersItem.cs @@ -0,0 +1,47 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class HeadersItem : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(HeadersItem other) + { + return other != null && other.Header == Header; + } + + public HeadersItem(ConfigurationElement element) + { + this.Element = element; + if (element == null) + { + return; + } + + Header = (string)element["header"]; + SizeLimit = (uint)element["sizeLimit"]; + } + + public uint SizeLimit { get; set; } + + public string Header { get; set; } + + public void Apply() + { + Element["header"] = Header; + Element["sizeLimit"] = SizeLimit; + } + + public string Flag { get; set; } + + public bool Equals(HeadersItem other) + { + return Match(other) && other.SizeLimit == SizeLimit; + } + } +} diff --git a/JexusManager.Features.RequestFiltering/HiddenSegmentsFeature.cs b/JexusManager.Features.RequestFiltering/HiddenSegmentsFeature.cs new file mode 100644 index 00000000..9426dc76 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/HiddenSegmentsFeature.cs @@ -0,0 +1,122 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class HiddenSegmentsFeature : RequestFilteringFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly HiddenSegmentsFeature _owner; + + public FeatureTaskList(HiddenSegmentsFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + + result.Add(new MethodTaskItem("AddSegement", "Add Hidden Segment...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddSegment() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + } + + public HiddenSegmentsFeature(Module module) + : base(module) + { + } + + private TaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Add() + { + var dialog = new NewHiddenSegmentDialog(this.Module); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected hidden segment?", this.Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public override void Load() + { + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection requestFilteringSection = + service.GetSection("system.webServer/security/requestFiltering"); + return requestFilteringSection.GetCollection("hiddenSegments"); + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Hidden_Segments"); + return true; + } + + public override string Name + { + get + { + return " Hidden Segments"; + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/HiddenSegmentsItem.cs b/JexusManager.Features.RequestFiltering/HiddenSegmentsItem.cs new file mode 100644 index 00000000..eb611c0c --- /dev/null +++ b/JexusManager.Features.RequestFiltering/HiddenSegmentsItem.cs @@ -0,0 +1,42 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class HiddenSegmentsItem : IItem + { + public HiddenSegmentsItem(ConfigurationElement element) + { + Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + Element = element; + if (element == null) + { + return; + } + + Segment = (string)element["segment"]; + } + + public string Segment { get; set; } + public string Flag { get; set; } + public ConfigurationElement Element { get; set; } + + public bool Equals(HiddenSegmentsItem other) + { + return Match(other); + } + + public bool Match(HiddenSegmentsItem other) + { + return other != null && other.Segment == Segment; + } + + public void Apply() + { + Element["segment"] = Segment; + } + } +} diff --git a/JexusManager.Features.RequestFiltering/IRequestFilteringFeature.cs b/JexusManager.Features.RequestFiltering/IRequestFilteringFeature.cs new file mode 100644 index 00000000..9354a7fc --- /dev/null +++ b/JexusManager.Features.RequestFiltering/IRequestFilteringFeature.cs @@ -0,0 +1,17 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Management.Client; + + internal interface IRequestFilteringFeature + { + TaskList GetTaskList(); + + bool ShowHelp(); + + RequestFilteringSettingsSavedEventHandler RequestFilteringSettingsUpdate { get; } + } +} \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/JexusManager.Features.RequestFiltering.csproj b/JexusManager.Features.RequestFiltering/JexusManager.Features.RequestFiltering.csproj new file mode 100644 index 00000000..a5abb2f0 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/JexusManager.Features.RequestFiltering.csproj @@ -0,0 +1,231 @@ + + + + + Debug + AnyCPU + {56D44318-5979-4FC5-9F01-4D7F78709A0E} + Library + Properties + JexusManager.Features.RequestFiltering + JexusManager.Features.RequestFiltering + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + + + + + + + + + + Form + + + NewExtensionDialog.cs + + + Form + + + NewHeaderDialog.cs + + + Form + + + NewHiddenSegmentDialog.cs + + + Form + + + NewQueryDialog.cs + + + Form + + + NewRuleDialog.cs + + + Form + + + NewUrlDialog.cs + + + Form + + + NewVerbDialog.cs + + + + True + True + Resources.resx + + + + + + + + UserControl + + + RequestFilteringPage.cs + + + + + Form + + + SegmentSettingsDialog.cs + + + + + + + + + NewExtensionDialog.cs + + + NewHeaderDialog.cs + + + NewHiddenSegmentDialog.cs + + + NewQueryDialog.cs + + + NewRuleDialog.cs + + + NewUrlDialog.cs + + + NewVerbDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + RequestFilteringPage.cs + + + SegmentSettingsDialog.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/JexusManager.snk b/JexusManager.Features.RequestFiltering/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.RequestFiltering/JexusManager.snk differ diff --git a/JexusManager.Features.RequestFiltering/NewExtensionDialog.Designer.cs b/JexusManager.Features.RequestFiltering/NewExtensionDialog.Designer.cs new file mode 100644 index 00000000..93c537a4 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewExtensionDialog.Designer.cs @@ -0,0 +1,102 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal sealed partial class NewExtensionDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(103, 13); + this.label1.TabIndex = 7; + this.label1.Text = "File name extension:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 6; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(139, 87); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(240, 87); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // NewExtensionDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 126); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewExtensionDialog"; + this.Text = "Allow File Name Extension"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHiddenSegmentDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.RequestFiltering/NewExtensionDialog.cs b/JexusManager.Features.RequestFiltering/NewExtensionDialog.cs new file mode 100644 index 00000000..d1d54ec4 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewExtensionDialog.cs @@ -0,0 +1,51 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewExtensionDialog : DialogForm + { + public NewExtensionDialog(IServiceProvider serviceProvider, bool allowed) + : base(serviceProvider) + { + InitializeComponent(); + Text = string.Format("{0} File Name Extension", allowed ? "Allow" : "Deny"); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item = new FileExtensionsItem(null) { Extension = txtName.Text }; + DialogResult = DialogResult.OK; + })); + } + + public FileExtensionsItem Item { get; set; } + + private void NewHiddenSegmentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Allow_Extension"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/NewExtensionDialog.resx b/JexusManager.Features.RequestFiltering/NewExtensionDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewExtensionDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/NewHeaderDialog.Designer.cs b/JexusManager.Features.RequestFiltering/NewHeaderDialog.Designer.cs new file mode 100644 index 00000000..e989d092 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewHeaderDialog.Designer.cs @@ -0,0 +1,124 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal sealed partial class NewHeaderDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblName = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtLimit = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // lblName + // + this.lblName.AutoSize = true; + this.lblName.Location = new System.Drawing.Point(12, 9); + this.lblName.Name = "lblName"; + this.lblName.Size = new System.Drawing.Size(45, 13); + this.lblName.TabIndex = 7; + this.lblName.Text = "Header:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 6; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(141, 131); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(242, 131); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 65); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(50, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Size limit:"; + // + // txtLimit + // + this.txtLimit.Location = new System.Drawing.Point(16, 81); + this.txtLimit.Name = "txtLimit"; + this.txtLimit.Size = new System.Drawing.Size(320, 20); + this.txtLimit.TabIndex = 8; + // + // NewHeaderDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 166); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtLimit); + this.Controls.Add(this.lblName); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewHeaderDialog"; + this.Text = "Allow Query String"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHiddenSegmentDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label lblName; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + private Label label1; + private TextBox txtLimit; + } +} diff --git a/JexusManager.Features.RequestFiltering/NewHeaderDialog.cs b/JexusManager.Features.RequestFiltering/NewHeaderDialog.cs new file mode 100644 index 00000000..6d49acb2 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewHeaderDialog.cs @@ -0,0 +1,52 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewHeaderDialog : DialogForm + { + public NewHeaderDialog(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(txtLimit, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && !string.IsNullOrWhiteSpace(txtLimit.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item = new HeadersItem(null) { Header = txtName.Text, SizeLimit = uint.Parse(txtLimit.Text) }; + DialogResult = DialogResult.OK; + })); + } + + public HeadersItem Item { get; set; } + + private void NewHiddenSegmentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Allow_Header"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/NewHeaderDialog.resx b/JexusManager.Features.RequestFiltering/NewHeaderDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewHeaderDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.Designer.cs b/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.Designer.cs new file mode 100644 index 00000000..b8d66ba0 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.Designer.cs @@ -0,0 +1,102 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class NewHiddenSegmentDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(87, 13); + this.label1.TabIndex = 7; + this.label1.Text = "Hidden segment:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 6; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(139, 87); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(240, 87); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // NewHiddenSegmentDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 126); + this.Controls.Add(this.label1); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewHiddenSegmentDialog"; + this.Text = "Add Hidden Segment"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHiddenSegmentDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.cs b/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.cs new file mode 100644 index 00000000..f5bd8c34 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.cs @@ -0,0 +1,51 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class NewHiddenSegmentDialog : DialogForm + { + public NewHiddenSegmentDialog(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item = new HiddenSegmentsItem(null); + Item.Segment = txtName.Text; + DialogResult = DialogResult.OK; + })); + } + + public HiddenSegmentsItem Item { get; set; } + + private void NewHiddenSegmentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Add_Hidden"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.resx b/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewHiddenSegmentDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/NewQueryDialog.Designer.cs b/JexusManager.Features.RequestFiltering/NewQueryDialog.Designer.cs new file mode 100644 index 00000000..8d888185 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewQueryDialog.Designer.cs @@ -0,0 +1,102 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal sealed partial class NewQueryDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblName = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lblName + // + this.lblName.AutoSize = true; + this.lblName.Location = new System.Drawing.Point(12, 9); + this.lblName.Name = "lblName"; + this.lblName.Size = new System.Drawing.Size(66, 13); + this.lblName.TabIndex = 7; + this.lblName.Text = "Query string:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 6; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(139, 87); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(240, 87); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // NewQueryDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 126); + this.Controls.Add(this.lblName); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewQueryDialog"; + this.Text = "Allow Query String"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHiddenSegmentDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label lblName; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.RequestFiltering/NewQueryDialog.cs b/JexusManager.Features.RequestFiltering/NewQueryDialog.cs new file mode 100644 index 00000000..4946798c --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewQueryDialog.cs @@ -0,0 +1,51 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewQueryDialog : DialogForm + { + public NewQueryDialog(IServiceProvider serviceProvider, bool allowed) + : base(serviceProvider) + { + InitializeComponent(); + Text = allowed ? "Allow Query String" : "Deny Query String"; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item = new QueryStringsItem(null, allowed) { QueryString = txtName.Text }; + DialogResult = DialogResult.OK; + })); + } + + public QueryStringsItem Item { get; set; } + + private void NewHiddenSegmentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Allow_Query"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/NewQueryDialog.resx b/JexusManager.Features.RequestFiltering/NewQueryDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewQueryDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/NewRuleDialog.Designer.cs b/JexusManager.Features.RequestFiltering/NewRuleDialog.Designer.cs new file mode 100644 index 00000000..0c81a162 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewRuleDialog.Designer.cs @@ -0,0 +1,239 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal sealed partial class NewRuleDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblName = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.cbUrl = new System.Windows.Forms.CheckBox(); + this.cbQuery = new System.Windows.Forms.CheckBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.dgvHeaders = new System.Windows.Forms.DataGridView(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.dgvExtensions = new System.Windows.Forms.DataGridView(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.dgvStrings = new System.Windows.Forms.DataGridView(); + this.chString = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.chExtension = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.chHeader = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvHeaders)).BeginInit(); + this.groupBox2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvExtensions)).BeginInit(); + this.groupBox3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvStrings)).BeginInit(); + this.SuspendLayout(); + // + // lblName + // + this.lblName.AutoSize = true; + this.lblName.Location = new System.Drawing.Point(12, 9); + this.lblName.Name = "lblName"; + this.lblName.Size = new System.Drawing.Size(38, 13); + this.lblName.TabIndex = 7; + this.lblName.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 6; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(251, 591); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(352, 591); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // cbUrl + // + this.cbUrl.AutoSize = true; + this.cbUrl.Location = new System.Drawing.Point(15, 62); + this.cbUrl.Name = "cbUrl"; + this.cbUrl.Size = new System.Drawing.Size(65, 17); + this.cbUrl.TabIndex = 8; + this.cbUrl.Text = "Scan url"; + this.cbUrl.UseVisualStyleBackColor = true; + // + // cbQuery + // + this.cbQuery.AutoSize = true; + this.cbQuery.Location = new System.Drawing.Point(15, 97); + this.cbQuery.Name = "cbQuery"; + this.cbQuery.Size = new System.Drawing.Size(108, 17); + this.cbQuery.TabIndex = 9; + this.cbQuery.Text = "Scan query string"; + this.cbQuery.UseVisualStyleBackColor = true; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.dgvHeaders); + this.groupBox1.Location = new System.Drawing.Point(15, 120); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(432, 130); + this.groupBox1.TabIndex = 10; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Scan Headers"; + // + // dgvHeaders + // + this.dgvHeaders.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgvHeaders.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.chHeader}); + this.dgvHeaders.Location = new System.Drawing.Point(6, 19); + this.dgvHeaders.Name = "dgvHeaders"; + this.dgvHeaders.Size = new System.Drawing.Size(420, 105); + this.dgvHeaders.TabIndex = 0; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.dgvExtensions); + this.groupBox2.Location = new System.Drawing.Point(15, 266); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(432, 130); + this.groupBox2.TabIndex = 11; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Applied To"; + // + // dgvExtensions + // + this.dgvExtensions.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgvExtensions.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.chExtension}); + this.dgvExtensions.Location = new System.Drawing.Point(6, 19); + this.dgvExtensions.Name = "dgvExtensions"; + this.dgvExtensions.Size = new System.Drawing.Size(420, 105); + this.dgvExtensions.TabIndex = 0; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.dgvStrings); + this.groupBox3.Location = new System.Drawing.Point(15, 418); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(432, 130); + this.groupBox3.TabIndex = 11; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Deny Strings"; + // + // dgvStrings + // + this.dgvStrings.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgvStrings.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.chString}); + this.dgvStrings.Location = new System.Drawing.Point(6, 19); + this.dgvStrings.Name = "dgvStrings"; + this.dgvStrings.Size = new System.Drawing.Size(420, 105); + this.dgvStrings.TabIndex = 0; + // + // chString + // + this.chString.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + this.chString.HeaderText = "String"; + this.chString.Name = "chString"; + // + // chExtension + // + this.chExtension.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + this.chExtension.HeaderText = "File Extension"; + this.chExtension.Name = "chExtension"; + // + // chHeader + // + this.chHeader.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + this.chHeader.HeaderText = "Header"; + this.chHeader.Name = "chHeader"; + // + // NewRuleDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(459, 626); + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.cbQuery); + this.Controls.Add(this.cbUrl); + this.Controls.Add(this.lblName); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewRuleDialog"; + this.Text = "Add Filtering Rule"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHiddenSegmentDialogHelpButtonClicked); + this.groupBox1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgvHeaders)).EndInit(); + this.groupBox2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgvExtensions)).EndInit(); + this.groupBox3.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgvStrings)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label lblName; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + private CheckBox cbUrl; + private CheckBox cbQuery; + private GroupBox groupBox1; + private DataGridView dgvHeaders; + private DataGridViewTextBoxColumn chHeader; + private GroupBox groupBox2; + private DataGridView dgvExtensions; + private DataGridViewTextBoxColumn chExtension; + private GroupBox groupBox3; + private DataGridView dgvStrings; + private DataGridViewTextBoxColumn chString; + } +} diff --git a/JexusManager.Features.RequestFiltering/NewRuleDialog.cs b/JexusManager.Features.RequestFiltering/NewRuleDialog.cs new file mode 100644 index 00000000..e6ef6db1 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewRuleDialog.cs @@ -0,0 +1,105 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewRuleDialog : DialogForm + { + public NewRuleDialog(IServiceProvider serviceProvider, FilteringRulesItem existing) + : base(serviceProvider) + { + InitializeComponent(); + Text = existing == null ? "Add Filtering Rule" : "Edit Filtering Rule"; + txtName.ReadOnly = existing != null; + if (existing != null) + { + txtName.Text = existing.Name; + dgvHeaders.DataSource = existing.Headers; + dgvExtensions.DataSource = existing.Extensions; + dgvStrings.DataSource = existing.DenyStrings; + cbUrl.Checked = existing.ScanUrl; + cbQuery.Checked = existing.ScanQueryString; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item = new FilteringRulesItem(null) { Name = txtName.Text }; + Item.ScanQueryString = cbQuery.Checked; + Item.ScanUrl = cbUrl.Checked; + Item.Headers.Clear(); + foreach (DataGridViewRow row in dgvHeaders.Rows) + { + if (row.IsNewRow) + { + continue; + } + + var header = row.Cells[0].Value.ToString(); + Item.Headers.Add(new ScanHeadersItem(null) { RequestHeader = header }); + } + + Item.Extensions.Clear(); + foreach (DataGridViewRow row in dgvExtensions.Rows) + { + if (row.IsNewRow) + { + continue; + } + + var header = row.Cells[0].Value.ToString(); + Item.Extensions.Add(new AppliesToItem(null) { FileExtension = header }); + } + + Item.DenyStrings.Clear(); + foreach (DataGridViewRow row in dgvStrings.Rows) + { + if (row.IsNewRow) + { + continue; + } + + var header = row.Cells[0].Value.ToString(); + Item.DenyStrings.Add(new DenyStringsItem(null) { DenyString = header }); + } + + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Merge(Observable.FromEventPattern(dgvHeaders, "RowsAdded")) + .Merge(Observable.FromEventPattern(dgvExtensions, "RowsAdded")) + .Merge(Observable.FromEventPattern(dgvStrings, "RowsAdded")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) + && dgvHeaders.RowCount > 1 + && dgvExtensions.RowCount > 1 + && dgvStrings.RowCount > 1; + })); + } + + public FilteringRulesItem Item { get; set; } + + private void NewHiddenSegmentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Add_Filtering"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/NewRuleDialog.resx b/JexusManager.Features.RequestFiltering/NewRuleDialog.resx new file mode 100644 index 00000000..e956b17d --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewRuleDialog.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/NewUrlDialog.Designer.cs b/JexusManager.Features.RequestFiltering/NewUrlDialog.Designer.cs new file mode 100644 index 00000000..1c158ab0 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewUrlDialog.Designer.cs @@ -0,0 +1,102 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal sealed partial class NewUrlDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblName = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lblName + // + this.lblName.AutoSize = true; + this.lblName.Location = new System.Drawing.Point(12, 9); + this.lblName.Name = "lblName"; + this.lblName.Size = new System.Drawing.Size(32, 13); + this.lblName.TabIndex = 7; + this.lblName.Text = "URL:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 6; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(139, 87); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(240, 87); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // NewUrlDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 126); + this.Controls.Add(this.lblName); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewUrlDialog"; + this.Text = "Allow URL"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHiddenSegmentDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label lblName; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.RequestFiltering/NewUrlDialog.cs b/JexusManager.Features.RequestFiltering/NewUrlDialog.cs new file mode 100644 index 00000000..e5e98931 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewUrlDialog.cs @@ -0,0 +1,52 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewUrlDialog : DialogForm + { + public NewUrlDialog(IServiceProvider serviceProvider, bool allowed) + : base(serviceProvider) + { + InitializeComponent(); + Text = allowed ? "Allow URL" : "Deny Sequence"; + lblName.Text = allowed ? "URL:" : "URL sequence"; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item = new UrlsItem(null, allowed) { Url = txtName.Text }; + DialogResult = DialogResult.OK; + })); + } + + public UrlsItem Item { get; set; } + + private void NewHiddenSegmentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Add_Sequence"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/NewUrlDialog.resx b/JexusManager.Features.RequestFiltering/NewUrlDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewUrlDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/NewVerbDialog.Designer.cs b/JexusManager.Features.RequestFiltering/NewVerbDialog.Designer.cs new file mode 100644 index 00000000..480324fa --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewVerbDialog.Designer.cs @@ -0,0 +1,102 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal sealed partial class NewVerbDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lblName = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lblName + // + this.lblName.AutoSize = true; + this.lblName.Location = new System.Drawing.Point(12, 9); + this.lblName.Name = "lblName"; + this.lblName.Size = new System.Drawing.Size(66, 13); + this.lblName.TabIndex = 7; + this.lblName.Text = "Query string:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(320, 20); + this.txtName.TabIndex = 6; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(139, 87); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(95, 23); + this.btnOK.TabIndex = 5; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(240, 87); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 4; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // NewVerbDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 126); + this.Controls.Add(this.lblName); + this.Controls.Add(this.txtName); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewVerbDialog"; + this.Text = "Allow Query String"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHiddenSegmentDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label lblName; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.RequestFiltering/NewVerbDialog.cs b/JexusManager.Features.RequestFiltering/NewVerbDialog.cs new file mode 100644 index 00000000..c140ed39 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewVerbDialog.cs @@ -0,0 +1,51 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewVerbDialog : DialogForm + { + public NewVerbDialog(IServiceProvider serviceProvider, bool allowed) + : base(serviceProvider) + { + InitializeComponent(); + Text = allowed ? "Allow Verb" : "Deny Verb"; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item = new VerbsItem(null) { Verb = txtName.Text, Allowed = allowed }; + DialogResult = DialogResult.OK; + })); + } + + public VerbsItem Item { get; set; } + + private void NewHiddenSegmentDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Allow_Verb"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/NewVerbDialog.resx b/JexusManager.Features.RequestFiltering/NewVerbDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/NewVerbDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/Properties/AssemblyInfo.cs b/JexusManager.Features.RequestFiltering/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d9e223b7 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.RequestFiltering")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.RequestFiltering")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("56d44318-5979-4fc5-9f01-4d7f78709a0e")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.RequestFiltering/Properties/Resources.Designer.cs b/JexusManager.Features.RequestFiltering/Properties/Resources.Designer.cs new file mode 100644 index 00000000..437c750a --- /dev/null +++ b/JexusManager.Features.RequestFiltering/Properties/Resources.Designer.cs @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.RequestFiltering.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.RequestFiltering.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap extensions_16 { + get { + object obj = ResourceManager.GetObject("extensions_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap headers_16 { + get { + object obj = ResourceManager.GetObject("headers_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap hidden_16 { + get { + object obj = ResourceManager.GetObject("hidden_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap map_16 { + get { + object obj = ResourceManager.GetObject("map_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap query_16 { + get { + object obj = ResourceManager.GetObject("query_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap request_filtering_36 { + get { + object obj = ResourceManager.GetObject("request_filtering_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap url_16 { + get { + object obj = ResourceManager.GetObject("url_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap verbs_16 { + get { + object obj = ResourceManager.GetObject("verbs_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/Properties/Resources.resx b/JexusManager.Features.RequestFiltering/Properties/Resources.resx new file mode 100644 index 00000000..be887598 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/Properties/Resources.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\extensions_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\headers_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\hidden_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\map_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\query_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\request_filtering_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\url_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\verbs_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/QueryStringsFeature.cs b/JexusManager.Features.RequestFiltering/QueryStringsFeature.cs new file mode 100644 index 00000000..c907469d --- /dev/null +++ b/JexusManager.Features.RequestFiltering/QueryStringsFeature.cs @@ -0,0 +1,146 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class QueryStringsFeature : RequestFilteringFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly QueryStringsFeature _owner; + + public FeatureTaskList(QueryStringsFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("AddQuery", "Allow Query String...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("AddDenyQuery", "Deny Query String...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddQuery() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public void AddDenyQuery() + { + _owner.AddDeny(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + } + + public QueryStringsFeature(Module module) + : base(module) + { + } + + private TaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Add() + { + var dialog = new NewQueryDialog(this.Module, true); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddDeny() + { + var dialog = new NewQueryDialog(this.Module, false); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected query string?", this.Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + var section = service.GetSection("system.webServer/security/requestFiltering"); + ConfigurationElement hiddenSegmentsElement = section.ChildElements["alwaysAllowedQueryStrings"]; + return hiddenSegmentsElement.GetCollection(); + } + + protected override ConfigurationElementCollection GetSecondaryCollection(IConfigurationService service) + { + var section = service.GetSection("system.webServer/security/requestFiltering"); + ConfigurationElement hiddenSegmentsElement = section.ChildElements["denyQueryStringSequences"]; + return hiddenSegmentsElement.GetCollection(); + } + + public override void Load() + { + LoadItems(); + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Query_Strings"); + return true; + } + + public override string Name + { + get + { + return "Query Strings"; + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/QueryStringsItem.cs b/JexusManager.Features.RequestFiltering/QueryStringsItem.cs new file mode 100644 index 00000000..c4e417a1 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/QueryStringsItem.cs @@ -0,0 +1,60 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class QueryStringsItem : IDuoItem + { + public bool Allowed { get; } + + public ConfigurationElement Element { get; set; } + + public bool Match(QueryStringsItem other) + { + return other != null && other.QueryString == QueryString; + } + + public QueryStringsItem(ConfigurationElement element, bool allowed) + { + this.Allowed = allowed; + this.Element = element; + if (element == null) + { + return; + } + + if (allowed) + { + QueryString = (string)element["queryString"]; + } + else + { + QueryString = (string)element["sequence"]; + } + } + + public string QueryString { get; set; } + + public void Apply() + { + if (Allowed) + { + Element["queryString"] = QueryString; + } + else + { + Element["sequence"] = QueryString; + } + } + + public string Flag { get; set; } + + public bool Equals(QueryStringsItem other) + { + return Match(other) && other.Allowed == Allowed; + } + } +} diff --git a/JexusManager.Features.RequestFiltering/RequestFilteringFeature.cs b/JexusManager.Features.RequestFiltering/RequestFilteringFeature.cs new file mode 100644 index 00000000..f894dbc8 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/RequestFilteringFeature.cs @@ -0,0 +1,57 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.RequestFiltering +{ + using System; + + using Microsoft.Web.Management.Client; + + /// + /// Description of DefaultDocumentFeature. + /// + internal abstract class RequestFilteringFeature : FeatureBase, IRequestFilteringFeature + where T : class, IItem + { + protected RequestFilteringFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + + public abstract TaskList GetTaskList(); + + public abstract void Load(); + + protected override void OnSettingsSaved() + { + this.RequestFilteringSettingsUpdate?.Invoke(); + } + + public abstract bool ShowHelp(); + + public RequestFilteringSettingsSavedEventHandler RequestFilteringSettingsUpdate { get; set; } + + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public abstract string Name { get; } + } +} diff --git a/JexusManager.Features.RequestFiltering/RequestFilteringModule.cs b/JexusManager.Features.RequestFiltering/RequestFilteringModule.cs new file mode 100644 index 00000000..2723b07a --- /dev/null +++ b/JexusManager.Features.RequestFiltering/RequestFilteringModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class RequestFilteringModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(RequestFilteringPage), "Request Filtering", + "Use this feature to configure filtering rules", Resources.request_filtering_36, + Resources.request_filtering_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/RequestFilteringModuleProvider.cs b/JexusManager.Features.RequestFiltering/RequestFilteringModuleProvider.cs new file mode 100644 index 00000000..8d832ac6 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/RequestFilteringModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + + using Microsoft.Web.Management.Server; + + public class RequestFilteringModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(RequestFilteringModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.RequestFiltering/RequestFilteringPage.Designer.cs b/JexusManager.Features.RequestFiltering/RequestFilteringPage.Designer.cs new file mode 100644 index 00000000..4b97cd08 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/RequestFilteringPage.Designer.cs @@ -0,0 +1,581 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class RequestFilteringPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tpExtensions = new System.Windows.Forms.TabPage(); + this.lvExtensions = new System.Windows.Forms.ListView(); + this.chExtension = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chAllowedExtension = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpRules = new System.Windows.Forms.TabPage(); + this.lvRules = new System.Windows.Forms.ListView(); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader10 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader11 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader12 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpSegments = new System.Windows.Forms.TabPage(); + this.lvSegments = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpUrl = new System.Windows.Forms.TabPage(); + this.lvUrls = new System.Windows.Forms.ListView(); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader9 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpVerbs = new System.Windows.Forms.TabPage(); + this.lvVerbs = new System.Windows.Forms.ListView(); + this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader8 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpHeaders = new System.Windows.Forms.TabPage(); + this.lvHeaders = new System.Windows.Forms.ListView(); + this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.tpQuery = new System.Windows.Forms.TabPage(); + this.lvQueries = new System.Windows.Forms.ListView(); + this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.imageList1 = new System.Windows.Forms.ImageList(this.components); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tpExtensions.SuspendLayout(); + this.tpRules.SuspendLayout(); + this.tpSegments.SuspendLayout(); + this.tpUrl.SuspendLayout(); + this.tpVerbs.SuspendLayout(); + this.tpHeaders.SuspendLayout(); + this.tpQuery.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 4; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.tabControl1); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tpExtensions); + this.tabControl1.Controls.Add(this.tpRules); + this.tabControl1.Controls.Add(this.tpSegments); + this.tabControl1.Controls.Add(this.tpUrl); + this.tabControl1.Controls.Add(this.tpVerbs); + this.tabControl1.Controls.Add(this.tpHeaders); + this.tabControl1.Controls.Add(this.tpQuery); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.ImageList = this.imageList1; + this.tabControl1.Location = new System.Drawing.Point(0, 13); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(560, 527); + this.tabControl1.TabIndex = 2; + this.tabControl1.SelectedIndexChanged += new System.EventHandler(this.TabControl1SelectedIndexChanged); + // + // tpExtensions + // + this.tpExtensions.Controls.Add(this.lvExtensions); + this.tpExtensions.Location = new System.Drawing.Point(4, 23); + this.tpExtensions.Name = "tpExtensions"; + this.tpExtensions.Size = new System.Drawing.Size(552, 500); + this.tpExtensions.TabIndex = 2; + this.tpExtensions.Text = "File Name Extensions"; + this.tpExtensions.UseVisualStyleBackColor = true; + // + // lvExtensions + // + this.lvExtensions.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chExtension, + this.chAllowedExtension}); + this.lvExtensions.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvExtensions.FullRowSelect = true; + this.lvExtensions.HideSelection = false; + this.lvExtensions.Location = new System.Drawing.Point(0, 0); + this.lvExtensions.Margin = new System.Windows.Forms.Padding(5); + this.lvExtensions.MultiSelect = false; + this.lvExtensions.Name = "lvExtensions"; + this.lvExtensions.Size = new System.Drawing.Size(552, 500); + this.lvExtensions.TabIndex = 1; + this.lvExtensions.UseCompatibleStateImageBehavior = false; + this.lvExtensions.View = System.Windows.Forms.View.Details; + this.lvExtensions.SelectedIndexChanged += new System.EventHandler(this.LvExtensionsSelectedIndexChanged); + // + // chExtension + // + this.chExtension.Text = "Extension"; + this.chExtension.Width = 105; + // + // chAllowedExtension + // + this.chAllowedExtension.Text = "Allowed"; + // + // tpRules + // + this.tpRules.Controls.Add(this.lvRules); + this.tpRules.Location = new System.Drawing.Point(4, 23); + this.tpRules.Name = "tpRules"; + this.tpRules.Size = new System.Drawing.Size(552, 500); + this.tpRules.TabIndex = 3; + this.tpRules.Text = "Rules"; + this.tpRules.UseVisualStyleBackColor = true; + // + // lvRules + // + this.lvRules.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader2, + this.columnHeader10, + this.columnHeader11, + this.columnHeader12}); + this.lvRules.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvRules.FullRowSelect = true; + this.lvRules.HideSelection = false; + this.lvRules.Location = new System.Drawing.Point(0, 0); + this.lvRules.Margin = new System.Windows.Forms.Padding(5); + this.lvRules.MultiSelect = false; + this.lvRules.Name = "lvRules"; + this.lvRules.Size = new System.Drawing.Size(552, 500); + this.lvRules.TabIndex = 1; + this.lvRules.UseCompatibleStateImageBehavior = false; + this.lvRules.View = System.Windows.Forms.View.Details; + this.lvRules.SelectedIndexChanged += new System.EventHandler(this.LvRuleSelectedIndexChanged); + // + // columnHeader2 + // + this.columnHeader2.Text = "Name"; + this.columnHeader2.Width = 105; + // + // columnHeader10 + // + this.columnHeader10.Text = "Scan"; + this.columnHeader10.Width = 105; + // + // columnHeader11 + // + this.columnHeader11.Text = "Applied To"; + this.columnHeader11.Width = 105; + // + // columnHeader12 + // + this.columnHeader12.Text = "Deny Strings"; + this.columnHeader12.Width = 105; + // + // tpSegments + // + this.tpSegments.Controls.Add(this.lvSegments); + this.tpSegments.Location = new System.Drawing.Point(4, 23); + this.tpSegments.Name = "tpSegments"; + this.tpSegments.Padding = new System.Windows.Forms.Padding(3); + this.tpSegments.Size = new System.Drawing.Size(552, 500); + this.tpSegments.TabIndex = 0; + this.tpSegments.Text = "Hidden Segments"; + this.tpSegments.UseVisualStyleBackColor = true; + // + // lvSegments + // + this.lvSegments.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName}); + this.lvSegments.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvSegments.FullRowSelect = true; + this.lvSegments.HideSelection = false; + this.lvSegments.Location = new System.Drawing.Point(3, 3); + this.lvSegments.Margin = new System.Windows.Forms.Padding(5); + this.lvSegments.MultiSelect = false; + this.lvSegments.Name = "lvSegments"; + this.lvSegments.Size = new System.Drawing.Size(546, 494); + this.lvSegments.TabIndex = 0; + this.lvSegments.UseCompatibleStateImageBehavior = false; + this.lvSegments.View = System.Windows.Forms.View.Details; + this.lvSegments.SelectedIndexChanged += new System.EventHandler(this.LvSegmentsSelectedIndexChanged); + // + // chName + // + this.chName.Text = "Segment"; + this.chName.Width = 200; + // + // tpUrl + // + this.tpUrl.Controls.Add(this.lvUrls); + this.tpUrl.Location = new System.Drawing.Point(4, 23); + this.tpUrl.Name = "tpUrl"; + this.tpUrl.Padding = new System.Windows.Forms.Padding(3); + this.tpUrl.Size = new System.Drawing.Size(552, 500); + this.tpUrl.TabIndex = 1; + this.tpUrl.Text = "URL"; + this.tpUrl.UseVisualStyleBackColor = true; + // + // lvUrls + // + this.lvUrls.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader3, + this.columnHeader9}); + this.lvUrls.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvUrls.FullRowSelect = true; + this.lvUrls.HideSelection = false; + this.lvUrls.Location = new System.Drawing.Point(3, 3); + this.lvUrls.Margin = new System.Windows.Forms.Padding(5); + this.lvUrls.MultiSelect = false; + this.lvUrls.Name = "lvUrls"; + this.lvUrls.Size = new System.Drawing.Size(546, 494); + this.lvUrls.TabIndex = 1; + this.lvUrls.UseCompatibleStateImageBehavior = false; + this.lvUrls.View = System.Windows.Forms.View.Details; + this.lvUrls.SelectedIndexChanged += new System.EventHandler(this.LvUrlsSelectedIndexChanged); + // + // columnHeader3 + // + this.columnHeader3.Text = "Url"; + this.columnHeader3.Width = 145; + // + // columnHeader9 + // + this.columnHeader9.Text = "Action"; + this.columnHeader9.Width = 145; + // + // tpVerbs + // + this.tpVerbs.Controls.Add(this.lvVerbs); + this.tpVerbs.Location = new System.Drawing.Point(4, 23); + this.tpVerbs.Name = "tpVerbs"; + this.tpVerbs.Size = new System.Drawing.Size(552, 500); + this.tpVerbs.TabIndex = 4; + this.tpVerbs.Text = "HTTP Verbs"; + this.tpVerbs.UseVisualStyleBackColor = true; + // + // lvVerbs + // + this.lvVerbs.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader4, + this.columnHeader8}); + this.lvVerbs.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvVerbs.FullRowSelect = true; + this.lvVerbs.HideSelection = false; + this.lvVerbs.Location = new System.Drawing.Point(0, 0); + this.lvVerbs.Margin = new System.Windows.Forms.Padding(5); + this.lvVerbs.MultiSelect = false; + this.lvVerbs.Name = "lvVerbs"; + this.lvVerbs.Size = new System.Drawing.Size(552, 500); + this.lvVerbs.TabIndex = 1; + this.lvVerbs.UseCompatibleStateImageBehavior = false; + this.lvVerbs.View = System.Windows.Forms.View.Details; + this.lvVerbs.SelectedIndexChanged += new System.EventHandler(this.LvVerbsSelectedIndexChanged); + // + // columnHeader4 + // + this.columnHeader4.Text = "Verb"; + this.columnHeader4.Width = 175; + // + // columnHeader8 + // + this.columnHeader8.Text = "Allowed"; + // + // tpHeaders + // + this.tpHeaders.Controls.Add(this.lvHeaders); + this.tpHeaders.Location = new System.Drawing.Point(4, 23); + this.tpHeaders.Name = "tpHeaders"; + this.tpHeaders.Size = new System.Drawing.Size(552, 500); + this.tpHeaders.TabIndex = 5; + this.tpHeaders.Text = "Headers"; + this.tpHeaders.UseVisualStyleBackColor = true; + // + // lvHeaders + // + this.lvHeaders.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader5, + this.columnHeader7}); + this.lvHeaders.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvHeaders.FullRowSelect = true; + this.lvHeaders.HideSelection = false; + this.lvHeaders.Location = new System.Drawing.Point(0, 0); + this.lvHeaders.Margin = new System.Windows.Forms.Padding(5); + this.lvHeaders.MultiSelect = false; + this.lvHeaders.Name = "lvHeaders"; + this.lvHeaders.Size = new System.Drawing.Size(552, 500); + this.lvHeaders.TabIndex = 1; + this.lvHeaders.UseCompatibleStateImageBehavior = false; + this.lvHeaders.View = System.Windows.Forms.View.Details; + this.lvHeaders.SelectedIndexChanged += new System.EventHandler(this.LvHeadersSelectedIndexChanged); + // + // columnHeader5 + // + this.columnHeader5.Text = "Header"; + this.columnHeader5.Width = 175; + // + // columnHeader7 + // + this.columnHeader7.Text = "Size Limit"; + // + // tpQuery + // + this.tpQuery.Controls.Add(this.lvQueries); + this.tpQuery.Location = new System.Drawing.Point(4, 23); + this.tpQuery.Name = "tpQuery"; + this.tpQuery.Size = new System.Drawing.Size(552, 500); + this.tpQuery.TabIndex = 6; + this.tpQuery.Text = "Query Strings"; + this.tpQuery.UseVisualStyleBackColor = true; + // + // lvQueries + // + this.lvQueries.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader6, + this.columnHeader1}); + this.lvQueries.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvQueries.FullRowSelect = true; + this.lvQueries.HideSelection = false; + this.lvQueries.Location = new System.Drawing.Point(0, 0); + this.lvQueries.Margin = new System.Windows.Forms.Padding(5); + this.lvQueries.MultiSelect = false; + this.lvQueries.Name = "lvQueries"; + this.lvQueries.Size = new System.Drawing.Size(552, 500); + this.lvQueries.TabIndex = 1; + this.lvQueries.UseCompatibleStateImageBehavior = false; + this.lvQueries.View = System.Windows.Forms.View.Details; + this.lvQueries.SelectedIndexChanged += new System.EventHandler(this.LvQueriesSelectedIndexChanged); + // + // columnHeader6 + // + this.columnHeader6.Text = "Query String"; + this.columnHeader6.Width = 145; + // + // columnHeader1 + // + this.columnHeader1.Text = "Action"; + this.columnHeader1.Width = 145; + // + // imageList1 + // + this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; + this.imageList1.ImageSize = new System.Drawing.Size(16, 16); + this.imageList1.TransparentColor = System.Drawing.Color.Transparent; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(204, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure filtering rules."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(175, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Request Filtering"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // RequestFilteringPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "RequestFilteringPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.tabControl1.ResumeLayout(false); + this.tpExtensions.ResumeLayout(false); + this.tpRules.ResumeLayout(false); + this.tpSegments.ResumeLayout(false); + this.tpUrl.ResumeLayout(false); + this.tpVerbs.ResumeLayout(false); + this.tpHeaders.ResumeLayout(false); + this.tpQuery.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView lvSegments; + private ColumnHeader chName; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private Panel panel2; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private TabControl tabControl1; + private TabPage tpExtensions; + private TabPage tpRules; + private TabPage tpSegments; + private TabPage tpUrl; + private TabPage tpVerbs; + private TabPage tpHeaders; + private TabPage tpQuery; + private ImageList imageList1; + private ListView lvExtensions; + private ColumnHeader chExtension; + private ListView lvRules; + private ColumnHeader columnHeader2; + private ListView lvUrls; + private ColumnHeader columnHeader3; + private ListView lvVerbs; + private ColumnHeader columnHeader4; + private ListView lvHeaders; + private ColumnHeader columnHeader5; + private ListView lvQueries; + private ColumnHeader columnHeader6; + private ColumnHeader chAllowedExtension; + private ColumnHeader columnHeader10; + private ColumnHeader columnHeader11; + private ColumnHeader columnHeader12; + private ColumnHeader columnHeader9; + private ColumnHeader columnHeader8; + private ColumnHeader columnHeader7; + private ColumnHeader columnHeader1; + } +} diff --git a/JexusManager.Features.RequestFiltering/RequestFilteringPage.cs b/JexusManager.Features.RequestFiltering/RequestFilteringPage.cs new file mode 100644 index 00000000..471de78d --- /dev/null +++ b/JexusManager.Features.RequestFiltering/RequestFilteringPage.cs @@ -0,0 +1,541 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class RequestFilteringPage : ModuleListPage + { + private sealed class PageTaskList : DefaultTaskList + { + private readonly RequestFilteringPage _owner; + + public PageTaskList(RequestFilteringPage owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Set", "Edit Feature Settings...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(HelpTaskItem); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + + [Obfuscation(Exclude = true)] + public void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class ExtensionListViewItem : ListViewItem + { + public FileExtensionsItem Item { get; } + private readonly RequestFilteringPage _page; + + public ExtensionListViewItem(FileExtensionsItem item, RequestFilteringPage page) + : base(item.Extension) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Allowed ? "True" : "False")); + } + } + + private sealed class RuleListViewItem : ListViewItem + { + public FilteringRulesItem Item { get; } + private readonly RequestFilteringPage _page; + + public RuleListViewItem(FilteringRulesItem item, RequestFilteringPage page) + : base(item.Name) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.ScanString)); + SubItems.Add(new ListViewSubItem(this, item.AppliesToString)); + SubItems.Add(new ListViewSubItem(this, item.DenyStringsString)); + } + } + + private sealed class SegmentListViewItem : ListViewItem + { + public HiddenSegmentsItem Item { get; } + private readonly RequestFilteringPage _page; + + public SegmentListViewItem(HiddenSegmentsItem item, RequestFilteringPage page) + : base(item.Segment) + { + Item = item; + _page = page; + } + } + + private sealed class UrlListViewItem : ListViewItem + { + public UrlsItem Item { get; } + private readonly RequestFilteringPage _page; + + public UrlListViewItem(UrlsItem item, RequestFilteringPage page) + : base(item.Url) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Allowed ? "always allow" : "Deny")); + } + } + + private sealed class VerbListViewItem : ListViewItem + { + public VerbsItem Item { get; } + private readonly RequestFilteringPage _page; + + public VerbListViewItem(VerbsItem item, RequestFilteringPage page) + : base(item.Verb) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Allowed ? "True" : "False")); + } + } + + private sealed class HeaderListViewItem : ListViewItem + { + public HeadersItem Item { get; } + private readonly RequestFilteringPage _page; + + public HeaderListViewItem(HeadersItem item, RequestFilteringPage page) + : base(item.Header) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.SizeLimit.ToString())); + } + } + + private sealed class QueryListViewItem : ListViewItem + { + public QueryStringsItem Item { get; } + private readonly RequestFilteringPage _page; + + public QueryListViewItem(QueryStringsItem item, RequestFilteringPage page) + : base(item.QueryString) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Allowed ? "Always allow" : "Deny")); + } + } + + private PageTaskList _taskList; + + public RequestFilteringPage() + { + InitializeComponent(); + imageList1.Images.Add(Resources.extensions_16); + imageList1.Images.Add(Resources.map_16); + imageList1.Images.Add(Resources.hidden_16); + imageList1.Images.Add(Resources.url_16); + imageList1.Images.Add(Resources.verbs_16); + imageList1.Images.Add(Resources.headers_16); + imageList1.Images.Add(Resources.query_16); + tpExtensions.ImageIndex = 0; + tpRules.ImageIndex = 1; + tpSegments.ImageIndex = 2; + tpUrl.ImageIndex = 3; + tpVerbs.ImageIndex = 4; + tpHeaders.ImageIndex = 5; + tpQuery.ImageIndex = 6; + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + var extensions = new FileExtensionsFeature(Module); + extensions.RequestFilteringSettingsUpdate = RefreshExtensions; + extensions.Load(); + tpExtensions.Tag = extensions; + RefreshExtensions(); + + var rules = new FilteringRulesFeature(Module); + rules.RequestFilteringSettingsUpdate = RefreshRules; + rules.Load(); + tpRules.Tag = rules; + + var segments = new HiddenSegmentsFeature(Module); + segments.RequestFilteringSettingsUpdate = RefreshSegments; + segments.Load(); + tpSegments.Tag = segments; + + var urls = new UrlsFeature(Module); + urls.RequestFilteringSettingsUpdate = RefreshUrls; + urls.Load(); + tpUrl.Tag = urls; + + var verbs = new VerbsFeature(Module); + verbs.RequestFilteringSettingsUpdate = RefreshVerbs; + verbs.Load(); + tpVerbs.Tag = verbs; + + var headers = new HeadersFeature(Module); + headers.RequestFilteringSettingsUpdate = RefreshHeaders; + headers.Load(); + tpHeaders.Tag = headers; + + var queries = new QueryStringsFeature(Module); + queries.RequestFilteringSettingsUpdate = RefreshQueries; + queries.Load(); + tpQuery.Tag = queries; + } + + protected override void InitializeListPage() + { + } + + private void RefreshExtensions() + { + var feature = (FileExtensionsFeature)tpExtensions.Tag; + if (feature == null) + { + return; + } + + lvExtensions.Items.Clear(); + foreach (var file in feature.Items) + { + lvExtensions.Items.Add(new ExtensionListViewItem(file, this)); + } + + if (feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (ExtensionListViewItem item in lvExtensions.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshRules() + { + var feature = (FilteringRulesFeature)tpRules.Tag; + if (feature == null) + { + return; + } + + lvRules.Items.Clear(); + foreach (var file in feature.Items) + { + lvRules.Items.Add(new RuleListViewItem(file, this)); + } + + if (feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (RuleListViewItem item in lvRules.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshSegments() + { + var feature = (HiddenSegmentsFeature)tpSegments.Tag; + if (feature == null) + { + return; + } + + lvSegments.Items.Clear(); + foreach (var file in feature.Items) + { + lvSegments.Items.Add(new SegmentListViewItem(file, this)); + } + + if (feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (SegmentListViewItem item in lvSegments.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshUrls() + { + var feature = (UrlsFeature)tpUrl.Tag; + if (feature == null) + { + return; + } + + lvUrls.Items.Clear(); + foreach (var url in feature.Items) + { + lvUrls.Items.Add(new UrlListViewItem(url, this)); + } + + if (feature.SelectedItem == null) + { + Refresh(); + return; + } + + foreach (UrlListViewItem item in lvUrls.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshVerbs() + { + var feature = (VerbsFeature)tpVerbs.Tag; + if (feature == null) + { + return; + } + + lvVerbs.Items.Clear(); + foreach (var url in feature.Items) + { + lvVerbs.Items.Add(new VerbListViewItem(url, this)); + } + + if (feature.SelectedItem == null) + { + Refresh(); + return; + } + + foreach (VerbListViewItem item in lvVerbs.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshHeaders() + { + var feature = (HeadersFeature)tpHeaders.Tag; + if (feature == null) + { + return; + } + + lvHeaders.Items.Clear(); + foreach (var url in feature.Items) + { + lvHeaders.Items.Add(new HeaderListViewItem(url, this)); + } + + if (feature.SelectedItem == null) + { + Refresh(); + return; + } + + foreach (HeaderListViewItem item in lvHeaders.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void RefreshQueries() + { + var feature = (QueryStringsFeature)tpQuery.Tag; + if (feature == null) + { + return; + } + + lvQueries.Items.Clear(); + foreach (var url in feature.Items) + { + lvQueries.Items.Add(new QueryListViewItem(url, this)); + } + + if (feature.SelectedItem == null) + { + Refresh(); + return; + } + + foreach (QueryListViewItem item in lvQueries.Items) + { + if (item.Item == feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + var feature = (IRequestFilteringFeature)tabControl1.SelectedTab.Tag; + TaskList extra = feature.GetTaskList(); + Tasks.Fill(tsActionPanel, cmsActionPanel, extra); + base.Refresh(); + } + + private void LvExtensionsSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (FileExtensionsFeature)tpExtensions.Tag; + feature.SelectedItem = lvExtensions.SelectedItems.Count > 0 + ? ((ExtensionListViewItem)lvExtensions.SelectedItems[0]).Item + : null; + Refresh(); + } + + private void LvRuleSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (FilteringRulesFeature)tpRules.Tag; + feature.SelectedItem = lvRules.SelectedItems.Count > 0 + ? ((RuleListViewItem)lvRules.SelectedItems[0]).Item + : null; + Refresh(); + } + + private void LvSegmentsSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (HiddenSegmentsFeature)tpSegments.Tag; + feature.SelectedItem = lvSegments.SelectedItems.Count > 0 + ? ((SegmentListViewItem)lvSegments.SelectedItems[0]).Item + : null; + Refresh(); + } + + private void LvUrlsSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (UrlsFeature)tpUrl.Tag; + feature.SelectedItem = lvUrls.SelectedItems.Count > 0 + ? ((UrlListViewItem)lvUrls.SelectedItems[0]).Item + : null; + Refresh(); + } + + private void LvVerbsSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (VerbsFeature)tpVerbs.Tag; + feature.SelectedItem = lvVerbs.SelectedItems.Count > 0 + ? ((VerbListViewItem)lvVerbs.SelectedItems[0]).Item + : null; + Refresh(); + } + + private void LvHeadersSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (HeadersFeature)tpHeaders.Tag; + feature.SelectedItem = lvHeaders.SelectedItems.Count > 0 + ? ((HeaderListViewItem)lvHeaders.SelectedItems[0]).Item + : null; + Refresh(); + } + + private void LvQueriesSelectedIndexChanged(object sender, EventArgs e) + { + var feature = (QueryStringsFeature)tpQuery.Tag; + feature.SelectedItem = lvQueries.SelectedItems.Count > 0 + ? ((QueryListViewItem)lvQueries.SelectedItems[0]).Item + : null; + Refresh(); + } + + protected override bool ShowHelp() + { + var feature = (IRequestFilteringFeature)tabControl1.SelectedTab.Tag; + feature.ShowHelp(); + return true; + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void TabControl1SelectedIndexChanged(object sender, EventArgs e) + { + var feature = (IRequestFilteringFeature)tabControl1.SelectedTab.Tag; + feature.RequestFilteringSettingsUpdate.Invoke(); + Refresh(); + } + + public void Set() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/security/requestFiltering"); + var dialog = new SegmentSettingsDialog(Module, section); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/RequestFilteringPage.resx b/JexusManager.Features.RequestFiltering/RequestFilteringPage.resx new file mode 100644 index 00000000..89b194c5 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/RequestFilteringPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 281, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/RequestFilteringSettingsSavedEventHandler.cs b/JexusManager.Features.RequestFiltering/RequestFilteringSettingsSavedEventHandler.cs new file mode 100644 index 00000000..88ead1bc --- /dev/null +++ b/JexusManager.Features.RequestFiltering/RequestFilteringSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + public delegate void RequestFilteringSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/Resources/extensions_16.png b/JexusManager.Features.RequestFiltering/Resources/extensions_16.png new file mode 100644 index 00000000..f4b0b19e Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/extensions_16.png differ diff --git a/JexusManager.Features.RequestFiltering/Resources/headers_16.png b/JexusManager.Features.RequestFiltering/Resources/headers_16.png new file mode 100644 index 00000000..1787a7ff Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/headers_16.png differ diff --git a/JexusManager.Features.RequestFiltering/Resources/hidden_16.png b/JexusManager.Features.RequestFiltering/Resources/hidden_16.png new file mode 100644 index 00000000..793993c4 Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/hidden_16.png differ diff --git a/JexusManager.Features.RequestFiltering/Resources/map_16.png b/JexusManager.Features.RequestFiltering/Resources/map_16.png new file mode 100644 index 00000000..19d90ebc Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/map_16.png differ diff --git a/JexusManager.Features.RequestFiltering/Resources/query_16.png b/JexusManager.Features.RequestFiltering/Resources/query_16.png new file mode 100644 index 00000000..ee4a4441 Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/query_16.png differ diff --git a/JexusManager.Features.RequestFiltering/Resources/request_filtering_36.png b/JexusManager.Features.RequestFiltering/Resources/request_filtering_36.png new file mode 100644 index 00000000..4860de2f Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/request_filtering_36.png differ diff --git a/JexusManager.Features.RequestFiltering/Resources/url_16.png b/JexusManager.Features.RequestFiltering/Resources/url_16.png new file mode 100644 index 00000000..a9850ee2 Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/url_16.png differ diff --git a/JexusManager.Features.RequestFiltering/Resources/verbs_16.png b/JexusManager.Features.RequestFiltering/Resources/verbs_16.png new file mode 100644 index 00000000..feebe1d4 Binary files /dev/null and b/JexusManager.Features.RequestFiltering/Resources/verbs_16.png differ diff --git a/JexusManager.Features.RequestFiltering/ScanHeadersItem.cs b/JexusManager.Features.RequestFiltering/ScanHeadersItem.cs new file mode 100644 index 00000000..691f3886 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/ScanHeadersItem.cs @@ -0,0 +1,43 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class ScanHeadersItem : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(ScanHeadersItem other) + { + return other != null && other.RequestHeader == RequestHeader; + } + + public ScanHeadersItem(ConfigurationElement element) + { + this.Element = element; + if (element == null) + { + return; + } + + this.RequestHeader = (string)element["requestHeader"]; + } + + public string RequestHeader { get; set; } + + public string Flag { get; set; } + + public void Apply() + { + this.Element["requestHeader"] = RequestHeader; + } + + public bool Equals(ScanHeadersItem other) + { + return Match(other); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.Designer.cs b/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.Designer.cs new file mode 100644 index 00000000..e74b0ead --- /dev/null +++ b/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.Designer.cs @@ -0,0 +1,227 @@ +namespace JexusManager.Features.RequestFiltering +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class SegmentSettingsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.cbDouble = new System.Windows.Forms.CheckBox(); + this.cbHigh = new System.Windows.Forms.CheckBox(); + this.cbVerb = new System.Windows.Forms.CheckBox(); + this.cbExtension = new System.Windows.Forms.CheckBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.txtQuery = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtURL = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtContent = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.cbDouble); + this.groupBox1.Controls.Add(this.cbHigh); + this.groupBox1.Controls.Add(this.cbVerb); + this.groupBox1.Controls.Add(this.cbExtension); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(355, 164); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "General"; + // + // cbDouble + // + this.cbDouble.AutoSize = true; + this.cbDouble.Location = new System.Drawing.Point(6, 125); + this.cbDouble.Name = "cbDouble"; + this.cbDouble.Size = new System.Drawing.Size(132, 17); + this.cbDouble.TabIndex = 3; + this.cbDouble.Text = "Allow double escaping"; + this.cbDouble.UseVisualStyleBackColor = true; + // + // cbHigh + // + this.cbHigh.AutoSize = true; + this.cbHigh.Location = new System.Drawing.Point(6, 90); + this.cbHigh.Name = "cbHigh"; + this.cbHigh.Size = new System.Drawing.Size(141, 17); + this.cbHigh.TabIndex = 2; + this.cbHigh.Text = "Allow high-bit characters"; + this.cbHigh.UseVisualStyleBackColor = true; + // + // cbVerb + // + this.cbVerb.AutoSize = true; + this.cbVerb.Location = new System.Drawing.Point(6, 53); + this.cbVerb.Name = "cbVerb"; + this.cbVerb.Size = new System.Drawing.Size(119, 17); + this.cbVerb.TabIndex = 1; + this.cbVerb.Text = "Allow unlisted verbs"; + this.cbVerb.UseVisualStyleBackColor = true; + // + // cbExtension + // + this.cbExtension.AutoSize = true; + this.cbExtension.Location = new System.Drawing.Point(6, 19); + this.cbExtension.Name = "cbExtension"; + this.cbExtension.Size = new System.Drawing.Size(188, 17); + this.cbExtension.TabIndex = 0; + this.cbExtension.Text = "Allow unlisted file name extensions"; + this.cbExtension.UseVisualStyleBackColor = true; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.txtQuery); + this.groupBox2.Controls.Add(this.label3); + this.groupBox2.Controls.Add(this.txtURL); + this.groupBox2.Controls.Add(this.label2); + this.groupBox2.Controls.Add(this.txtContent); + this.groupBox2.Controls.Add(this.label1); + this.groupBox2.Location = new System.Drawing.Point(12, 182); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(355, 184); + this.groupBox2.TabIndex = 1; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Request Limits"; + // + // txtQuery + // + this.txtQuery.Location = new System.Drawing.Point(6, 146); + this.txtQuery.Name = "txtQuery"; + this.txtQuery.Size = new System.Drawing.Size(228, 20); + this.txtQuery.TabIndex = 5; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(3, 130); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(146, 13); + this.label3.TabIndex = 4; + this.label3.Text = "Maximum query string (Bytes):"; + // + // txtURL + // + this.txtURL.Location = new System.Drawing.Point(6, 91); + this.txtURL.Name = "txtURL"; + this.txtURL.Size = new System.Drawing.Size(228, 20); + this.txtURL.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(3, 75); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(146, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Maximum URL length (Bytes):"; + // + // txtContent + // + this.txtContent.Location = new System.Drawing.Point(6, 43); + this.txtContent.Name = "txtContent"; + this.txtContent.Size = new System.Drawing.Size(228, 20); + this.txtContent.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 27); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(199, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Maximum allowed content length (Bytes):"; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(272, 401); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 2; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(176, 401); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 3; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // SegmentSettingsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(379, 436); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Name = "SegmentSettingsDialog"; + this.Text = "Edit Request Filtering Settings"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.SegmentSettingsDialog_HelpButtonClicked); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private GroupBox groupBox1; + private CheckBox cbDouble; + private CheckBox cbHigh; + private CheckBox cbVerb; + private CheckBox cbExtension; + private GroupBox groupBox2; + private TextBox txtQuery; + private Label label3; + private TextBox txtURL; + private Label label2; + private TextBox txtContent; + private Label label1; + private Button btnCancel; + private Button btnOK; + } +} diff --git a/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.cs b/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.cs new file mode 100644 index 00000000..41c29b5b --- /dev/null +++ b/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.cs @@ -0,0 +1,113 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + public partial class SegmentSettingsDialog : DialogForm + { + public SegmentSettingsDialog(IServiceProvider serviceProvider, ConfigurationSection section) + : base(serviceProvider) + { + InitializeComponent(); + cbExtension.Checked = (bool)section.ChildElements["fileExtensions"]["allowUnlisted"]; + cbVerb.Checked = (bool)section.ChildElements["verbs"]["allowUnlisted"]; + cbHigh.Checked = (bool)section["allowHighBitCharacters"]; + cbDouble.Checked = (bool)section["allowDoubleEscaping"]; + var limits = section.ChildElements["requestLimits"]; + txtContent.Text = limits["maxAllowedContentLength"].ToString(); + txtURL.Text = limits["maxUrl"].ToString(); + txtQuery.Text = limits["maxQueryString"].ToString(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + section.ChildElements["fileExtensions"]["allowUnlisted"] = cbExtension.Checked; + section.ChildElements["verbs"]["allowUnlisted"] = cbVerb.Checked; + section["allowHighBitCharacters"] = cbHigh.Checked; + section["allowDoubleEscaping"] = cbDouble.Checked; + + uint result; + if (!uint.TryParse(txtContent.Text, out result)) + { + ShowMessage( + string.Format( + "'{0}' is an invalid maximum {1}. Enter an integer between 0 and 4294967295", + txtContent.Text, + "content length"), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + txtContent.SelectAll(); + return; + } + + limits["maxAllowedContentLength"] = result; + + if (!uint.TryParse(txtURL.Text, out result)) + { + ShowMessage( + string.Format( + "'{0}' is an invalid maximum {1}. Enter an integer between 0 and 4294967295", + txtURL.Text, + "URL length"), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + txtURL.SelectAll(); + return; + } + + limits["maxUrl"] = result; + + if (!uint.TryParse(txtQuery.Text, out result)) + { + ShowMessage( + string.Format( + "'{0}' is an invalid maximum {1}. Enter an integer between 0 and 4294967295", + txtQuery.Text, + "query string length"), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + txtQuery.SelectAll(); + return; + } + + limits["maxQueryString"] = result; + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtContent, "TextChanged") + .Merge(Observable.FromEventPattern(txtQuery, "TextChanged")) + .Merge(Observable.FromEventPattern(txtURL, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtContent.Text) && + !string.IsNullOrWhiteSpace(txtURL.Text) && + !string.IsNullOrWhiteSpace(txtQuery.Text); + })); + } + + private void SegmentSettingsDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Edit_Filtering"); + } + } +} diff --git a/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.resx b/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/SegmentSettingsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.RequestFiltering/UrlsFeature.cs b/JexusManager.Features.RequestFiltering/UrlsFeature.cs new file mode 100644 index 00000000..6ab445ff --- /dev/null +++ b/JexusManager.Features.RequestFiltering/UrlsFeature.cs @@ -0,0 +1,147 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class UrlsFeature : RequestFilteringFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly UrlsFeature _owner; + + public FeatureTaskList(UrlsFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + + result.Add(new MethodTaskItem("AddUrl", "Allow URL...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("AddDenyUrl", "Deny Sequence...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddUrl() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public void AddDenyUrl() + { + _owner.AddDeny(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + } + + public UrlsFeature(Module module) + : base(module) + { + } + + private TaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Add() + { + var dialog = new NewUrlDialog(this.Module, true); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddDeny() + { + var dialog = new NewUrlDialog(this.Module, false); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected URL?", this.Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection requestFilteringSection = + service.GetSection("system.webServer/security/requestFiltering"); + return requestFilteringSection.GetCollection("alwaysAllowedUrls"); + } + + protected override ConfigurationElementCollection GetSecondaryCollection(IConfigurationService service) + { + ConfigurationSection requestFilteringSection = + service.GetSection("system.webServer/security/requestFiltering"); + return requestFilteringSection.GetCollection("denyUrlSequences"); + } + + public override void Load() + { + LoadItems(); + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#URL_Page"); + return true; + } + + public override string Name + { + get + { + return "URL"; + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/UrlsItem.cs b/JexusManager.Features.RequestFiltering/UrlsItem.cs new file mode 100644 index 00000000..556e3e74 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/UrlsItem.cs @@ -0,0 +1,60 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class UrlsItem : IDuoItem + { + public bool Allowed { get; } + + public ConfigurationElement Element { get; set; } + + public bool Match(UrlsItem other) + { + return other != null && other.Url == Url; + } + + public UrlsItem(ConfigurationElement element, bool allowed) + { + this.Allowed = allowed; + this.Element = element; + if (element == null) + { + return; + } + + if (allowed) + { + Url = (string)element["url"]; + } + else + { + Url = (string)element["sequence"]; + } + } + + public string Url { get; set; } + + public void Apply() + { + if (Allowed) + { + Element["url"] = Url; + } + else + { + Element["sequence"] = Url; + } + } + + public string Flag { get; set; } + + public bool Equals(UrlsItem other) + { + return Match(other) && other.Allowed == Allowed; + } + } +} diff --git a/JexusManager.Features.RequestFiltering/VerbsFeature.cs b/JexusManager.Features.RequestFiltering/VerbsFeature.cs new file mode 100644 index 00000000..a9931bdc --- /dev/null +++ b/JexusManager.Features.RequestFiltering/VerbsFeature.cs @@ -0,0 +1,139 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + internal class VerbsFeature : RequestFilteringFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly VerbsFeature _owner; + + public FeatureTaskList(VerbsFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("AddVerb", "Allow Verb...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("AddDenyVerb", "Deny Verb...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void AddVerb() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public void AddDenyVerb() + { + _owner.AddDeny(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + } + + public VerbsFeature(Module module) + : base(module) + { + } + + private TaskList _taskList; + + public override TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Add() + { + var dialog = new NewVerbDialog(this.Module, true); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void AddDeny() + { + var dialog = new NewVerbDialog(this.Module, false); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected verb?", this.Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + ConfigurationSection requestFilteringSection = + service.GetSection("system.webServer/security/requestFiltering"); + return requestFilteringSection.GetCollection("verbs"); + } + + public override void Load() + { + LoadItems(); + } + + public override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210526#Http_Verbs"); + return true; + } + + public override string Name + { + get + { + return "HTTP Verbs"; + } + } + } +} diff --git a/JexusManager.Features.RequestFiltering/VerbsItem.cs b/JexusManager.Features.RequestFiltering/VerbsItem.cs new file mode 100644 index 00000000..88f3a1e9 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/VerbsItem.cs @@ -0,0 +1,47 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.RequestFiltering +{ + using Microsoft.Web.Administration; + + internal class VerbsItem : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(VerbsItem other) + { + return other != null && other.Verb == Verb; + } + + public VerbsItem(ConfigurationElement element) + { + this.Element = element; + if (element == null) + { + return; + } + + Verb = (string)element["verb"]; + Allowed = (bool)element["allowed"]; + } + + public bool Allowed { get; set; } + + public string Verb { get; set; } + + public void Apply() + { + Element["verb"] = Verb; + Element["allowed"] = Allowed; + } + + public string Flag { get; set; } + + public bool Equals(VerbsItem other) + { + return Match(other) && other.Allowed == Allowed; + } + } +} diff --git a/JexusManager.Features.RequestFiltering/packages.config b/JexusManager.Features.RequestFiltering/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.RequestFiltering/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/JexusManager.Features.ResponseHeaders.csproj b/JexusManager.Features.ResponseHeaders/JexusManager.Features.ResponseHeaders.csproj new file mode 100644 index 00000000..eb5833b9 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/JexusManager.Features.ResponseHeaders.csproj @@ -0,0 +1,139 @@ + + + + + Debug + AnyCPU + {7184AE3D-12D2-444D-B75A-2589F66E6B54} + Library + Properties + JexusManager.Features.ResponseHeaders + JexusManager.Features.ResponseHeaders + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + Form + + + NewHeaderDialog.cs + + + + True + True + Resources.resx + + + + + + + UserControl + + + ResponseHeadersPage.cs + + + + Form + + + SetCommonHeadersDialog.cs + + + + + NewHeaderDialog.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + ResponseHeadersPage.cs + + + SetCommonHeadersDialog.cs + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/JexusManager.snk b/JexusManager.Features.ResponseHeaders/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.ResponseHeaders/JexusManager.snk differ diff --git a/JexusManager.Features.ResponseHeaders/NewHeaderDialog.Designer.cs b/JexusManager.Features.ResponseHeaders/NewHeaderDialog.Designer.cs new file mode 100644 index 00000000..c685d071 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/NewHeaderDialog.Designer.cs @@ -0,0 +1,122 @@ +namespace JexusManager.Features.ResponseHeaders +{ + using System.ComponentModel; + using System.Windows.Forms; + + sealed partial class NewHeaderDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtValue = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.Location = new System.Drawing.Point(252, 146); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(156, 146); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(38, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(332, 20); + this.txtName.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 60); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(37, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Value:"; + // + // txtValue + // + this.txtValue.Location = new System.Drawing.Point(15, 76); + this.txtValue.Name = "txtValue"; + this.txtValue.Size = new System.Drawing.Size(332, 20); + this.txtValue.TabIndex = 5; + // + // NewHeaderDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(359, 181); + this.Controls.Add(this.txtValue); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewHeaderDialog"; + this.Text = "Add Custom HTTP Response Header"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewHeaderDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private TextBox txtName; + private Label label2; + private TextBox txtValue; + } +} diff --git a/JexusManager.Features.ResponseHeaders/NewHeaderDialog.cs b/JexusManager.Features.ResponseHeaders/NewHeaderDialog.cs new file mode 100644 index 00000000..0c6ac847 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/NewHeaderDialog.cs @@ -0,0 +1,67 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.ResponseHeaders +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal sealed partial class NewHeaderDialog : DialogForm + { + public NewHeaderDialog(IServiceProvider serviceProvider, ResponseHeadersItem selectedItem, ResponseHeadersFeature feature) + : base(serviceProvider) + { + Item = selectedItem; + InitializeComponent(); + Text = selectedItem == null ? "Add Custom HTTP Response Header" : "Edit Custom HTTP Response Header"; + if (selectedItem != null) + { + txtName.Text = Item.Name; + txtValue.Text = Item.Value; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (feature.Items.Any(item => item != Item && txtName.Text == item.Name)) + { + ShowMessage( + "A header with this name already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + if (Item == null) + { + Item = new ResponseHeadersItem(null); + } + + Item.Name = txtName.Text; + Item.Value = txtValue.Text; + + DialogResult = DialogResult.OK; + })); + } + + private void NewHeaderDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210509"); + } + + public ResponseHeadersItem Item { get; set; } + } +} diff --git a/JexusManager.Features.ResponseHeaders/NewHeaderDialog.resx b/JexusManager.Features.ResponseHeaders/NewHeaderDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/NewHeaderDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/Properties/AssemblyInfo.cs b/JexusManager.Features.ResponseHeaders/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..e328621e --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +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("JexusManager.Features.ResponseHeaders")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JexusManager.Features.ResponseHeaders")] +[assembly: AssemblyCopyright("Copyright \u00A9 2016")] +[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("7184ae3d-12d2-444d-b75a-2589f66e6b54")] + +// 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")] + +[assembly: InternalsVisibleTo("Tests.JexusManager, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f7030532c52524" ++ "993841a0d09420340f3814e1b65473851bdcd18815510b035a2ae9ecee69c4cd2d9e4d6e6d5fbf" ++ "a564e86c4a4cddc9597619a31c060846ebb2e99511a0323ff82b1ebd95d6a4912502945f0e769f" ++ "190a69a439dbfb969ebad72a6f7e2e047907da4a7b9c08c6e98d5f1be8b8cafaf3eb978914059a" ++ "245d4bc1")] diff --git a/JexusManager.Features.ResponseHeaders/Properties/Resources.Designer.cs b/JexusManager.Features.ResponseHeaders/Properties/Resources.Designer.cs new file mode 100644 index 00000000..37dc2aa2 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace JexusManager.Features.ResponseHeaders.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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", "4.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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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("JexusManager.Features.ResponseHeaders.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap response_header_36 { + get { + object obj = ResourceManager.GetObject("response_header_36", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/JexusManager.Features.ResponseHeaders/Properties/Resources.resx b/JexusManager.Features.ResponseHeaders/Properties/Resources.resx new file mode 100644 index 00000000..d6b63b74 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\response_header_36.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/Resources/response_header_36.png b/JexusManager.Features.ResponseHeaders/Resources/response_header_36.png new file mode 100644 index 00000000..7b24c374 Binary files /dev/null and b/JexusManager.Features.ResponseHeaders/Resources/response_header_36.png differ diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersFeature.cs b/JexusManager.Features.ResponseHeaders/ResponseHeadersFeature.cs new file mode 100644 index 00000000..901499c4 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersFeature.cs @@ -0,0 +1,184 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * Created by SharpDevelop. + * User: lextm + * Time: 11:06 AM + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ + +namespace JexusManager.Features.ResponseHeaders +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class ResponseHeadersFeature : FeatureBase + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly ResponseHeadersFeature _owner; + + public FeatureTaskList(ResponseHeadersFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Set Common Headers...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + } + + public ResponseHeadersFeature(Module module) + : base(module) + { + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + LoadItems(); + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + var section = service.GetSection("system.webServer/httpProtocol"); + return section.GetCollection("customHeaders"); + } + + public void Add() + { + var dialog = new NewHeaderDialog(Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.AddItem(dialog.Item); + } + + public void Remove() + { + var dialog = (IManagementUIService)GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected header?", Name, + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + RemoveItem(); + } + + public void Edit() + { + var dialog = new NewHeaderDialog(Module, SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.EditItem(dialog.Item); + } + + public void Set() + { + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + var staticContent = service.GetSection("system.webServer/staticContent"); + var dialog = new SetCommonHeadersDialog(Module, service.GetSection("system.webServer/httpProtocol"), staticContent.ChildElements["clientCache"]); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + service.ServerManager.CommitChanges(); + } + + protected override void OnSettingsSaved() + { + ResponseHeadersSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210509"); + return false; + } + + public ResponseHeadersSettingsSavedEventHandler ResponseHeadersSettingsUpdated { get; set; } + public string Description { get; } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public string Name + { + get { return "HTTP Response Headers"; } + } + } +} diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersItem.cs b/JexusManager.Features.ResponseHeaders/ResponseHeadersItem.cs new file mode 100644 index 00000000..23e7cca8 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersItem.cs @@ -0,0 +1,46 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.ResponseHeaders +{ + using Microsoft.Web.Administration; + + internal class ResponseHeadersItem : IItem + { + public string Name { get; internal set; } + public string Value { get; internal set; } + public string Flag { get; set; } + public ConfigurationElement Element { get; set; } + + public ResponseHeadersItem(ConfigurationElement element) + { + Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + Element = element; + if (element == null) + { + Value = string.Empty; + return; + } + + Name = (string)element["name"]; + Value = (string)element["value"]; + } + + public bool Equals(ResponseHeadersItem other) + { + return Match(other); + } + + public bool Match(ResponseHeadersItem other) + { + return other != null && other.Name == Name; + } + + public void Apply() + { + Element["name"] = Name; + Element["value"] = Value; + } + } +} diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersModule.cs b/JexusManager.Features.ResponseHeaders/ResponseHeadersModule.cs new file mode 100644 index 00000000..d2f8097e --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersModule.cs @@ -0,0 +1,26 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.ResponseHeaders +{ + using System; + + using Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Server; + + internal class ResponseHeadersModule : Module + { + protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo) + { + base.Initialize(serviceProvider, moduleInfo); + var controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); + var modulePage = new ModulePageInfo(this, typeof(ResponseHeadersPage), "HTTP Response Headers", + "Configure HTTP headers that are added to responses from the Web server", Resources.response_header_36, + Resources.response_header_36); + controlPanel.RegisterPage(modulePage); + } + } +} diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersModuleProvider.cs b/JexusManager.Features.ResponseHeaders/ResponseHeadersModuleProvider.cs new file mode 100644 index 00000000..39447348 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersModuleProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.ResponseHeaders +{ + using System; + + using Microsoft.Web.Management.Server; + + public class ResponseHeadersModuleProvider : ModuleProvider + { + public override Type ServiceType + { + get { return null; } + } + + public override ModuleDefinition GetModuleDefinition(IManagementContext context) + { + return new ModuleDefinition(Name, typeof(ResponseHeadersModule).AssemblyQualifiedName); + } + + public override bool SupportsScope(ManagementScope scope) + { + return true; + } + } +} diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.Designer.cs b/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.Designer.cs new file mode 100644 index 00000000..36ce4a9c --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.Designer.cs @@ -0,0 +1,292 @@ +namespace JexusManager.Features.ResponseHeaders +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class ResponseHeadersPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 4; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 6; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chValue, + this.chType}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + this.listView1.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.ListView1MouseDoubleClick); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 120; + // + // chValue + // + this.chValue.Text = "Value"; + this.chValue.Width = 120; + // + // chType + // + this.chType.Text = "Entry Type"; + this.chType.Width = 120; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel2, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel2 + // + this.toolStripLabel2.Name = "toolStripLabel2"; + this.toolStripLabel2.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel2.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Status", + "IP Address", + "Protocol", + "Port"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(450, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to configure HTTP headers that are added to responses from the W" + + "eb server."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 5; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(257, 25); + this.label3.TabIndex = 2; + this.label3.Text = "HTTP Response Headers"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // ResponseHeadersPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "ResponseHeadersPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private ListView listView1; + private ColumnHeader chName; + private ColumnHeader chValue; + private ColumnHeader chType; + private Label label2; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private Panel panel2; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel2; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private ToolStrip tsActionPanel; + private ContextMenuStrip cmsActionPanel; + } +} diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.cs b/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.cs new file mode 100644 index 00000000..b08ead6d --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.cs @@ -0,0 +1,141 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.ResponseHeaders +{ + using System; + using System.Collections; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class ResponseHeadersPage : ModuleListPage + { + private sealed class PageTaskList : ShowHelpTaskList + { + private readonly ResponseHeadersPage _owner; + + public PageTaskList(ResponseHeadersPage owner) + { + _owner = owner; + } + + [Obfuscation(Exclude = true)] + public override void ShowHelp() + { + _owner.ShowHelp(); + } + } + + private sealed class ResponseHeadersListViewItem : ListViewItem + { + public ResponseHeadersItem Item { get; } + private readonly ResponseHeadersPage _page; + + public ResponseHeadersListViewItem(ResponseHeadersItem item, ResponseHeadersPage page) + : base(item.Name) + { + Item = item; + _page = page; + SubItems.Add(new ListViewSubItem(this, item.Value)); + SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private ResponseHeadersFeature _feature; + private PageTaskList _taskList; + + public ResponseHeadersPage() + { + InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new ResponseHeadersFeature(Module); + _feature.ResponseHeadersSettingsUpdated = InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new ResponseHeadersListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + Refresh(); + return; + } + + foreach (ResponseHeadersListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + protected override void Refresh() + { + Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((ResponseHeadersListViewItem)listView1.SelectedItems[0]).Item + : null; + Refresh(); + } + + private void ListView1MouseDoubleClick(object sender, MouseEventArgs e) + { + _feature.Edit(); + } + + protected override bool ShowHelp() + { + _feature.ShowHelp(); + return true; + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + } +} diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.resx b/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.resx new file mode 100644 index 00000000..876b3bf2 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersPage.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 281, 17 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/ResponseHeadersSettingsSavedEventHandler.cs b/JexusManager.Features.ResponseHeaders/ResponseHeadersSettingsSavedEventHandler.cs new file mode 100644 index 00000000..bfd7b533 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/ResponseHeadersSettingsSavedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.ResponseHeaders +{ + public delegate void ResponseHeadersSettingsSavedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.Designer.cs b/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.Designer.cs new file mode 100644 index 00000000..4f2151e9 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.Designer.cs @@ -0,0 +1,205 @@ +namespace JexusManager.Features.ResponseHeaders +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class SetCommonHeadersDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cbKeepAlive = new System.Windows.Forms.CheckBox(); + this.cbExpired = new System.Windows.Forms.CheckBox(); + this.rbImmediate = new System.Windows.Forms.RadioButton(); + this.rbAfter = new System.Windows.Forms.RadioButton(); + this.txtAfter = new System.Windows.Forms.TextBox(); + this.cbUnit = new System.Windows.Forms.ComboBox(); + this.rbTime = new System.Windows.Forms.RadioButton(); + this.dtpDate = new System.Windows.Forms.DateTimePicker(); + this.dtpTime = new System.Windows.Forms.DateTimePicker(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // cbKeepAlive + // + this.cbKeepAlive.AutoSize = true; + this.cbKeepAlive.Location = new System.Drawing.Point(12, 12); + this.cbKeepAlive.Name = "cbKeepAlive"; + this.cbKeepAlive.Size = new System.Drawing.Size(143, 17); + this.cbKeepAlive.TabIndex = 0; + this.cbKeepAlive.Text = "Enable HTTP keep-alive"; + this.cbKeepAlive.UseVisualStyleBackColor = true; + // + // cbExpired + // + this.cbExpired.AutoSize = true; + this.cbExpired.Location = new System.Drawing.Point(12, 35); + this.cbExpired.Name = "cbExpired"; + this.cbExpired.Size = new System.Drawing.Size(123, 17); + this.cbExpired.TabIndex = 1; + this.cbExpired.Text = "Expire Web content:"; + this.cbExpired.UseVisualStyleBackColor = true; + this.cbExpired.CheckedChanged += new System.EventHandler(this.cbExpired_CheckedChanged); + // + // rbImmediate + // + this.rbImmediate.AutoSize = true; + this.rbImmediate.Enabled = false; + this.rbImmediate.Location = new System.Drawing.Point(33, 58); + this.rbImmediate.Name = "rbImmediate"; + this.rbImmediate.Size = new System.Drawing.Size(80, 17); + this.rbImmediate.TabIndex = 2; + this.rbImmediate.TabStop = true; + this.rbImmediate.Text = "Immediately"; + this.rbImmediate.UseVisualStyleBackColor = true; + // + // rbAfter + // + this.rbAfter.AutoSize = true; + this.rbAfter.Enabled = false; + this.rbAfter.Location = new System.Drawing.Point(33, 81); + this.rbAfter.Name = "rbAfter"; + this.rbAfter.Size = new System.Drawing.Size(47, 17); + this.rbAfter.TabIndex = 3; + this.rbAfter.TabStop = true; + this.rbAfter.Text = "After"; + this.rbAfter.UseVisualStyleBackColor = true; + this.rbAfter.CheckedChanged += new System.EventHandler(this.rbAfter_CheckedChanged); + // + // txtAfter + // + this.txtAfter.Enabled = false; + this.txtAfter.Location = new System.Drawing.Point(53, 104); + this.txtAfter.Name = "txtAfter"; + this.txtAfter.Size = new System.Drawing.Size(60, 20); + this.txtAfter.TabIndex = 4; + // + // cbUnit + // + this.cbUnit.Enabled = false; + this.cbUnit.FormattingEnabled = true; + this.cbUnit.Items.AddRange(new object[] { + "Second(s)", + "Minute(s)", + "Hour(s)", + "Day(s)"}); + this.cbUnit.Location = new System.Drawing.Point(119, 104); + this.cbUnit.Name = "cbUnit"; + this.cbUnit.Size = new System.Drawing.Size(121, 21); + this.cbUnit.TabIndex = 5; + // + // rbTime + // + this.rbTime.AutoSize = true; + this.rbTime.Enabled = false; + this.rbTime.Location = new System.Drawing.Point(33, 130); + this.rbTime.Name = "rbTime"; + this.rbTime.Size = new System.Drawing.Size(209, 17); + this.rbTime.TabIndex = 6; + this.rbTime.TabStop = true; + this.rbTime.Text = "On (Coordinated Universal Time (UTC))"; + this.rbTime.UseVisualStyleBackColor = true; + this.rbTime.CheckedChanged += new System.EventHandler(this.rbTime_CheckedChanged); + // + // dtpDate + // + this.dtpDate.Enabled = false; + this.dtpDate.Location = new System.Drawing.Point(53, 166); + this.dtpDate.Name = "dtpDate"; + this.dtpDate.Size = new System.Drawing.Size(200, 20); + this.dtpDate.TabIndex = 7; + // + // dtpTime + // + this.dtpTime.Enabled = false; + this.dtpTime.Format = System.Windows.Forms.DateTimePickerFormat.Time; + this.dtpTime.Location = new System.Drawing.Point(259, 166); + this.dtpTime.Name = "dtpTime"; + this.dtpTime.Size = new System.Drawing.Size(101, 20); + this.dtpTime.TabIndex = 8; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(352, 221); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 9; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(256, 221); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 10; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // SetCommonHeadersDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(459, 256); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.dtpTime); + this.Controls.Add(this.dtpDate); + this.Controls.Add(this.rbTime); + this.Controls.Add(this.cbUnit); + this.Controls.Add(this.txtAfter); + this.Controls.Add(this.rbAfter); + this.Controls.Add(this.rbImmediate); + this.Controls.Add(this.cbExpired); + this.Controls.Add(this.cbKeepAlive); + this.Name = "SetCommonHeadersDialog"; + this.Text = "Set Common HTTP Response Headers"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.SetCommonHeadersDialog_HelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private CheckBox cbKeepAlive; + private CheckBox cbExpired; + private RadioButton rbImmediate; + private RadioButton rbAfter; + private TextBox txtAfter; + private ComboBox cbUnit; + private RadioButton rbTime; + private DateTimePicker dtpDate; + private DateTimePicker dtpTime; + private Button btnCancel; + private Button btnOK; + } +} \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.cs b/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.cs new file mode 100644 index 00000000..55afa4d9 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.cs @@ -0,0 +1,203 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.ResponseHeaders +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Windows.Forms; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + public partial class SetCommonHeadersDialog : DialogForm + { + private readonly ConfigurationSection _httpProtocolSection; + private readonly ConfigurationElement _staticContent; + + public SetCommonHeadersDialog(IServiceProvider serviceProvider, ConfigurationSection httpProtocolSection, ConfigurationElement staticContent) + : base(serviceProvider) + { + InitializeComponent(); + _httpProtocolSection = httpProtocolSection; + _staticContent = staticContent; + cbKeepAlive.Checked = (bool)httpProtocolSection["allowKeepAlive"]; + var mode = (long)_staticContent["cacheControlMode"]; + cbExpired.Checked = mode != 0L; + rbImmediate.Checked = mode == 1L; + rbAfter.Checked = mode == 2L; + rbTime.Checked = mode == 3L; + string time = (string)_staticContent["httpExpires"]; + DateTime output; + if (DateTime.TryParse(time, out output)) + { + dtpDate.Value = output; + dtpTime.Value = output; + } + + var span = (TimeSpan)_staticContent["cacheControlMaxAge"]; + if (span.Seconds == 0) + { + if (span.Minutes == 0) + { + if (span.Hours == 0) + { + cbUnit.SelectedIndex = 3; + txtAfter.Text = span.Days.ToString(); + } + else + { + cbUnit.SelectedIndex = 2; + txtAfter.Text = (span.Hours + span.Days * 24).ToString(); + } + } + else + { + cbUnit.SelectedIndex = 1; + txtAfter.Text = (span.Minutes + (span.Hours + span.Days * 24) * 60).ToString(); + } + } + else + { + cbUnit.SelectedIndex = 0; + txtAfter.Text = (span.Seconds + (span.Minutes + (span.Hours + span.Days * 24) * 60) * 60).ToString(); + } + } + + private void btnOK_Click(object sender, EventArgs e) + { + _httpProtocolSection["allowKeepAlive"] = cbKeepAlive.Checked; + if (cbExpired.Checked) + { + if (rbImmediate.Checked) + { + _staticContent["cacheControlMode"] = 1L; + } + else if (rbAfter.Checked) + { + _staticContent["cacheControlMode"] = 2L; + long value; + if (!long.TryParse(txtAfter.Text, out value) || value < 0) + { + ShowMessage( + GetMessage(cbUnit.SelectedIndex), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + if (cbUnit.SelectedIndex == 0) + { + if (value > GetMax(cbUnit.SelectedIndex)) + { + ShowMessage( + GetMessage(cbUnit.SelectedIndex), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + _staticContent["cacheControlMaxAge"] = TimeSpan.FromSeconds(value); + } + else if (cbUnit.SelectedIndex == 1) + { + if (value > GetMax(cbUnit.SelectedIndex)) + { + ShowMessage( + GetMessage(cbUnit.SelectedIndex), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + _staticContent["cacheControlMaxAge"] = TimeSpan.FromMinutes(value); + } + else if (cbUnit.SelectedIndex == 2) + { + if (value > GetMax(cbUnit.SelectedIndex)) + { + ShowMessage( + GetMessage(cbUnit.SelectedIndex), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + _staticContent["cacheControlMaxAge"] = TimeSpan.FromHours(value); + } + else + { + if (value > GetMax(cbUnit.SelectedIndex)) + { + ShowMessage( + GetMessage(cbUnit.SelectedIndex), + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + _staticContent["cacheControlMaxAge"] = TimeSpan.FromDays(value); + } + } + else + { + _staticContent["cacheControlMode"] = 3L; + // Wed, 27 May 2015 00:00:00 GMT + _staticContent["httpExpires"] = (dtpDate.Value.Date + dtpTime.Value.TimeOfDay).ToString("R"); + } + } + else + { + _staticContent["cacheControlMode"] = 0L; + } + + DialogResult = DialogResult.OK; + } + + private string GetMessage(int selectedIndex) + { + return string.Format( + "The specified expiration value is invalid. The valid range is between 0 and {0} {1}.", GetMax(selectedIndex), cbUnit.Text); + } + + private readonly long[] _max = { 922337203686, 15372286728, 256204778, 10675199 }; + + private long GetMax(int index) + { + return _max[index]; + } + + private void SetCommonHeadersDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=210509"); + } + + private void cbExpired_CheckedChanged(object sender, EventArgs e) + { + rbImmediate.Enabled = cbExpired.Checked; + rbAfter.Enabled = cbExpired.Checked; + rbTime.Enabled = cbExpired.Checked; + rbAfter_CheckedChanged(sender, e); + rbTime_CheckedChanged(sender, e); + } + + private void rbAfter_CheckedChanged(object sender, EventArgs e) + { + txtAfter.Enabled = rbAfter.Checked && rbAfter.Enabled; + cbUnit.Enabled = rbAfter.Checked && rbAfter.Enabled; + } + + private void rbTime_CheckedChanged(object sender, EventArgs e) + { + dtpDate.Enabled = rbTime.Checked && rbTime.Enabled; + dtpTime.Enabled = rbTime.Checked && rbTime.Enabled; + } + } +} diff --git a/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.resx b/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/SetCommonHeadersDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.ResponseHeaders/packages.config b/JexusManager.Features.ResponseHeaders/packages.config new file mode 100644 index 00000000..571e4fe0 --- /dev/null +++ b/JexusManager.Features.ResponseHeaders/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/ConditionItem.cs b/JexusManager.Features.Rewrite/ConditionItem.cs new file mode 100644 index 00000000..fe30a6a3 --- /dev/null +++ b/JexusManager.Features.Rewrite/ConditionItem.cs @@ -0,0 +1,57 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite +{ + using Microsoft.Web.Administration; + + public class ConditionItem + { + public ConfigurationElement Element { get; set; } + + public ConditionItem(ConfigurationElement element) + { + this.Element = element; + if (element == null) + { + this.Input = "{QUERY_STRING}"; + this.MatchType = 4; + this.IgnoreCase = true; + return; + } + + this.Input = (string)element["input"]; + this.Pattern = (string)element["pattern"]; + this.IgnoreCase = (bool)element["ignoreCase"]; + + var root = (long)element["matchType"]; + var negate = (bool)element["negate"]; + this.MatchType = (int)root * 2 + (negate ? 1 : 0); + } + + public bool IgnoreCase { get; set; } + + public string Pattern { get; set; } + + public string Input { get; set; } + + public int MatchType { get; set; } + + public void Apply() + { + Element["input"] = Input; + Element["pattern"] = Pattern; + Element["ignoreCase"] = IgnoreCase; + Element["negate"] = MatchType % 2 == 1; + Element["matchType"] = MatchType / 2; + } + + public void AppendTo(ConfigurationElementCollection conditionsCollection) + { + Element = conditionsCollection.CreateElement(); + Apply(); + conditionsCollection.Add(Element); + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/ConditionListViewItem.cs b/JexusManager.Features.Rewrite/ConditionListViewItem.cs new file mode 100644 index 00000000..95f6b2cb --- /dev/null +++ b/JexusManager.Features.Rewrite/ConditionListViewItem.cs @@ -0,0 +1,55 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite +{ + using System.Windows.Forms; + + internal class ConditionListViewItem : ListViewItem + { + private readonly ListViewSubItem _matchType; + + private readonly ListViewSubItem _pattern; + + public ConditionListViewItem(ConditionItem condition) + : base(condition.Input) + { + _matchType = new ListViewSubItem(this, GetText(condition.MatchType)); + this.SubItems.Add(_matchType); + _pattern = new ListViewSubItem(this, condition.Pattern); + this.SubItems.Add(_pattern); + this.Item = condition; + } + + public ConditionItem Item { get; } + + public void Update() + { + this.Text = this.Item.Input; + _matchType.Text = GetText(this.Item.MatchType); + _pattern.Text = this.Item.MatchType > 3 ? this.Item.Pattern : "N/A"; + } + + private static string GetText(int matchType) + { + switch (matchType) + { + case 0: + return "Is File"; + case 1: + return "Is Not File"; + case 2: + return "Is Directory"; + case 3: + return "Is Not Directory"; + case 4: + return "Matches the Pattern"; + case 5: + return "Does Not Match the Pattern"; + } + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.Designer.cs b/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.Designer.cs new file mode 100644 index 00000000..9a21d9bb --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.Designer.cs @@ -0,0 +1,102 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AddAllowedVariableDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(110, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Server variable name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(16, 39); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(351, 20); + this.txtName.TabIndex = 1; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(176, 81); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 2; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(272, 81); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // AddVariableDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(379, 116); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label1); + this.Name = "AddVariableDialog"; + this.Text = "Add Server Variable"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AddVariableDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.cs b/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.cs new file mode 100644 index 00000000..de06ea74 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.cs @@ -0,0 +1,67 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class AddAllowedVariableDialog : DialogForm + { + public AddAllowedVariableDialog(IServiceProvider serviceProvider, AllowedVariablesFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (feature.Items.Any(item => txtName.Text == item.Name)) + { + ShowMessage( + "The specified server variable already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + Item = new AllowedVariableItem(null, feature) { Name = txtName.Text }; + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + } + + private void AddVariableDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=163108"); + } + + public AllowedVariableItem Item { get; set; } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.resx b/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddAllowedVariableDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.Designer.cs b/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.Designer.cs new file mode 100644 index 00000000..12edc038 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.Designer.cs @@ -0,0 +1,180 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class AddConditionDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.txtInput = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.cbCheck = new System.Windows.Forms.ComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPattern = new System.Windows.Forms.TextBox(); + this.btnTest = new System.Windows.Forms.Button(); + this.cbIgnore = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(352, 241); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(256, 241); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(9, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(80, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Condition input:"; + // + // txtInput + // + this.txtInput.Location = new System.Drawing.Point(12, 25); + this.txtInput.Name = "txtInput"; + this.txtInput.Size = new System.Drawing.Size(435, 20); + this.txtInput.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(9, 67); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(103, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Check if input string:"; + // + // cbCheck + // + this.cbCheck.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbCheck.FormattingEnabled = true; + this.cbCheck.Items.AddRange(new object[] { + "Is a File", + "Is Not a File", + "Is a Directory", + "Is Not a Directory", + "Matches the Pattern", + "Does Not Match the Pattern"}); + this.cbCheck.Location = new System.Drawing.Point(12, 83); + this.cbCheck.Name = "cbCheck"; + this.cbCheck.Size = new System.Drawing.Size(243, 21); + this.cbCheck.TabIndex = 5; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(9, 130); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(44, 13); + this.label3.TabIndex = 6; + this.label3.Text = "Pattern:"; + // + // txtPattern + // + this.txtPattern.Location = new System.Drawing.Point(12, 146); + this.txtPattern.Name = "txtPattern"; + this.txtPattern.Size = new System.Drawing.Size(305, 20); + this.txtPattern.TabIndex = 7; + // + // btnTest + // + this.btnTest.Location = new System.Drawing.Point(323, 144); + this.btnTest.Name = "btnTest"; + this.btnTest.Size = new System.Drawing.Size(125, 23); + this.btnTest.TabIndex = 8; + this.btnTest.Text = "Test pattern..."; + this.btnTest.UseVisualStyleBackColor = true; + // + // cbIgnore + // + this.cbIgnore.AutoSize = true; + this.cbIgnore.Location = new System.Drawing.Point(12, 184); + this.cbIgnore.Name = "cbIgnore"; + this.cbIgnore.Size = new System.Drawing.Size(82, 17); + this.cbIgnore.TabIndex = 9; + this.cbIgnore.Text = "Ignore case"; + this.cbIgnore.UseVisualStyleBackColor = true; + // + // AddConditionDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(459, 276); + this.Controls.Add(this.cbIgnore); + this.Controls.Add(this.btnTest); + this.Controls.Add(this.txtPattern); + this.Controls.Add(this.label3); + this.Controls.Add(this.cbCheck); + this.Controls.Add(this.label2); + this.Controls.Add(this.txtInput); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "AddConditionDialog"; + this.Text = "Add Condition"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AddConditionDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private TextBox txtInput; + private Label label2; + private ComboBox cbCheck; + private Label label3; + private TextBox txtPattern; + private Button btnTest; + private CheckBox cbIgnore; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.cs b/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.cs new file mode 100644 index 00000000..ad343774 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.cs @@ -0,0 +1,89 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class AddConditionDialog : DialogForm + { + public AddConditionDialog(IServiceProvider serviceProvider, ConditionItem existing) + : base(serviceProvider) + { + InitializeComponent(); + Item = existing ?? new ConditionItem(null); + txtInput.Text = Item.Input; + txtPattern.Text = Item.Pattern; + cbIgnore.Checked = Item.IgnoreCase; + cbCheck.SelectedIndex = Item.MatchType; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + var check = Observable.FromEventPattern(cbCheck, "SelectedIndexChanged"); + container.Add( + check.Subscribe(evt => + { + txtPattern.Enabled = btnTest.Enabled = cbCheck.SelectedIndex > 3; + if (!txtPattern.Enabled) + { + txtPattern.Text = string.Empty; + } + })); + + container.Add( + Observable.FromEventPattern(txtPattern, "TextChanged") + .Merge(check) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtInput.Text) && (cbCheck.SelectedIndex < 4 || !string.IsNullOrWhiteSpace(txtPattern.Text)); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.Input = txtInput.Text; + Item.Pattern = txtPattern.Text; + Item.IgnoreCase = cbIgnore.Checked; + Item.MatchType = cbCheck.SelectedIndex; + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(btnTest, "Click") + .Subscribe(evt => + { + var dialog = new RegexTestDialog(ServiceProvider, txtPattern.Text, cbIgnore.Checked, true); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + txtPattern.Text = dialog.Pattern; + cbIgnore.Checked = dialog.IgnoreCase; + })); + } + + public ConditionItem Item { get; private set; } + + private void AddConditionDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130403&clcid=0x409"); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.resx b/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddConditionDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/AddMapDialog.Designer.cs b/JexusManager.Features.Rewrite/Inbound/AddMapDialog.Designer.cs new file mode 100644 index 00000000..cfa6fc8a --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddMapDialog.Designer.cs @@ -0,0 +1,124 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AddMapDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.txtOriginal = new System.Windows.Forms.TextBox(); + this.txtNew = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(216, 156); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 0; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(312, 156); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(74, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Original value:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 73); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(61, 13); + this.label2.TabIndex = 3; + this.label2.Text = "New value:"; + // + // txtOriginal + // + this.txtOriginal.Location = new System.Drawing.Point(12, 29); + this.txtOriginal.Name = "txtOriginal"; + this.txtOriginal.Size = new System.Drawing.Size(395, 20); + this.txtOriginal.TabIndex = 4; + // + // txtNew + // + this.txtNew.Location = new System.Drawing.Point(12, 89); + this.txtNew.Name = "txtNew"; + this.txtNew.Size = new System.Drawing.Size(395, 20); + this.txtNew.TabIndex = 5; + // + // AddMapDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(419, 191); + this.Controls.Add(this.txtNew); + this.Controls.Add(this.txtOriginal); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Name = "AddMapDialog"; + this.Text = "Add Mapping Entry"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AddMapDialog_HelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnOK; + private Button btnCancel; + private Label label1; + private Label label2; + private TextBox txtOriginal; + private TextBox txtNew; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddMapDialog.cs b/JexusManager.Features.Rewrite/Inbound/AddMapDialog.cs new file mode 100644 index 00000000..6581a0e2 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddMapDialog.cs @@ -0,0 +1,63 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class AddMapDialog : DialogForm + { + public MapRule Item { get; set; } + + public AddMapDialog(IServiceProvider serviceProvider, MapRule existing, MapsFeature feature) + : base(serviceProvider) + { + Item = existing; + InitializeComponent(); + Text = existing == null ? "Add Mapping Entry" : "Edit Mapping Entry"; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtOriginal, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtOriginal.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (Item == null) + { + Item = new MapRule(null, feature); + } + + Item.Original = txtOriginal.Text; + Item.New = txtNew.Text; + DialogResult = DialogResult.OK; + })); + } + + private void AddMapDialog_HelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130406&clcid=0x409"); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddMapDialog.resx b/JexusManager.Features.Rewrite/Inbound/AddMapDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddMapDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.Designer.cs b/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.Designer.cs new file mode 100644 index 00000000..49d13dbb --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.Designer.cs @@ -0,0 +1,102 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class AddMapsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(110, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Server variable name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(16, 39); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(351, 20); + this.txtName.TabIndex = 1; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(176, 81); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 2; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(272, 81); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // AddVariableDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(379, 116); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label1); + this.Name = "AddVariableDialog"; + this.Text = "Add Server Variable"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AddVariableDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private TextBox txtName; + private Button btnOK; + private Button btnCancel; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.cs b/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.cs new file mode 100644 index 00000000..5d0de39a --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.cs @@ -0,0 +1,67 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class AddMapsDialog : DialogForm + { + public AddMapsDialog(IServiceProvider serviceProvider, MapsFeature feature) + : base(serviceProvider) + { + InitializeComponent(); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + if (feature.Items.Any(item => txtName.Text == item.Name)) + { + ShowMessage( + "The specified server variable already exists.", + MessageBoxButtons.OK, + MessageBoxIcon.Error, + MessageBoxDefaultButton.Button1); + return; + } + + Item = new MapItem(null, feature) { Name = txtName.Text, DefaultValue = string.Empty }; + DialogResult = DialogResult.OK; + })); + + container.Add( + Observable.FromEventPattern(txtName, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text); + })); + } + + private void AddVariableDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=163108"); + } + + public MapItem Item { get; set; } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.resx b/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddMapsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.Designer.cs b/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.Designer.cs new file mode 100644 index 00000000..763be652 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.Designer.cs @@ -0,0 +1,138 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class AddServerVariableDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.cbName = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtValue = new System.Windows.Forms.TextBox(); + this.cbReplace = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(242, 211); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(146, 211); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(110, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Server variable name:"; + // + // cbName + // + this.cbName.FormattingEnabled = true; + this.cbName.Location = new System.Drawing.Point(15, 25); + this.cbName.Name = "cbName"; + this.cbName.Size = new System.Drawing.Size(294, 21); + this.cbName.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 84); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(37, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Value:"; + // + // txtValue + // + this.txtValue.Location = new System.Drawing.Point(15, 100); + this.txtValue.Name = "txtValue"; + this.txtValue.Size = new System.Drawing.Size(294, 20); + this.txtValue.TabIndex = 5; + // + // cbReplace + // + this.cbReplace.AutoSize = true; + this.cbReplace.Location = new System.Drawing.Point(15, 149); + this.cbReplace.Name = "cbReplace"; + this.cbReplace.Size = new System.Drawing.Size(151, 17); + this.cbReplace.TabIndex = 6; + this.cbReplace.Text = "Replace the existing value"; + this.cbReplace.UseVisualStyleBackColor = true; + // + // AddServerVariableDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(349, 246); + this.Controls.Add(this.cbReplace); + this.Controls.Add(this.txtValue); + this.Controls.Add(this.label2); + this.Controls.Add(this.cbName); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "AddServerVariableDialog"; + this.Text = "Set Server Variable"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.AddServerVariableDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private ComboBox cbName; + private Label label2; + private TextBox txtValue; + private CheckBox cbReplace; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.cs b/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.cs new file mode 100644 index 00000000..7ea8300e --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.cs @@ -0,0 +1,72 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client.Win32; + + internal partial class AddServerVariableDialog : DialogForm + { + public AddServerVariableDialog(IServiceProvider serviceProvider, ServerVariableItem existing) + : base(serviceProvider) + { + InitializeComponent(); + Item = existing ?? new ServerVariableItem(null); + cbName.Text = Item.Name; + txtValue.Text = Item.Value; + cbReplace.Checked = Item.Replace; + var service = (IConfigurationService)serviceProvider.GetService(typeof(IConfigurationService)); + var rulesSection = service.GetSection("system.webServer/rewrite/allowedServerVariables"); + ConfigurationElementCollection rulesCollection = rulesSection.GetCollection(); + foreach (ConfigurationElement ruleElement in rulesCollection) + { + cbName.Items.Add(ruleElement["name"]); + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(txtValue, "TextChanged") + .Merge(Observable.FromEventPattern(cbName, "TextChanged")) + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(cbName.Text) && !string.IsNullOrWhiteSpace(txtValue.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + Item.Name = cbName.Text; + Item.Value = txtValue.Text; + Item.Replace = cbReplace.Checked; + DialogResult = DialogResult.OK; + })); + } + + public ServerVariableItem Item { get; set; } + + private void AddServerVariableDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkId=163108"); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.resx b/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AddServerVariableDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/AllowedVariableItem.cs b/JexusManager.Features.Rewrite/Inbound/AllowedVariableItem.cs new file mode 100644 index 00000000..7743a6d0 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AllowedVariableItem.cs @@ -0,0 +1,47 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite.Inbound +{ + using Microsoft.Web.Administration; + + internal class AllowedVariableItem : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(AllowedVariableItem other) + { + return other != null && other.Name == Name; + } + + private readonly AllowedVariablesFeature _feature; + + public string Name { get; internal set; } + + public string Flag { get; set; } + + public AllowedVariableItem(ConfigurationElement element, AllowedVariablesFeature feature) + { + this.Element = element; + _feature = feature; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + if (element == null) + { + return; + } + + this.Name = (string)element["name"]; + } + + public void Apply() + { + Element["name"] = Name; + } + + public bool Equals(AllowedVariableItem other) + { + return Match(other); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/AllowedVariablesFeature.cs b/JexusManager.Features.Rewrite/Inbound/AllowedVariablesFeature.cs new file mode 100644 index 00000000..d7441429 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/AllowedVariablesFeature.cs @@ -0,0 +1,251 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using System.Resources; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class AllowedVariablesFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly AllowedVariablesFeature _owner; + + public FeatureTaskList(AllowedVariablesFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Rename", "Rename", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + if (_owner.CanRevert) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Revert", "Revert to Parent", string.Empty).SetUsage()); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Rename() + { + _owner.Rename(); + } + + [Obfuscation(Exclude = true)] + public void Revert() + { + _owner.Revert(); + } + } + + public AllowedVariablesFeature(Module module) + { + this.Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), string.Empty, false); + } + + protected object GetService(Type type) + { + return (this.Module as IServiceProvider).GetService(type); + } + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + this.Items = new List(); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/rewrite/allowedServerVariables"); + ConfigurationElementCollection rulesCollection = section.GetCollection(); + foreach (ConfigurationElement ruleElement in rulesCollection) + { + var node = new AllowedVariableItem(ruleElement, this); + this.Items.Add(node); + } + + this.CanRevert = section.CanRevert(); + this.OnRewriteSettingsSaved(); + } + + public List Items { get; set; } + + public void Add() + { + var dialog = new AddAllowedVariableDialog(this.Module, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var rulesSection = service.GetSection("system.webServer/rewrite/allowedServerVariables"); + ConfigurationElementCollection rulesCollection = rulesSection.GetCollection(); + + if (this.SelectedItem != newItem) + { + this.Items.Add(newItem); + this.SelectedItem = newItem; + } + else if (newItem.Flag != "Local") + { + rulesCollection.Remove(newItem.Element); + newItem.Flag = "Local"; + } + + newItem.AppendTo(rulesCollection); + service.ServerManager.CommitChanges(); + this.OnRewriteSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected entry?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + this.Items.Remove(this.SelectedItem); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/rewrite/allowedServerVariables"); + ConfigurationElementCollection collection = section.GetCollection(); + collection.Remove(this.SelectedItem.Element); + service.ServerManager.CommitChanges(); + + this.SelectedItem = null; + this.OnRewriteSettingsSaved(); + } + + internal protected void OnRewriteSettingsSaved() + { + this.RewriteSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130403&clcid=0x409"); + return false; + } + + public void Revert() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "Reverting to the parent configuration will result in the loss of all settings in the local configuration file for this feature. Are you sure you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/rewrite/allowedServerVariables"); + ConfigurationElementCollection collection = section.GetCollection(); + collection.Clear(); + collection.Delete(); + collection = section.GetCollection(); + + this.SelectedItem = null; + this.Items.Clear(); + foreach (ConfigurationElement ruleElement in collection) + { + var node = new AllowedVariableItem(ruleElement, this); + this.Items.Add(node); + } + + service.ServerManager.CommitChanges(); + this.OnRewriteSettingsSaved(); + } + + private void Rename() + { + } + + public AllowedVariableItem SelectedItem { get; internal set; } + public bool CanRevert { get; private set; } + + public RewriteSettingsSavedEventHandler RewriteSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + public string Name { get; } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/InboundFeature.cs b/JexusManager.Features.Rewrite/Inbound/InboundFeature.cs new file mode 100644 index 00000000..b5316898 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/InboundFeature.cs @@ -0,0 +1,184 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.Linq; + using System.Windows.Forms; + + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + public class InboundFeature : FeatureBase + { + public InboundFeature(Module module) + : base(module) + { + } + + protected override ConfigurationElementCollection GetCollection(IConfigurationService service) + { + var outSection = service.GetSection("system.webServer/rewrite/rules"); + return outSection.GetCollection(); + } + + protected override void OnSettingsSaved() + { + this.RewriteSettingsUpdated?.Invoke(); + } + + public void Refresh() + { + OnSettingsSaved(); + } + + public RewriteSettingsSavedEventHandler RewriteSettingsUpdated { get; set; } + + public string Name + { + get { return "URL Rewrite"; } + } + + public void Load() + { + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/rewrite/rules"); + this.CanRevert = section.CanRevert(); + } + + public bool CanRevert { get; set; } + + public void Add() + { + int index = 0; + string name; + do + { + index++; + name = string.Format("LowerCaseRule{0}", index); + } + while (this.Items.All(item => item.Name != name)); + var newRule = new InboundRule(null); + newRule.Name = name; + newRule.Input = "URL Path"; + newRule.PatternSyntax = 0L; + newRule.PatternUrl = "[A-Z]"; + newRule.Type = 2L; + newRule.ActionUrl = "{ToLower:{URL}}"; + newRule.IgnoreCase = false; + newRule.RedirectType = 301; + + this.AddItem(newRule); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected rule?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + this.RemoveItem(); + } + + public void AddConditions() + { + var dialog = new AddConditionDialog(this.Module, null); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + this.SelectedItem.Conditions.Add(newItem); + } + + public void MoveUp() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + this.MoveUpItem(); + } + + public void MoveDown() + { + if (this.Items.Any(item => item.Flag != "Local")) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "The list order will be changed for this feature. If you continue, changes made to this feature at a parent level will no longer be inherited at this level. Do you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + } + + this.MoveDownItem(); + } + + public void Disable() + { + this.SelectedItem.Enabled = false; + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + this.SelectedItem.Element["enabled"] = false; + service.ServerManager.CommitChanges(); + this.OnSettingsSaved(); + } + + public void Enable() + { + this.SelectedItem.Enabled = true; + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + this.SelectedItem.Element["enabled"] = true; + service.ServerManager.CommitChanges(); + this.OnSettingsSaved(); + } + + public void Revert() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + var result = + dialog.ShowMessage( + "Reverting to the parent configuration will result in the loss of all settings in the local configuration file for this feature. Are you sure you want to continue?", + this.Name, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + if (result != DialogResult.Yes) + { + return; + } + + this.RevertItems(); + } + + public void Edit() + { + var service = (INavigationService)this.GetService(typeof(INavigationService)); + service.Navigate(null, null, typeof(InboundRulePage), new Tuple(this, this.SelectedItem)); + this.OnSettingsSaved(); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/InboundRule.cs b/JexusManager.Features.Rewrite/Inbound/InboundRule.cs new file mode 100644 index 00000000..8097573e --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/InboundRule.cs @@ -0,0 +1,227 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.Collections.Generic; + using System.Diagnostics; + + using Microsoft.Web.Administration; + + public class InboundRule : IItem + { + public InboundRule(ConfigurationElement element) + { + Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + this.Element = element; + this.ServerVariables = new List(); + this.Conditions = new List(); + if (element == null) + { + this.Type = 1; + IgnoreCase = true; + this.AppendQueryString = true; + Enabled = true; + return; + } + + CancelChanges(); + } + + public string Name { get; set; } + + public string Input { get; set; } + + public bool Negate { get; set; } + + public string PatternUrl { get; set; } + + public long Type { get; set; } + + public string ActionUrl { get; set; } + + public bool StopProcessing { get; set; } + + public string Flag { get; set; } + + public ConfigurationElement Element { get; set; } + + public bool Enabled { get; set; } + + public RuleSettingsUpdatedEventHandler RuleSettingsUpdated { get; set; } + + public bool IgnoreCase { get; set; } + + public long PatternSyntax { get; set; } + + public bool AppendQueryString { get; set; } + + public bool LogRewrittenUrl { get; set; } + + public int RedirectType { get; set; } + + public uint StatusCode { get; set; } + + public uint SubStatusCode { get; set; } + + public string StatusReason { get; set; } + + public string StatusDescription { get; set; } + + public List ServerVariables { get; } + + public List Conditions { get; } + + public bool Equals(InboundRule other) + { + return Match(other); + } + + public bool Match(InboundRule other) + { + return other != null && other.Name == this.Name; + } + + public bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130425&clcid=0x409"); + return true; + } + + public void CancelChanges() + { + if (Element == null) + { + return; + } + + this.Name = (string)this.Element["name"]; + this.Enabled = (bool)this.Element["enabled"]; + this.PatternSyntax = (long)this.Element["patternSyntax"]; + this.StopProcessing = (bool)this.Element["stopProcessing"]; + ConfigurationElement matchElement = this.Element.ChildElements["match"]; + this.PatternUrl = (string)matchElement["url"]; + this.Negate = (bool)matchElement["negate"]; + ConfigurationElement actionElement = this.Element.ChildElements["action"]; + this.Type = (long)actionElement["type"]; + this.ActionUrl = (string)actionElement["url"]; + this.AppendQueryString = (bool)actionElement["appendQueryString"]; + this.LogRewrittenUrl = (bool)actionElement["logRewrittenUrl"]; + var redirect = (long)actionElement["redirectType"]; + switch (redirect) + { + case 301: + this.RedirectType = 0; + break; + case 302: + this.RedirectType = 1; + break; + case 303: + this.RedirectType = 2; + break; + case 307: + this.RedirectType = 3; + break; + } + + this.StatusCode = (uint)actionElement["statusCode"]; + this.SubStatusCode = (uint)actionElement["subStatusCode"]; + this.StatusReason = (string)actionElement["statusReason"]; + this.StatusDescription = (string)actionElement["statusDescription"]; + + var conditions = this.Element.ChildElements["conditions"]; + this.TrackAllCaptures = (bool)conditions["trackAllCaptures"]; + this.LogicalGrouping = (long)conditions["logicalGrouping"]; + + foreach (ConfigurationElement condition in conditions.GetCollection()) + { + var item = new ConditionItem(condition); + this.Conditions.Add(item); + } + + var variables = this.Element.ChildElements["serverVariables"]; + foreach (ConfigurationElement variable in variables.GetCollection()) + { + var item = new ServerVariableItem(variable); + this.ServerVariables.Add(item); + } + } + + public long LogicalGrouping { get; set; } + + public bool TrackAllCaptures { get; set; } + + public bool ApplyChanges() + { + this.Apply(); + return true; + } + + public void Apply() + { + Element["name"] = Name; + Element["enabled"] = Enabled; + Element["patternSyntax"] = this.PatternSyntax; + Element["stopProcessing"] = this.StopProcessing; + ConfigurationElement matchElement = Element.ChildElements["match"]; + matchElement["url"] = this.PatternUrl; + matchElement["negate"] = this.Negate; + ConfigurationElement actionElement = Element.ChildElements["action"]; + actionElement["type"] = this.Type; + actionElement["url"] = this.ActionUrl; + actionElement["appendQueryString"] = this.AppendQueryString; + actionElement["logRewrittenUrl"] = this.LogRewrittenUrl; + + switch (RedirectType) + { + case 0: + actionElement["redirectType"] = 301L; + break; + case 1: + actionElement["redirectType"] = 302L; + break; + case 2: + actionElement["redirectType"] = 303L; + break; + case 3: + actionElement["redirectType"] = 307L; + break; + } + + actionElement["statusCode"] = this.StatusCode; + actionElement["subStatusCode"] = this.SubStatusCode; + actionElement["statusReason"] = this.StatusReason; + actionElement["statusDescription"] = this.StatusDescription; + + var conditions = Element.ChildElements["conditions"]; + conditions["trackAllCaptures"] = TrackAllCaptures; + conditions["logicalGrouping"] = LogicalGrouping; + var conditionsCollection = conditions.GetCollection(); + conditionsCollection.Clear(); + foreach (var condition in Conditions) + { + condition.AppendTo(conditionsCollection); + } + + var variableCollection = Element.ChildElements["serverVariables"].GetCollection(); + variableCollection.Clear(); + foreach (var variable in ServerVariables) + { + variable.AppendTo(variableCollection); + } + } + + public void AppendTo(ConfigurationElementCollection rulesCollection) + { + Element = rulesCollection.CreateElement(); + Apply(); + rulesCollection.Add(Element); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/InboundRulePage.Designer.cs b/JexusManager.Features.Rewrite/Inbound/InboundRulePage.Designer.cs new file mode 100644 index 00000000..d0b6fd57 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/InboundRulePage.Designer.cs @@ -0,0 +1,1053 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + using MakarovDev.ExpandCollapsePanel; + + partial class InboundRulePage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.advancedFlowLayoutPanel1 = new MakarovDev.ExpandCollapsePanel.AdvancedFlowLayoutPanel(); + this.gbMatch = new MakarovDev.ExpandCollapsePanel.ExpandCollapsePanel(); + this.cbIgnoreCase = new System.Windows.Forms.CheckBox(); + this.btnTest = new System.Windows.Forms.Button(); + this.txtPattern = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.cbType = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); + this.cbMatch = new System.Windows.Forms.ComboBox(); + this.label4 = new System.Windows.Forms.Label(); + this.gbConditions = new MakarovDev.ExpandCollapsePanel.ExpandCollapsePanel(); + this.btnDown = new System.Windows.Forms.Button(); + this.btnUp = new System.Windows.Forms.Button(); + this.btnRemove = new System.Windows.Forms.Button(); + this.btnEdit = new System.Windows.Forms.Button(); + this.lvConditions = new System.Windows.Forms.ListView(); + this.chInput = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chPattern = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.cbTrack = new System.Windows.Forms.CheckBox(); + this.btnAdd = new System.Windows.Forms.Button(); + this.label19 = new System.Windows.Forms.Label(); + this.cbAny = new System.Windows.Forms.ComboBox(); + this.label20 = new System.Windows.Forms.Label(); + this.gbVariables = new MakarovDev.ExpandCollapsePanel.ExpandCollapsePanel(); + this.btnVarRemove = new System.Windows.Forms.Button(); + this.btnVarEdit = new System.Windows.Forms.Button(); + this.btnVarAdd = new System.Windows.Forms.Button(); + this.btnVarUp = new System.Windows.Forms.Button(); + this.btnVarDown = new System.Windows.Forms.Button(); + this.lvVariables = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chReplace = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.groupBox2 = new MakarovDev.ExpandCollapsePanel.ExpandCollapsePanel(); + this.gbRedirect = new System.Windows.Forms.GroupBox(); + this.cbRedirect = new System.Windows.Forms.ComboBox(); + this.label14 = new System.Windows.Forms.Label(); + this.cbAppendRedirect = new System.Windows.Forms.CheckBox(); + this.txtRedirect = new System.Windows.Forms.TextBox(); + this.label9 = new System.Windows.Forms.Label(); + this.gbCustom = new System.Windows.Forms.GroupBox(); + this.txtError = new System.Windows.Forms.TextBox(); + this.txtReason = new System.Windows.Forms.TextBox(); + this.label13 = new System.Windows.Forms.Label(); + this.label12 = new System.Windows.Forms.Label(); + this.txtStatus = new System.Windows.Forms.TextBox(); + this.label11 = new System.Windows.Forms.Label(); + this.txtSubstatus = new System.Windows.Forms.TextBox(); + this.label10 = new System.Windows.Forms.Label(); + this.cbStop = new System.Windows.Forms.CheckBox(); + this.gbRewrite = new System.Windows.Forms.GroupBox(); + this.cbLog = new System.Windows.Forms.CheckBox(); + this.cbAppend = new System.Windows.Forms.CheckBox(); + this.txtUrl = new System.Windows.Forms.TextBox(); + this.label8 = new System.Windows.Forms.Label(); + this.cbAction = new System.Windows.Forms.ComboBox(); + this.label7 = new System.Windows.Forms.Label(); + this.panel2 = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.panel3 = new System.Windows.Forms.Panel(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.advancedFlowLayoutPanel1.SuspendLayout(); + this.gbMatch.SuspendLayout(); + this.gbConditions.SuspendLayout(); + this.gbVariables.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.gbRedirect.SuspendLayout(); + this.gbCustom.SuspendLayout(); + this.gbRewrite.SuspendLayout(); + this.panel2.SuspendLayout(); + this.panel3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.tableLayoutPanel2); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 6; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 1; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.panel3, 0, 0); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 3; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 60F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(580, 600); + this.tableLayoutPanel2.TabIndex = 12; + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.ColumnCount = 1; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Controls.Add(this.advancedFlowLayoutPanel1, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.panel2, 0, 0); + this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 63); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 2; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 60F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Size = new System.Drawing.Size(574, 514); + this.tableLayoutPanel3.TabIndex = 8; + // + // advancedFlowLayoutPanel1 + // + this.advancedFlowLayoutPanel1.AutoScroll = true; + this.advancedFlowLayoutPanel1.Controls.Add(this.gbMatch); + this.advancedFlowLayoutPanel1.Controls.Add(this.gbConditions); + this.advancedFlowLayoutPanel1.Controls.Add(this.gbVariables); + this.advancedFlowLayoutPanel1.Controls.Add(this.groupBox2); + this.advancedFlowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.advancedFlowLayoutPanel1.Location = new System.Drawing.Point(3, 63); + this.advancedFlowLayoutPanel1.Name = "advancedFlowLayoutPanel1"; + this.advancedFlowLayoutPanel1.Size = new System.Drawing.Size(568, 448); + this.advancedFlowLayoutPanel1.TabIndex = 7; + // + // gbMatch + // + this.gbMatch.ButtonStyle = MakarovDev.ExpandCollapsePanel.ExpandCollapseButton.ExpandButtonStyle.Circle; + this.gbMatch.Controls.Add(this.cbIgnoreCase); + this.gbMatch.Controls.Add(this.btnTest); + this.gbMatch.Controls.Add(this.txtPattern); + this.gbMatch.Controls.Add(this.label6); + this.gbMatch.Controls.Add(this.cbType); + this.gbMatch.Controls.Add(this.label5); + this.gbMatch.Controls.Add(this.cbMatch); + this.gbMatch.Controls.Add(this.label4); + this.gbMatch.ExpandedHeight = 182; + this.gbMatch.Location = new System.Drawing.Point(3, 3); + this.gbMatch.Name = "gbMatch"; + this.gbMatch.Size = new System.Drawing.Size(545, 182); + this.gbMatch.TabIndex = 5; + this.gbMatch.Text = "Match URL"; + // + // cbIgnoreCase + // + this.cbIgnoreCase.AutoSize = true; + this.cbIgnoreCase.Location = new System.Drawing.Point(18, 140); + this.cbIgnoreCase.Name = "cbIgnoreCase"; + this.cbIgnoreCase.Size = new System.Drawing.Size(90, 19); + this.cbIgnoreCase.TabIndex = 7; + this.cbIgnoreCase.Text = "Ignore case"; + this.cbIgnoreCase.UseVisualStyleBackColor = true; + this.cbIgnoreCase.CheckedChanged += new System.EventHandler(this.CbAppendRedirectCheckedChanged); + // + // btnTest + // + this.btnTest.Location = new System.Drawing.Point(549, 111); + this.btnTest.Name = "btnTest"; + this.btnTest.Size = new System.Drawing.Size(95, 23); + this.btnTest.TabIndex = 6; + this.btnTest.Text = "Test patterns..."; + this.btnTest.UseVisualStyleBackColor = true; + this.btnTest.Click += new System.EventHandler(this.BtnTestClick); + // + // txtPattern + // + this.txtPattern.Location = new System.Drawing.Point(18, 113); + this.txtPattern.Name = "txtPattern"; + this.txtPattern.Size = new System.Drawing.Size(525, 21); + this.txtPattern.TabIndex = 5; + this.txtPattern.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(15, 85); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(49, 15); + this.label6.TabIndex = 4; + this.label6.Text = "Pattern:"; + // + // cbType + // + this.cbType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbType.FormattingEnabled = true; + this.cbType.Items.AddRange(new object[] { + "Regular Expressions", + "Wildcards", + "Exact Match"}); + this.cbType.Location = new System.Drawing.Point(379, 43); + this.cbType.Name = "cbType"; + this.cbType.Size = new System.Drawing.Size(265, 21); + this.cbType.TabIndex = 3; + this.cbType.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(376, 27); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(42, 15); + this.label5.TabIndex = 2; + this.label5.Text = "Using:"; + // + // cbMatch + // + this.cbMatch.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbMatch.FormattingEnabled = true; + this.cbMatch.Items.AddRange(new object[] { + "Matches the Pattern", + "Does Not Match the Pattern"}); + this.cbMatch.Location = new System.Drawing.Point(18, 43); + this.cbMatch.Name = "cbMatch"; + this.cbMatch.Size = new System.Drawing.Size(265, 21); + this.cbMatch.TabIndex = 1; + this.cbMatch.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(15, 27); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(98, 15); + this.label4.TabIndex = 0; + this.label4.Text = "Requested URL:"; + // + // gbConditions + // + this.gbConditions.ButtonStyle = MakarovDev.ExpandCollapsePanel.ExpandCollapseButton.ExpandButtonStyle.Circle; + this.gbConditions.Controls.Add(this.btnDown); + this.gbConditions.Controls.Add(this.btnUp); + this.gbConditions.Controls.Add(this.btnRemove); + this.gbConditions.Controls.Add(this.btnEdit); + this.gbConditions.Controls.Add(this.lvConditions); + this.gbConditions.Controls.Add(this.cbTrack); + this.gbConditions.Controls.Add(this.btnAdd); + this.gbConditions.Controls.Add(this.label19); + this.gbConditions.Controls.Add(this.cbAny); + this.gbConditions.Controls.Add(this.label20); + this.gbConditions.ExpandedHeight = 288; + this.gbConditions.IsExpanded = false; + this.gbConditions.Location = new System.Drawing.Point(3, 191); + this.gbConditions.Name = "gbConditions"; + this.gbConditions.Size = new System.Drawing.Size(545, 35); + this.gbConditions.TabIndex = 9; + this.gbConditions.Text = "Conditions"; + // + // btnDown + // + this.btnDown.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnDown.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnDown.Location = new System.Drawing.Point(539, 219); + this.btnDown.Name = "btnDown"; + this.btnDown.Size = new System.Drawing.Size(95, 23); + this.btnDown.TabIndex = 12; + this.btnDown.Text = "Move Down"; + this.btnDown.UseVisualStyleBackColor = false; + this.btnDown.Click += new System.EventHandler(this.BtnDownClick); + // + // btnUp + // + this.btnUp.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnUp.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnUp.Location = new System.Drawing.Point(539, 190); + this.btnUp.Name = "btnUp"; + this.btnUp.Size = new System.Drawing.Size(95, 23); + this.btnUp.TabIndex = 11; + this.btnUp.Text = "Move Up"; + this.btnUp.UseVisualStyleBackColor = false; + this.btnUp.Click += new System.EventHandler(this.BtnUpClick); + // + // btnRemove + // + this.btnRemove.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnRemove.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnRemove.Location = new System.Drawing.Point(539, 141); + this.btnRemove.Name = "btnRemove"; + this.btnRemove.Size = new System.Drawing.Size(95, 23); + this.btnRemove.TabIndex = 10; + this.btnRemove.Text = "Remove"; + this.btnRemove.UseVisualStyleBackColor = false; + this.btnRemove.Click += new System.EventHandler(this.BtnRemoveClick); + // + // btnEdit + // + this.btnEdit.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnEdit.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnEdit.Location = new System.Drawing.Point(539, 112); + this.btnEdit.Name = "btnEdit"; + this.btnEdit.Size = new System.Drawing.Size(95, 23); + this.btnEdit.TabIndex = 9; + this.btnEdit.Text = "Edit..."; + this.btnEdit.UseVisualStyleBackColor = false; + this.btnEdit.Click += new System.EventHandler(this.BtnEditClick); + // + // lvConditions + // + this.lvConditions.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.lvConditions.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chInput, + this.chType, + this.chPattern}); + this.lvConditions.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.lvConditions.FullRowSelect = true; + this.lvConditions.HideSelection = false; + this.lvConditions.Location = new System.Drawing.Point(8, 70); + this.lvConditions.MultiSelect = false; + this.lvConditions.Name = "lvConditions"; + this.lvConditions.Size = new System.Drawing.Size(525, 185); + this.lvConditions.TabIndex = 8; + this.lvConditions.UseCompatibleStateImageBehavior = false; + this.lvConditions.View = System.Windows.Forms.View.Details; + // + // chInput + // + this.chInput.Text = "Input"; + this.chInput.Width = 100; + // + // chType + // + this.chType.Text = "Type"; + this.chType.Width = 160; + // + // chPattern + // + this.chPattern.Text = "Pattern"; + this.chPattern.Width = 180; + // + // cbTrack + // + this.cbTrack.AutoSize = true; + this.cbTrack.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.cbTrack.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.cbTrack.Location = new System.Drawing.Point(18, 261); + this.cbTrack.Name = "cbTrack"; + this.cbTrack.Size = new System.Drawing.Size(239, 19); + this.cbTrack.TabIndex = 7; + this.cbTrack.Text = "Track capture groups across conditions"; + this.cbTrack.UseVisualStyleBackColor = false; + // + // btnAdd + // + this.btnAdd.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnAdd.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnAdd.Location = new System.Drawing.Point(539, 83); + this.btnAdd.Name = "btnAdd"; + this.btnAdd.Size = new System.Drawing.Size(95, 23); + this.btnAdd.TabIndex = 6; + this.btnAdd.Text = "Add..."; + this.btnAdd.UseVisualStyleBackColor = false; + this.btnAdd.Click += new System.EventHandler(this.BtnAddClick); + // + // label19 + // + this.label19.AutoSize = true; + this.label19.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.label19.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.label19.Location = new System.Drawing.Point(376, 27); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(42, 15); + this.label19.TabIndex = 2; + this.label19.Text = "Using:"; + // + // cbAny + // + this.cbAny.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.cbAny.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbAny.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.cbAny.FormattingEnabled = true; + this.cbAny.Items.AddRange(new object[] { + "Match All", + "Match Any"}); + this.cbAny.Location = new System.Drawing.Point(18, 43); + this.cbAny.Name = "cbAny"; + this.cbAny.Size = new System.Drawing.Size(131, 21); + this.cbAny.TabIndex = 1; + // + // label20 + // + this.label20.AutoSize = true; + this.label20.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.label20.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.label20.Location = new System.Drawing.Point(15, 27); + this.label20.Name = "label20"; + this.label20.Size = new System.Drawing.Size(102, 15); + this.label20.TabIndex = 0; + this.label20.Text = "Logical grouping:"; + // + // gbVariables + // + this.gbVariables.ButtonStyle = MakarovDev.ExpandCollapsePanel.ExpandCollapseButton.ExpandButtonStyle.Circle; + this.gbVariables.Controls.Add(this.btnVarRemove); + this.gbVariables.Controls.Add(this.btnVarEdit); + this.gbVariables.Controls.Add(this.btnVarAdd); + this.gbVariables.Controls.Add(this.btnVarUp); + this.gbVariables.Controls.Add(this.btnVarDown); + this.gbVariables.Controls.Add(this.lvVariables); + this.gbVariables.ExpandedHeight = 229; + this.gbVariables.IsExpanded = false; + this.gbVariables.Location = new System.Drawing.Point(3, 232); + this.gbVariables.Name = "gbVariables"; + this.gbVariables.Size = new System.Drawing.Size(545, 35); + this.gbVariables.TabIndex = 8; + this.gbVariables.Text = "Server Variables"; + // + // btnVarRemove + // + this.btnVarRemove.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnVarRemove.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnVarRemove.Location = new System.Drawing.Point(539, 99); + this.btnVarRemove.Name = "btnVarRemove"; + this.btnVarRemove.Size = new System.Drawing.Size(95, 23); + this.btnVarRemove.TabIndex = 17; + this.btnVarRemove.Text = "Remove"; + this.btnVarRemove.UseVisualStyleBackColor = false; + this.btnVarRemove.Click += new System.EventHandler(this.BtnVarRemoveClick); + // + // btnVarEdit + // + this.btnVarEdit.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnVarEdit.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnVarEdit.Location = new System.Drawing.Point(539, 70); + this.btnVarEdit.Name = "btnVarEdit"; + this.btnVarEdit.Size = new System.Drawing.Size(95, 23); + this.btnVarEdit.TabIndex = 16; + this.btnVarEdit.Text = "Edit.."; + this.btnVarEdit.UseVisualStyleBackColor = false; + this.btnVarEdit.Click += new System.EventHandler(this.BtnVarEditClick); + // + // btnVarAdd + // + this.btnVarAdd.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnVarAdd.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnVarAdd.Location = new System.Drawing.Point(539, 41); + this.btnVarAdd.Name = "btnVarAdd"; + this.btnVarAdd.Size = new System.Drawing.Size(95, 23); + this.btnVarAdd.TabIndex = 15; + this.btnVarAdd.Text = "Add..."; + this.btnVarAdd.UseVisualStyleBackColor = false; + this.btnVarAdd.Click += new System.EventHandler(this.BtnVarAddClick); + // + // btnVarUp + // + this.btnVarUp.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnVarUp.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnVarUp.Location = new System.Drawing.Point(539, 152); + this.btnVarUp.Name = "btnVarUp"; + this.btnVarUp.Size = new System.Drawing.Size(95, 23); + this.btnVarUp.TabIndex = 14; + this.btnVarUp.Text = "Move Up"; + this.btnVarUp.UseVisualStyleBackColor = false; + this.btnVarUp.Click += new System.EventHandler(this.BtnVarUpClick); + // + // btnVarDown + // + this.btnVarDown.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.btnVarDown.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180))))); + this.btnVarDown.Location = new System.Drawing.Point(539, 181); + this.btnVarDown.Name = "btnVarDown"; + this.btnVarDown.Size = new System.Drawing.Size(95, 23); + this.btnVarDown.TabIndex = 13; + this.btnVarDown.Text = "Move Down"; + this.btnVarDown.UseVisualStyleBackColor = false; + this.btnVarDown.Click += new System.EventHandler(this.BtnVarDownClick); + // + // lvVariables + // + this.lvVariables.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + this.lvVariables.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chValue, + this.chReplace}); + this.lvVariables.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.lvVariables.FullRowSelect = true; + this.lvVariables.HideSelection = false; + this.lvVariables.Location = new System.Drawing.Point(8, 29); + this.lvVariables.MultiSelect = false; + this.lvVariables.Name = "lvVariables"; + this.lvVariables.Size = new System.Drawing.Size(525, 185); + this.lvVariables.TabIndex = 9; + this.lvVariables.UseCompatibleStateImageBehavior = false; + this.lvVariables.View = System.Windows.Forms.View.Details; + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 100; + // + // chValue + // + this.chValue.Text = "Value"; + this.chValue.Width = 160; + // + // chReplace + // + this.chReplace.Text = "Replace"; + this.chReplace.Width = 180; + // + // groupBox2 + // + this.groupBox2.ButtonStyle = MakarovDev.ExpandCollapsePanel.ExpandCollapseButton.ExpandButtonStyle.Circle; + this.groupBox2.Controls.Add(this.gbRedirect); + this.groupBox2.Controls.Add(this.gbCustom); + this.groupBox2.Controls.Add(this.cbStop); + this.groupBox2.Controls.Add(this.gbRewrite); + this.groupBox2.Controls.Add(this.cbAction); + this.groupBox2.Controls.Add(this.label7); + this.groupBox2.ExpandedHeight = 261; + this.groupBox2.Location = new System.Drawing.Point(3, 273); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(545, 261); + this.groupBox2.TabIndex = 6; + this.groupBox2.Text = "Action"; + // + // gbRedirect + // + this.gbRedirect.Controls.Add(this.cbRedirect); + this.gbRedirect.Controls.Add(this.label14); + this.gbRedirect.Controls.Add(this.cbAppendRedirect); + this.gbRedirect.Controls.Add(this.txtRedirect); + this.gbRedirect.Controls.Add(this.label9); + this.gbRedirect.Location = new System.Drawing.Point(18, 70); + this.gbRedirect.Name = "gbRedirect"; + this.gbRedirect.Size = new System.Drawing.Size(626, 176); + this.gbRedirect.TabIndex = 4; + this.gbRedirect.TabStop = false; + this.gbRedirect.Text = "Action Properties"; + // + // cbRedirect + // + this.cbRedirect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbRedirect.FormattingEnabled = true; + this.cbRedirect.Items.AddRange(new object[] { + "Permanent (301)", + "Found (302)", + "See Other (303)", + "Temporary (307)"}); + this.cbRedirect.Location = new System.Drawing.Point(21, 122); + this.cbRedirect.Name = "cbRedirect"; + this.cbRedirect.Size = new System.Drawing.Size(584, 21); + this.cbRedirect.TabIndex = 4; + this.cbRedirect.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(18, 101); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(81, 15); + this.label14.TabIndex = 3; + this.label14.Text = "Redirect type:"; + // + // cbAppendRedirect + // + this.cbAppendRedirect.AutoSize = true; + this.cbAppendRedirect.Checked = true; + this.cbAppendRedirect.CheckState = System.Windows.Forms.CheckState.Checked; + this.cbAppendRedirect.Location = new System.Drawing.Point(21, 72); + this.cbAppendRedirect.Name = "cbAppendRedirect"; + this.cbAppendRedirect.Size = new System.Drawing.Size(134, 19); + this.cbAppendRedirect.TabIndex = 2; + this.cbAppendRedirect.Text = "Append query string"; + this.cbAppendRedirect.UseVisualStyleBackColor = true; + this.cbAppendRedirect.CheckedChanged += new System.EventHandler(this.CbAppendRedirectCheckedChanged); + // + // txtRedirect + // + this.txtRedirect.Location = new System.Drawing.Point(21, 43); + this.txtRedirect.Name = "txtRedirect"; + this.txtRedirect.Size = new System.Drawing.Size(584, 21); + this.txtRedirect.TabIndex = 1; + this.txtRedirect.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(18, 26); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(84, 15); + this.label9.TabIndex = 0; + this.label9.Text = "Redirect URL:"; + // + // gbCustom + // + this.gbCustom.Controls.Add(this.txtError); + this.gbCustom.Controls.Add(this.txtReason); + this.gbCustom.Controls.Add(this.label13); + this.gbCustom.Controls.Add(this.label12); + this.gbCustom.Controls.Add(this.txtStatus); + this.gbCustom.Controls.Add(this.label11); + this.gbCustom.Controls.Add(this.txtSubstatus); + this.gbCustom.Controls.Add(this.label10); + this.gbCustom.Location = new System.Drawing.Point(18, 70); + this.gbCustom.Name = "gbCustom"; + this.gbCustom.Size = new System.Drawing.Size(626, 197); + this.gbCustom.TabIndex = 4; + this.gbCustom.TabStop = false; + this.gbCustom.Text = "Action Properties"; + // + // txtError + // + this.txtError.Location = new System.Drawing.Point(21, 164); + this.txtError.Name = "txtError"; + this.txtError.Size = new System.Drawing.Size(584, 21); + this.txtError.TabIndex = 9; + this.txtError.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // txtReason + // + this.txtReason.Location = new System.Drawing.Point(21, 117); + this.txtReason.Name = "txtReason"; + this.txtReason.Size = new System.Drawing.Size(584, 21); + this.txtReason.TabIndex = 8; + this.txtReason.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(18, 143); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(100, 15); + this.label13.TabIndex = 7; + this.label13.Text = "Error description:"; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(18, 101); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(53, 15); + this.label12.TabIndex = 6; + this.label12.Text = "Reason:"; + // + // txtStatus + // + this.txtStatus.Location = new System.Drawing.Point(133, 23); + this.txtStatus.Name = "txtStatus"; + this.txtStatus.Size = new System.Drawing.Size(472, 21); + this.txtStatus.TabIndex = 5; + this.txtStatus.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(18, 64); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(94, 15); + this.label11.TabIndex = 4; + this.label11.Text = "Substatus code:"; + // + // txtSubstatus + // + this.txtSubstatus.Location = new System.Drawing.Point(133, 61); + this.txtSubstatus.Name = "txtSubstatus"; + this.txtSubstatus.Size = new System.Drawing.Size(472, 21); + this.txtSubstatus.TabIndex = 1; + this.txtSubstatus.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(18, 26); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(74, 15); + this.label10.TabIndex = 0; + this.label10.Text = "Status code:"; + // + // cbStop + // + this.cbStop.AutoSize = true; + this.cbStop.Location = new System.Drawing.Point(18, 229); + this.cbStop.Name = "cbStop"; + this.cbStop.Size = new System.Drawing.Size(224, 19); + this.cbStop.TabIndex = 3; + this.cbStop.Text = "Stop processing of subsequent rules"; + this.cbStop.UseVisualStyleBackColor = true; + this.cbStop.CheckedChanged += new System.EventHandler(this.CbAppendRedirectCheckedChanged); + // + // gbRewrite + // + this.gbRewrite.Controls.Add(this.cbLog); + this.gbRewrite.Controls.Add(this.cbAppend); + this.gbRewrite.Controls.Add(this.txtUrl); + this.gbRewrite.Controls.Add(this.label8); + this.gbRewrite.Location = new System.Drawing.Point(18, 70); + this.gbRewrite.Name = "gbRewrite"; + this.gbRewrite.Size = new System.Drawing.Size(626, 143); + this.gbRewrite.TabIndex = 2; + this.gbRewrite.TabStop = false; + this.gbRewrite.Text = "Action Properties"; + // + // cbLog + // + this.cbLog.AutoSize = true; + this.cbLog.Location = new System.Drawing.Point(21, 108); + this.cbLog.Name = "cbLog"; + this.cbLog.Size = new System.Drawing.Size(125, 19); + this.cbLog.TabIndex = 3; + this.cbLog.Text = "Log rewritten URL"; + this.cbLog.UseVisualStyleBackColor = true; + this.cbLog.CheckedChanged += new System.EventHandler(this.CbAppendRedirectCheckedChanged); + // + // cbAppend + // + this.cbAppend.AutoSize = true; + this.cbAppend.Checked = true; + this.cbAppend.CheckState = System.Windows.Forms.CheckState.Checked; + this.cbAppend.Location = new System.Drawing.Point(21, 72); + this.cbAppend.Name = "cbAppend"; + this.cbAppend.Size = new System.Drawing.Size(134, 19); + this.cbAppend.TabIndex = 2; + this.cbAppend.Text = "Append query string"; + this.cbAppend.UseVisualStyleBackColor = true; + this.cbAppend.CheckedChanged += new System.EventHandler(this.CbAppendRedirectCheckedChanged); + // + // txtUrl + // + this.txtUrl.Location = new System.Drawing.Point(21, 43); + this.txtUrl.Name = "txtUrl"; + this.txtUrl.Size = new System.Drawing.Size(584, 21); + this.txtUrl.TabIndex = 1; + this.txtUrl.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(18, 26); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(80, 15); + this.label8.TabIndex = 0; + this.label8.Text = "Rewrite URL:"; + // + // cbAction + // + this.cbAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbAction.FormattingEnabled = true; + this.cbAction.Items.AddRange(new object[] { + "None", + "Rewrite", + "Redirect", + "Custom Response", + "Abort Request"}); + this.cbAction.Location = new System.Drawing.Point(18, 42); + this.cbAction.Name = "cbAction"; + this.cbAction.Size = new System.Drawing.Size(140, 21); + this.cbAction.TabIndex = 1; + this.cbAction.SelectedIndexChanged += new System.EventHandler(this.CbActionSelectedIndexChanged); + this.cbAction.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(15, 26); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(68, 15); + this.label7.TabIndex = 0; + this.label7.Text = "Action type:"; + // + // panel2 + // + this.panel2.Controls.Add(this.label2); + this.panel2.Controls.Add(this.txtName); + this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel2.Location = new System.Drawing.Point(3, 3); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(568, 54); + this.panel2.TabIndex = 11; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(18, 11); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(38, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Name:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(21, 27); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(665, 20); + this.txtName.TabIndex = 4; + this.txtName.TextChanged += new System.EventHandler(this.TxtNameTextChanged); + // + // panel3 + // + this.panel3.Controls.Add(this.pictureBox1); + this.panel3.Controls.Add(this.label3); + this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel3.Location = new System.Drawing.Point(3, 3); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(574, 54); + this.panel3.TabIndex = 8; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(182, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Edit Inbound Rule"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // InboundRulePage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "InboundRulePage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.advancedFlowLayoutPanel1.ResumeLayout(false); + this.gbMatch.ResumeLayout(false); + this.gbMatch.PerformLayout(); + this.gbConditions.ResumeLayout(false); + this.gbConditions.PerformLayout(); + this.gbVariables.ResumeLayout(false); + this.gbVariables.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.gbRedirect.ResumeLayout(false); + this.gbRedirect.PerformLayout(); + this.gbCustom.ResumeLayout(false); + this.gbCustom.PerformLayout(); + this.gbRewrite.ResumeLayout(false); + this.gbRewrite.PerformLayout(); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.panel3.ResumeLayout(false); + this.panel3.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private SplitContainer splitContainer1; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private PictureBox pictureBox1; + private ContextMenuStrip cmsActionPanel; + private TableLayoutPanel tableLayoutPanel2; + private TableLayoutPanel tableLayoutPanel3; + private AdvancedFlowLayoutPanel advancedFlowLayoutPanel1; + private ExpandCollapsePanel gbMatch; + private CheckBox cbIgnoreCase; + private Button btnTest; + private TextBox txtPattern; + private Label label6; + private ComboBox cbType; + private Label label5; + private ComboBox cbMatch; + private Label label4; + private ExpandCollapsePanel gbConditions; + private Button btnDown; + private Button btnUp; + private Button btnRemove; + private Button btnEdit; + private ListView lvConditions; + private ColumnHeader chInput; + private ColumnHeader chType; + private ColumnHeader chPattern; + private CheckBox cbTrack; + private Button btnAdd; + private Label label19; + private ComboBox cbAny; + private Label label20; + private ExpandCollapsePanel gbVariables; + private Button btnVarRemove; + private Button btnVarEdit; + private Button btnVarAdd; + private Button btnVarUp; + private Button btnVarDown; + private ListView lvVariables; + private ColumnHeader chName; + private ColumnHeader chValue; + private ColumnHeader chReplace; + private ExpandCollapsePanel groupBox2; + private GroupBox gbRedirect; + private ComboBox cbRedirect; + private Label label14; + private CheckBox cbAppendRedirect; + private TextBox txtRedirect; + private Label label9; + private GroupBox gbCustom; + private TextBox txtError; + private TextBox txtReason; + private Label label13; + private Label label12; + private TextBox txtStatus; + private Label label11; + private TextBox txtSubstatus; + private Label label10; + private CheckBox cbStop; + private GroupBox gbRewrite; + private CheckBox cbLog; + private CheckBox cbAppend; + private TextBox txtUrl; + private Label label8; + private ComboBox cbAction; + private Label label7; + private Panel panel2; + private Label label2; + private TextBox txtName; + private Panel panel3; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/InboundRulePage.cs b/JexusManager.Features.Rewrite/Inbound/InboundRulePage.cs new file mode 100644 index 00000000..0d62b5d8 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/InboundRulePage.cs @@ -0,0 +1,543 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class InboundRulePage : ModuleDialogPage, IModuleChildPage + { + private sealed class PageTaskList : DefaultTaskList + { + private readonly InboundRulePage _owner; + + public PageTaskList(InboundRulePage owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + return new TaskItem[] + { + GetBackTaskItem("Back", "Back to Rules"), + new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage(), + HelpTaskItem + }; + } + + [Obfuscation(Exclude = true)] + public void ShowHelp() + { + _owner.ShowHelp(); + } + + [Obfuscation(Exclude = true)] + public void Back() + { + _owner.Back(); + } + } + + private class ServerVariableListViewItem : ListViewItem + { + private readonly ListViewSubItem _value; + + private readonly ListViewSubItem _replace; + + public ServerVariableListViewItem(ServerVariableItem variable) + : base(variable.Name) + { + _value = new ListViewSubItem(this, variable.Value); + this.SubItems.Add(_value); + _replace = new ListViewSubItem(this, variable.Replace.ToString()); + this.SubItems.Add(_replace); + this.Item = variable; + } + + public ServerVariableItem Item { get; } + + public void Update() + { + this.Text = this.Item.Name; + _value.Text = this.Item.Value; + _replace.Text = this.Item.Replace.ToString(); + } + } + + private bool _hasChanges; + + private bool _initialized; + + private TaskList _taskList; + + private InboundFeature _feature; + + public InboundRulePage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + // TODO: pictureBox1.Image + var info = (Tuple)navigationData; + _feature = info.Item1; + this.Rule = info.Item2; + txtName.ReadOnly = this.Rule != null; + if (this.Rule != null) + { + // TODO: invoke RuleSettingsUpdate somewhere. + this.Rule.RuleSettingsUpdated = this.Refresh; + } + + if (this.Rule == null) + { + this.Rule = new InboundRule(null); + Rule.Enabled = true; + } + + this.Refresh(); + } + + protected override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130425&clcid=0x409"); + return true; + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void Back() + { + if (_hasChanges) + { + var result = ShowMessage( + "The changes you have made will be lost. Do you want to save changes?", + MessageBoxButtons.YesNoCancel, + MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + if (result == DialogResult.Cancel) + { + return; + } + + if (result == DialogResult.Yes) + { + this.ApplyChanges(); + } + } + + var service = (INavigationService)this.GetService(typeof(INavigationService)); + service?.NavigateBack(1); + _feature.SelectedItem = this.Rule; + _feature.Refresh(); + } + + private void TxtNameTextChanged(object sender, EventArgs e) + { + this.InformChanges(); + } + + public InboundRule Rule { get; set; } + + private void BtnTestClick(object sender, EventArgs e) + { + var dialog = new RegexTestDialog(this.Module, txtPattern.Text, cbIgnoreCase.Checked, false); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + txtPattern.Text = dialog.Pattern; + cbIgnoreCase.Checked = dialog.IgnoreCase; + } + + protected override bool ApplyChanges() + { + uint status = 0; + uint substatus = 0; + if (cbAction.SelectedIndex == 3) + { + if (!uint.TryParse(txtStatus.Text, out status) || status < 100 || status > 999 + || (status > 299 && status < 308)) + { + ShowMessage( + string.Format( + "The specific code '{0}' is not valid. The status code must be between 100 and 999 and cannot contain values between 300 and 307.", + txtStatus.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + return false; + } + + if (!uint.TryParse(txtSubstatus.Text, out substatus) || substatus > 999) + { + ShowMessage( + string.Format( + "The specified code '{0}' is not valid. Sub-status codes must be between 0 and 999.", + txtSubstatus.Text), + MessageBoxButtons.OK, + MessageBoxIcon.Warning, + MessageBoxDefaultButton.Button1); + return false; + } + } + + Rule.Name = txtName.Text; + Rule.PatternSyntax = cbType.SelectedIndex; + Rule.StopProcessing = cbStop.Checked; + Rule.PatternUrl = txtPattern.Text; + Rule.IgnoreCase = cbIgnoreCase.Checked; + Rule.Negate = cbMatch.SelectedIndex == 1; + Rule.Type = cbAction.SelectedIndex; + Rule.ActionUrl = cbAction.SelectedIndex == 1 ? txtUrl.Text : txtRedirect.Text; + Rule.AppendQueryString = cbAction.SelectedIndex == 1 ? cbAppend.Checked : cbAppendRedirect.Checked; + Rule.LogRewrittenUrl = cbLog.Checked; + Rule.RedirectType = cbRedirect.SelectedIndex; + Rule.StatusCode = status; + Rule.SubStatusCode = substatus; + Rule.StatusReason = txtReason.Text; + Rule.StatusDescription = txtError.Text; + + if (!this.Rule.ApplyChanges()) + { + return false; + } + + this.ClearChanges(); + txtName.ReadOnly = true; + _feature.AddItem(this.Rule); + return true; + } + + protected override void CancelChanges() + { + this.Rule.CancelChanges(); + this.ClearChanges(); + } + + protected override bool HasChanges + { + get + { + return _hasChanges; + } + } + + protected override bool CanApplyChanges + { + get + { + return !string.IsNullOrWhiteSpace(txtName.Text) && !string.IsNullOrWhiteSpace(txtPattern.Text) + && !string.IsNullOrWhiteSpace(txtUrl.Text); + } + } + + private void InformChanges() + { + if (!_initialized) + { + return; + } + + _hasChanges = true; + this.Refresh(); + } + + private void ClearChanges() + { + _hasChanges = false; + _initialized = false; + this.Refresh(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + protected override void OnRefresh() + { + if (!_hasChanges) + { + cbAction.SelectedIndex = 1; + cbAppend.Checked = true; + cbAppendRedirect.Checked = true; + cbIgnoreCase.Checked = true; + cbMatch.SelectedIndex = 0; + cbStop.Checked = false; + cbType.SelectedIndex = 0; + cbLog.Checked = false; + cbRedirect.SelectedIndex = 0; + cbAny.SelectedIndex = 0; + if (this.Rule != null) + { + txtName.Text = this.Rule.Name; + cbAction.SelectedIndex = (int)this.Rule.Type; + cbAppend.Checked = cbAppendRedirect.Checked = this.Rule.AppendQueryString; + cbIgnoreCase.Checked = this.Rule.IgnoreCase; + txtRedirect.Text = txtUrl.Text = this.Rule.ActionUrl; + cbMatch.SelectedIndex = this.Rule.Negate ? 1 : 0; + cbStop.Checked = this.Rule.StopProcessing; + cbType.SelectedIndex = (int)this.Rule.PatternSyntax; + txtPattern.Text = this.Rule.PatternUrl; + cbLog.Checked = this.Rule.LogRewrittenUrl; + cbRedirect.SelectedIndex = this.Rule.RedirectType; + txtStatus.Text = this.Rule.StatusCode.ToString(); + txtSubstatus.Text = this.Rule.SubStatusCode.ToString(); + txtReason.Text = this.Rule.StatusReason; + txtError.Text = this.Rule.StatusDescription; + + lvVariables.Items.Clear(); + foreach (var variable in this.Rule.ServerVariables) + { + lvVariables.Items.Add(new ServerVariableListViewItem(variable)); + gbVariables.IsExpanded = true; + } + + lvConditions.Items.Clear(); + foreach (var condition in this.Rule.Conditions) + { + lvConditions.Items.Add(new ConditionListViewItem(condition)); + gbConditions.IsExpanded = true; + } + + cbAny.SelectedIndex = (int)this.Rule.LogicalGrouping; + cbTrack.Checked = this.Rule.TrackAllCaptures; + } + + _initialized = true; + } + + this.CbActionSelectedIndexChanged(null, null); + cbStop.Visible = cbAction.SelectedIndex == 0 || cbAction.SelectedIndex == 1; + this.UpdateVariablesButtons(); + this.UpdateConditionsButtons(); + + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + } + + public IModulePage ParentPage { get; set; } + + private void CbActionSelectedIndexChanged(object sender, EventArgs e) + { + gbRewrite.Visible = cbAction.SelectedIndex == 1; + gbRedirect.Visible = cbAction.SelectedIndex == 2; + gbCustom.Visible = cbAction.SelectedIndex == 3; + } + + private void CbAppendRedirectCheckedChanged(object sender, EventArgs e) + { + this.InformChanges(); + } + + private void BtnAddClick(object sender, EventArgs e) + { + var dialog = new AddConditionDialog(this.ServiceProvider, null); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + this.Rule.Conditions.Add(newItem); + var listViewItem = new ConditionListViewItem(newItem); + lvConditions.Items.Add(listViewItem); + listViewItem.Selected = true; + this.InformChanges(); + } + + private void BtnRemoveClick(object sender, EventArgs e) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if (dialog.ShowMessage( + "Are you sure that you want to remove the selected condition?", + "Confirm Remove", + MessageBoxButtons.YesNoCancel, + MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1) != DialogResult.Yes) + { + return; + } + + var listViewItem = ((ConditionListViewItem)lvConditions.SelectedItems[0]); + listViewItem.Remove(); + var item = listViewItem.Item; + this.Rule.Conditions.Remove(item); + this.InformChanges(); + } + + private void BtnEditClick(object sender, EventArgs e) + { + var listViewItem = ((ConditionListViewItem)lvConditions.SelectedItems[0]); + var dialog = new AddConditionDialog(this.ServiceProvider, listViewItem.Item); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + listViewItem.Update(); + this.InformChanges(); + } + + private void BtnDownClick(object sender, EventArgs e) + { + var listViewItem = ((ConditionListViewItem)lvConditions.SelectedItems[0]); + var index = listViewItem.Index; + + var item = listViewItem.Item; + this.Rule.Conditions.RemoveAt(index); + listViewItem.Remove(); + + this.Rule.Conditions.Insert(index + 1, item); + lvConditions.Items.Insert(index + 1, listViewItem); + listViewItem.Selected = true; + this.InformChanges(); + } + + private void BtnUpClick(object sender, EventArgs e) + { + var listViewItem = ((ConditionListViewItem)lvConditions.SelectedItems[0]); + var index = listViewItem.Index; + + var item = listViewItem.Item; + this.Rule.Conditions.RemoveAt(index); + listViewItem.Remove(); + + this.Rule.Conditions.Insert(index - 1, item); + lvConditions.Items.Insert(index - 1, listViewItem); + listViewItem.Selected = true; + this.InformChanges(); + } + + private void UpdateConditionsButtons() + { + var hasSelection = lvConditions.SelectedItems.Count > 0; + btnRemove.Enabled = btnEdit.Enabled = hasSelection; + btnDown.Enabled = hasSelection + && lvConditions.SelectedItems[0].Index < lvConditions.Items.Count - 1; + btnUp.Enabled = hasSelection && lvConditions.SelectedItems[0].Index > 0; + } + + private void BtnVarAddClick(object sender, EventArgs e) + { + var dialog = new AddServerVariableDialog(this.ServiceProvider, null); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + this.Rule.ServerVariables.Add(newItem); + var listViewItem = new ServerVariableListViewItem(newItem); + lvVariables.Items.Add(listViewItem); + listViewItem.Selected = true; + this.InformChanges(); + } + + private void BtnVarRemoveClick(object sender, EventArgs e) + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if (dialog.ShowMessage( + "Are you sure that you want to remove the selected entry?", + "Confirm Remove", + MessageBoxButtons.YesNoCancel, + MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1) != DialogResult.Yes) + { + return; + } + + var listViewItem = ((ServerVariableListViewItem)lvVariables.SelectedItems[0]); + listViewItem.Remove(); + var item = listViewItem.Item; + this.Rule.ServerVariables.Remove(item); + this.InformChanges(); + } + + private void BtnVarEditClick(object sender, EventArgs e) + { + var listViewItem = ((ServerVariableListViewItem)lvVariables.SelectedItems[0]); + var dialog = new AddServerVariableDialog(this.ServiceProvider, listViewItem.Item); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + listViewItem.Update(); + this.InformChanges(); + } + + private void BtnVarDownClick(object sender, EventArgs e) + { + var listViewItem = ((ServerVariableListViewItem)lvVariables.SelectedItems[0]); + var index = listViewItem.Index; + + var item = listViewItem.Item; + this.Rule.ServerVariables.RemoveAt(index); + listViewItem.Remove(); + + this.Rule.ServerVariables.Insert(index + 1, item); + lvVariables.Items.Insert(index + 1, listViewItem); + listViewItem.Selected = true; + this.InformChanges(); + } + + private void BtnVarUpClick(object sender, EventArgs e) + { + var listViewItem = ((ServerVariableListViewItem)lvVariables.SelectedItems[0]); + var index = listViewItem.Index; + + var item = listViewItem.Item; + this.Rule.ServerVariables.RemoveAt(index); + listViewItem.Remove(); + + this.Rule.ServerVariables.Insert(index - 1, item); + lvVariables.Items.Insert(index - 1, listViewItem); + listViewItem.Selected = true; + this.InformChanges(); + } + + private void UpdateVariablesButtons() + { + var hasSelection = lvVariables.SelectedItems.Count > 0; + btnVarRemove.Enabled = btnVarEdit.Enabled = hasSelection; + btnVarDown.Enabled = hasSelection + && lvVariables.SelectedItems[0].Index < lvVariables.Items.Count - 1; + btnVarUp.Enabled = hasSelection && lvVariables.SelectedItems[0].Index > 0; + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/InboundRulePage.resx b/JexusManager.Features.Rewrite/Inbound/InboundRulePage.resx new file mode 100644 index 00000000..950be5d2 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/InboundRulePage.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/MapItem.cs b/JexusManager.Features.Rewrite/Inbound/MapItem.cs new file mode 100644 index 00000000..4beb2ca5 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapItem.cs @@ -0,0 +1,70 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.Collections.Generic; + + using Microsoft.Web.Administration; + + internal class MapItem : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(MapItem other) + { + return other != null && other.Name == Name; + } + + private readonly MapsFeature _feature; + + public string Name { get; internal set; } + + public string Flag { get; set; } + + public string DefaultValue { get; set; } + + public MapSettingsUpdatedEventHandler MapSettingsUpdated { get; set; } + + public List Items { get; private set; } + + public MapRule SelectedItem { get; set; } + + public MapItem(ConfigurationElement element, MapsFeature feature) + { + this.Element = element; + _feature = feature; + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + this.Name = element == null ? string.Empty : (string)element["name"]; + this.Items = new List(); + if (element != null) + { + var collection = element.GetCollection(); + foreach (ConfigurationElement rule in collection) + { + this.Items.Add(new MapRule(rule, _feature)); + } + } + } + + public void Apply() + { + Element["name"] = Name; + Element["defaultValue"] = DefaultValue; + Element["ignoreCase"] = IgnoreCase; + } + + internal protected void OnRewriteSettingsSaved() + { + this.MapSettingsUpdated?.Invoke(); + } + + public bool IgnoreCase { get; set; } + + public bool Equals(MapItem other) + { + return Match(other) && other.DefaultValue == DefaultValue && other.IgnoreCase == IgnoreCase; + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapPage.Designer.cs b/JexusManager.Features.Rewrite/Inbound/MapPage.Designer.cs new file mode 100644 index 00000000..51b19490 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapPage.Designer.cs @@ -0,0 +1,334 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class MapPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.panel2 = new System.Windows.Forms.Panel(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.panel3 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chOriginal = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chNew = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.panel4 = new System.Windows.Forms.Panel(); + this.txtName = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.panel3.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + this.panel4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 7; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.tableLayoutPanel2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 9; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 1; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.panel3, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.panel4, 0, 0); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 3; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 60F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(560, 540); + this.tableLayoutPanel2.TabIndex = 10; + // + // panel3 + // + this.panel3.Controls.Add(this.listView1); + this.panel3.Controls.Add(this.toolStrip2); + this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel3.Location = new System.Drawing.Point(3, 63); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(554, 454); + this.panel3.TabIndex = 10; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chOriginal, + this.chNew}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 25); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(554, 429); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + this.listView1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ListView1KeyDown); + // + // chOriginal + // + this.chOriginal.Text = "Original Value"; + this.chOriginal.Width = 200; + // + // chNew + // + this.chNew.Text = "New Value"; + this.chNew.Width = 200; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel3, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 0); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(554, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel3 + // + this.toolStripLabel3.Name = "toolStripLabel3"; + this.toolStripLabel3.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel3.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Category", + "Area"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // panel4 + // + this.panel4.Controls.Add(this.txtName); + this.panel4.Controls.Add(this.label2); + this.panel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel4.Location = new System.Drawing.Point(3, 3); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(554, 54); + this.panel4.TabIndex = 11; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(16, 26); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(177, 20); + this.txtName.TabIndex = 1; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 10); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(98, 13); + this.label2.TabIndex = 0; + this.label2.Text = "Rewrite map name:"; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 8; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(175, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Edit Rewrite Map"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // MapPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "MapPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.panel3.ResumeLayout(false); + this.panel3.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + this.panel4.ResumeLayout(false); + this.panel4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private ContextMenuStrip cmsActionPanel; + private SplitContainer splitContainer1; + private Panel panel2; + private ListView listView1; + private ColumnHeader chOriginal; + private ColumnHeader chNew; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel3; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private TableLayoutPanel tableLayoutPanel2; + private Panel panel3; + private Panel panel4; + private TextBox txtName; + private Label label2; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapPage.cs b/JexusManager.Features.Rewrite/Inbound/MapPage.cs new file mode 100644 index 00000000..72101947 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapPage.cs @@ -0,0 +1,250 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class MapPage : ModuleListPage, IModuleChildPage + { + private sealed class PageTaskList : DefaultTaskList + { + private readonly MapPage _owner; + + public PageTaskList(MapPage owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add Mapping Entry...", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Set", "Edit Map Settings...", string.Empty).SetUsage()); + if (_owner.Map.SelectedItem != null) + { + //result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit Mapping Entry...", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(GetBackTaskItem("BackMore", "Back to Rewrite Maps")); + result.Add(GetBackTaskItem("Back", "Back to Rules")); + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(HelpTaskItem); + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void ShowHelp() + { + _owner.ShowHelp(); + } + + [Obfuscation(Exclude = true)] + public void Back() + { + _owner.Back(); + } + + [Obfuscation(Exclude = true)] + public void BackMore() + { + _owner.BackMore(); + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + + [Obfuscation(Exclude = true)] + public void Set() + { + _owner.Set(); + } + } + + private sealed class MapListViewItem : ListViewItem + { + public MapRule Item { get; } + private readonly MapPage _page; + + public MapListViewItem(MapRule item, MapPage page) + : base(item.Original) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.New)); + } + } + + private TaskList _taskList; + private MapsFeature _feature; + + public MapPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var info = (Tuple)navigationData; + + // TODO: pictureBox1.Image = service.Scope.GetImage(); + + _feature = info.Item1; + this.Map = info.Item2; + txtName.ReadOnly = this.Map != null; + if (this.Map != null) + { + this.Map.MapSettingsUpdated = this.InitializeListPage; + txtName.Text = this.Map.Name; + } + + this.Map?.OnRewriteSettingsSaved(); + } + + public MapItem Map { get; set; } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in this.Map.Items) + { + listView1.Items.Add(new MapListViewItem(file, this)); + } + + if (this.Map.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (MapListViewItem item in listView1.Items) + { + if (item.Item == this.Map.SelectedItem) + { + item.Selected = true; + } + } + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + this.Map.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((MapListViewItem)listView1.SelectedItems[0]).Item + : null; + this.Refresh(); + } + + private void ListView1KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Delete) + { + _feature.Remove(); + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + protected override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130425&clcid=0x409"); + return true; + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void Back() + { + var service = (INavigationService)GetService(typeof(INavigationService)); + service?.NavigateBack(2); + _feature.SelectedItem = this.Map; + _feature.OnRewriteSettingsSaved(); + } + + private void BackMore() + { + var service = (INavigationService)GetService(typeof(INavigationService)); + service?.NavigateBack(1); + } + + public void Add() + { + _feature.AddRule(); + } + + public void Remove() + { + _feature.RemoveRule(); + } + + private void Edit() + { + _feature.EditRule(); + } + + private void Set() + { + _feature.Set(); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + public IModulePage ParentPage { get; set; } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapPage.resx b/JexusManager.Features.Rewrite/Inbound/MapPage.resx new file mode 100644 index 00000000..2e9f734f --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 556, 56 + + + 661, 56 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/MapRule.cs b/JexusManager.Features.Rewrite/Inbound/MapRule.cs new file mode 100644 index 00000000..91d380c6 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapRule.cs @@ -0,0 +1,50 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite.Inbound +{ + using Microsoft.Web.Administration; + + internal class MapRule : IItem + { + public ConfigurationElement Element { get; set; } + + public bool Match(MapRule other) + { + return other != null && other.Original == Original; + } + + private MapsFeature _feature; + + public MapRule(ConfigurationElement element, MapsFeature feature) + { + this.Element = element; + _feature = feature; + if (element != null) + { + this.Original = (string)element["key"]; + this.New = (string)element["value"]; + } + + this.Flag = element == null || element.IsLocallyStored ? "Local" : "Inherited"; + } + + public string Original { get; set; } + + public string New { get; set; } + + public string Flag { get; set; } + + public void Apply() + { + Element["key"] = Original; + Element["value"] = New; + } + + public bool Equals(MapRule other) + { + return Match(other) && other.New == New; + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.Designer.cs b/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.Designer.cs new file mode 100644 index 00000000..33b1572e --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.Designer.cs @@ -0,0 +1,136 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class MapSettingsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.cbIgnore = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.txtDefaultValue = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(216, 176); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 0; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(312, 176); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // cbIgnore + // + this.cbIgnore.AutoSize = true; + this.cbIgnore.Location = new System.Drawing.Point(15, 129); + this.cbIgnore.Name = "cbIgnore"; + this.cbIgnore.Size = new System.Drawing.Size(218, 17); + this.cbIgnore.TabIndex = 2; + this.cbIgnore.Text = "Ignore case when performing key lookup"; + this.cbIgnore.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(98, 13); + this.label1.TabIndex = 3; + this.label1.Text = "Rewrite map name:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 69); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(282, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Default value to use when the key is not found in the map:"; + // + // txtName + // + this.txtName.Location = new System.Drawing.Point(15, 25); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(392, 20); + this.txtName.TabIndex = 5; + // + // txtDefaultValue + // + this.txtDefaultValue.Location = new System.Drawing.Point(15, 85); + this.txtDefaultValue.Name = "txtDefaultValue"; + this.txtDefaultValue.Size = new System.Drawing.Size(392, 20); + this.txtDefaultValue.TabIndex = 6; + // + // MapSettingsDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(419, 211); + this.Controls.Add(this.txtDefaultValue); + this.Controls.Add(this.txtName); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.cbIgnore); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Name = "MapSettingsDialog"; + this.Text = "Edit Rewrite Map"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.MapSettingsDialogHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnOK; + private Button btnCancel; + private CheckBox cbIgnore; + private Label label1; + private Label label2; + private TextBox txtName; + private TextBox txtDefaultValue; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.cs b/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.cs new file mode 100644 index 00000000..c53323b8 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.cs @@ -0,0 +1,55 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class MapSettingsDialog : DialogForm + { + public MapSettingsDialog(IServiceProvider serviceProvider, MapItem item) + : base(serviceProvider) + { + InitializeComponent(); + if (item != null) + { + txtName.Text = item.Name; + txtName.ReadOnly = true; + txtDefaultValue.Text = item.DefaultValue; + cbIgnore.Checked = item.IgnoreCase; + } + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + item.Name = txtName.Text; + item.DefaultValue = txtDefaultValue.Text; + item.IgnoreCase = cbIgnore.Checked; + item.Apply(); + DialogResult = DialogResult.OK; + })); + } + + private void MapSettingsDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130406&clcid=0x409"); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.resx b/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapSettingsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/MapSettingsUpdatedEventHandler.cs b/JexusManager.Features.Rewrite/Inbound/MapSettingsUpdatedEventHandler.cs new file mode 100644 index 00000000..e8fa61cb --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapSettingsUpdatedEventHandler.cs @@ -0,0 +1,8 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite.Inbound +{ + internal delegate void MapSettingsUpdatedEventHandler(); +} \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/MapsFeature.cs b/JexusManager.Features.Rewrite/Inbound/MapsFeature.cs new file mode 100644 index 00000000..01825d95 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapsFeature.cs @@ -0,0 +1,303 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using System.Resources; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Administration; + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + using Module = Microsoft.Web.Management.Client.Module; + + /// + /// Description of DefaultDocumentFeature. + /// + internal class MapsFeature + { + private sealed class FeatureTaskList : DefaultTaskList + { + private readonly MapsFeature _owner; + + public FeatureTaskList(MapsFeature owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + var result = new ArrayList(); + result.Add(new MethodTaskItem("Add", "Add Rewrite Map...", string.Empty).SetUsage()); + if (_owner.SelectedItem != null) + { + result.Add(new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage()); + result.Add(new MethodTaskItem("Edit", "Edit Rewrite Map", string.Empty).SetUsage()); + result.Add(RemoveTaskItem); + } + + return result.ToArray(typeof(TaskItem)) as TaskItem[]; + } + + [Obfuscation(Exclude = true)] + public void Add() + { + _owner.Add(); + } + + [Obfuscation(Exclude = true)] + public override void Remove() + { + _owner.Remove(); + } + + [Obfuscation(Exclude = true)] + public void Edit() + { + _owner.Edit(); + } + } + + public MapsFeature(Module module) + { + this.Module = module; + } + + protected static readonly Version FxVersion10 = new Version("1.0"); + protected static readonly Version FxVersion11 = new Version("1.1"); + protected static readonly Version FxVersion20 = new Version("2.0"); + protected static readonly Version FxVersionNotRequired = new Version(); + private FeatureTaskList _taskList; + + protected void DisplayErrorMessage(Exception ex, ResourceManager resourceManager) + { + var service = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + service.ShowError(ex, resourceManager.GetString("General"), string.Empty, false); + } + + protected object GetService(Type type) + { + return (this.Module as IServiceProvider).GetService(type); + } + + public TaskList GetTaskList() + { + return _taskList ?? (_taskList = new FeatureTaskList(this)); + } + + public void Load() + { + this.Items = new List(); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var rulesSection = service.GetSection("system.webServer/rewrite/rewriteMaps"); + ConfigurationElementCollection rulesCollection = rulesSection.GetCollection(); + foreach (ConfigurationElement ruleElement in rulesCollection) + { + var node = new MapItem(ruleElement, this); + this.Items.Add(node); + } + + this.OnRewriteSettingsSaved(); + } + + public List Items { get; set; } + + public void Add() + { + var dialog = new AddMapsDialog(this.Module, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var rulesSection = service.GetSection("system.webServer/rewrite/rewriteMaps"); + ConfigurationElementCollection rulesCollection = rulesSection.GetCollection(); + + if (this.SelectedItem != newItem) + { + this.Items.Add(newItem); + this.SelectedItem = newItem; + } + else if (newItem.Flag != "Local") + { + rulesCollection.Remove(newItem.Element); + newItem.Flag = "Local"; + } + + newItem.AppendTo(rulesCollection); + service.ServerManager.CommitChanges(); + this.OnRewriteSettingsSaved(); + + this.Edit(); + } + + public void AddRule() + { + var dialog = new AddMapDialog(this.Module, null, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationElementCollection rulesCollection = this.SelectedItem.Element.GetCollection(); + + if (this.SelectedItem.SelectedItem != newItem) + { + this.SelectedItem.Items.Add(newItem); + this.SelectedItem.SelectedItem = newItem; + } + else if (newItem.Flag != "Local") + { + rulesCollection.Remove(newItem.Element); + newItem.Flag = "Local"; + } + + newItem.AppendTo(rulesCollection); + service.ServerManager.CommitChanges(); + this.OnRewriteSettingsSaved(); + this.SelectedItem.OnRewriteSettingsSaved(); + } + + public void Remove() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected entry?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + this.Items.Remove(this.SelectedItem); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + var section = service.GetSection("system.webServer/rewrite/rewriteMaps"); + ConfigurationElementCollection collection = section.GetCollection(); + collection.Remove(this.SelectedItem.Element); + service.ServerManager.CommitChanges(); + + this.SelectedItem = null; + this.OnRewriteSettingsSaved(); + } + + public void RemoveRule() + { + var dialog = (IManagementUIService)this.GetService(typeof(IManagementUIService)); + if ( + dialog.ShowMessage("Are you sure that you want to remove the selected entry?", "Confirm Remove", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != + DialogResult.Yes) + { + return; + } + + this.SelectedItem.Items.Remove(this.SelectedItem.SelectedItem); + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationElementCollection collection = this.SelectedItem.Element.GetCollection(); + collection.Remove(this.SelectedItem.SelectedItem.Element); + service.ServerManager.CommitChanges(); + + this.SelectedItem.SelectedItem = null; + this.OnRewriteSettingsSaved(); + this.SelectedItem.OnRewriteSettingsSaved(); + } + + internal protected void OnRewriteSettingsSaved() + { + this.RewriteSettingsUpdated?.Invoke(); + } + + public virtual bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130403&clcid=0x409"); + return false; + } + + internal void Edit() + { + var service = (INavigationService)this.GetService(typeof(INavigationService)); + service.Navigate(null, null, typeof(MapPage), new Tuple(this, this.SelectedItem)); + this.OnRewriteSettingsSaved(); + } + + public void EditRule() + { + var dialog = new AddMapDialog(this.Module, this.SelectedItem.SelectedItem, this); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + var newItem = dialog.Item; + var service = (IConfigurationService)this.GetService(typeof(IConfigurationService)); + ConfigurationElementCollection rulesCollection = this.SelectedItem.Element.GetCollection(); + + if (this.SelectedItem.SelectedItem != newItem) + { + this.SelectedItem.Items.Add(newItem); + this.SelectedItem.SelectedItem = newItem; + } + else if (newItem.Flag != "Local") + { + rulesCollection.Remove(newItem.Element); + newItem.Flag = "Local"; + } + + newItem.AppendTo(rulesCollection); + service.ServerManager.CommitChanges(); + this.OnRewriteSettingsSaved(); + this.SelectedItem.OnRewriteSettingsSaved(); + } + + public MapItem SelectedItem { get; internal set; } + public bool CanRevert { get; private set; } + + public RewriteSettingsSavedEventHandler RewriteSettingsUpdated { get; set; } + public string Description { get; } + + public virtual bool IsFeatureEnabled + { + get { return true; } + } + + public virtual Version MinimumFrameworkVersion + { + get { return FxVersionNotRequired; } + } + + public Module Module { get; } + public string Name { get; } + + public void Set() + { + var dialog = new MapSettingsDialog(this.Module, this.SelectedItem); + if (dialog.ShowDialog() != DialogResult.OK) + { + return; + } + + this.OnRewriteSettingsSaved(); + } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapsPage.Designer.cs b/JexusManager.Features.Rewrite/Inbound/MapsPage.Designer.cs new file mode 100644 index 00000000..2ec2dd3b --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapsPage.Designer.cs @@ -0,0 +1,275 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class MapsPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chNumber = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chValue = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 7; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 9; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chNumber, + this.chValue}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 25); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 515); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + this.listView1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ListView1KeyDown); + this.listView1.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.ListView1MouseDoubleClick); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 200; + // + // chNumber + // + this.chNumber.Text = "Number of Items"; + this.chNumber.Width = 200; + // + // chValue + // + this.chValue.Text = "Default Value"; + this.chValue.Width = 200; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel3, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 0); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel3 + // + this.toolStripLabel3.Name = "toolStripLabel3"; + this.toolStripLabel3.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel3.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Category", + "Area"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 8; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(191, 25); + this.label3.TabIndex = 2; + this.label3.Text = "URL Rewrite Maps"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // MapsPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "MapsPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private ContextMenuStrip cmsActionPanel; + private SplitContainer splitContainer1; + private Panel panel2; + private ListView listView1; + private ColumnHeader chName; + private ColumnHeader chNumber; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel3; + private ToolStripComboBox cbGroup; + private PictureBox pictureBox1; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + private ColumnHeader chValue; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapsPage.cs b/JexusManager.Features.Rewrite/Inbound/MapsPage.cs new file mode 100644 index 00000000..796a2bcb --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapsPage.cs @@ -0,0 +1,180 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class MapsPage : ModuleListPage, IModuleChildPage + { + private sealed class PageTaskList : DefaultTaskList + { + private readonly MapsPage _owner; + + public PageTaskList(MapsPage owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + return new TaskItem[] + { + GetBackTaskItem("Back", "Back to Rules"), + new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage(), + HelpTaskItem + }; + } + + [Obfuscation(Exclude = true)] + public void ShowHelp() + { + _owner.ShowHelp(); + } + + [Obfuscation(Exclude = true)] + public void Back() + { + _owner.Back(); + } + } + + private sealed class MapsListViewItem : ListViewItem + { + public MapItem Item { get; } + private readonly MapsPage _page; + + public MapsListViewItem(MapItem item, MapsPage page) + : base(item.Name) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Items.Count.ToString())); + this.SubItems.Add(new ListViewSubItem(this, item.DefaultValue)); + } + } + + private TaskList _taskList; + private MapsFeature _feature; + + public MapsPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new MapsFeature(Module); + _feature.RewriteSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new MapsListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (MapsListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((MapsListViewItem)listView1.SelectedItems[0]).Item + : null; + this.Refresh(); + } + + private void ListView1KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Delete) + { + _feature.Remove(); + } + } + + private void ListView1MouseDoubleClick(object sender, MouseEventArgs e) + { + _feature.Edit(); + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + protected override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130425&clcid=0x409"); + return true; + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void Back() + { + var service = (INavigationService)GetService(typeof(INavigationService)); + service?.NavigateBack(1); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + public IModulePage ParentPage { get; set; } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/MapsPage.resx b/JexusManager.Features.Rewrite/Inbound/MapsPage.resx new file mode 100644 index 00000000..2e9f734f --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/MapsPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 556, 56 + + + 661, 56 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/ServerVariableItem.cs b/JexusManager.Features.Rewrite/Inbound/ServerVariableItem.cs new file mode 100644 index 00000000..384c20c7 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/ServerVariableItem.cs @@ -0,0 +1,47 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite.Inbound +{ + using Microsoft.Web.Administration; + + public class ServerVariableItem + { + public ConfigurationElement Element { get; set; } + + public ServerVariableItem(ConfigurationElement element) + { + this.Element = element; + if (element == null) + { + this.Replace = true; + return; + } + + this.Name = (string)element["name"]; + this.Value = (string)element["value"]; + this.Replace = (bool)element["replace"]; + } + + public bool Replace { get; set; } + + public string Value { get; set; } + + public string Name { get; set; } + + public void Apply() + { + Element["name"] = Name; + Element["value"] = Value; + Element["replace"] = Replace; + } + + public void AppendTo(ConfigurationElementCollection variableCollection) + { + Element = variableCollection.CreateElement(); + Apply(); + variableCollection.Add(Element); + } + } +} \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.Designer.cs b/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.Designer.cs new file mode 100644 index 00000000..4f499ba5 --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.Designer.cs @@ -0,0 +1,281 @@ +namespace JexusManager.Features.Rewrite.Inbound +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class ServerVariablesPage + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (this.components != null)) + { + this.components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.cmsActionPanel = new System.Windows.Forms.ContextMenuStrip(this.components); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.panel2 = new System.Windows.Forms.Panel(); + this.listView1 = new System.Windows.Forms.ListView(); + this.chName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.chEntry = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.toolStrip2 = new System.Windows.Forms.ToolStrip(); + this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel(); + this.cbGroup = new System.Windows.Forms.ToolStripComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tsActionPanel = new System.Windows.Forms.ToolStrip(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.toolStrip2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // cmsActionPanel + // + this.cmsActionPanel.Name = "cmsActionPanel"; + this.cmsActionPanel.Size = new System.Drawing.Size(61, 4); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.BackColor = System.Drawing.Color.White; + this.splitContainer1.Panel1.ContextMenuStrip = this.cmsActionPanel; + this.splitContainer1.Panel1.Controls.Add(this.panel2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.label3); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1); + this.splitContainer1.Panel2MinSize = 200; + this.splitContainer1.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.SplitterDistance = 580; + this.splitContainer1.TabIndex = 7; + this.splitContainer1.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SplitContainer1SplitterMoved); + // + // panel2 + // + this.panel2.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.panel2.Controls.Add(this.listView1); + this.panel2.Controls.Add(this.toolStrip2); + this.panel2.Controls.Add(this.label2); + this.panel2.Location = new System.Drawing.Point(10, 50); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(560, 540); + this.panel2.TabIndex = 9; + // + // listView1 + // + this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.chName, + this.chEntry}); + this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.listView1.FullRowSelect = true; + this.listView1.HideSelection = false; + this.listView1.Location = new System.Drawing.Point(0, 38); + this.listView1.Margin = new System.Windows.Forms.Padding(5); + this.listView1.MultiSelect = false; + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(560, 502); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListView1SelectedIndexChanged); + this.listView1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ListView1KeyDown); + // + // chName + // + this.chName.Text = "Name"; + this.chName.Width = 300; + // + // chEntry + // + this.chEntry.Text = "Entry Type"; + this.chEntry.Width = 300; + // + // toolStrip2 + // + this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripLabel3, + this.cbGroup}); + this.toolStrip2.Location = new System.Drawing.Point(0, 13); + this.toolStrip2.Name = "toolStrip2"; + this.toolStrip2.Size = new System.Drawing.Size(560, 25); + this.toolStrip2.TabIndex = 3; + this.toolStrip2.Text = "toolStrip2"; + // + // toolStripLabel3 + // + this.toolStripLabel3.Name = "toolStripLabel3"; + this.toolStripLabel3.Size = new System.Drawing.Size(59, 22); + this.toolStripLabel3.Text = "Group by:"; + // + // cbGroup + // + this.cbGroup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbGroup.Items.AddRange(new object[] { + "No Grouping", + "Category", + "Area"}); + this.cbGroup.Name = "cbGroup"; + this.cbGroup.Size = new System.Drawing.Size(121, 25); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Top; + this.label2.Location = new System.Drawing.Point(0, 0); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(490, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Use this feature to manage the list of server variables that you can set or chang" + + "e by using rewrite rules."; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(10, 10); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(32, 32); + this.pictureBox1.TabIndex = 8; + this.pictureBox1.TabStop = false; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 10); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(252, 25); + this.label3.TabIndex = 2; + this.label3.Text = "Allowed Server Variables"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tsActionPanel, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(216, 600); + this.tableLayoutPanel1.TabIndex = 1; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(216, 23); + this.panel1.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(3, 5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(49, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Actions"; + // + // tsActionPanel + // + this.tsActionPanel.CanOverflow = false; + this.tsActionPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tsActionPanel.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.tsActionPanel.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow; + this.tsActionPanel.Location = new System.Drawing.Point(0, 23); + this.tsActionPanel.Name = "tsActionPanel"; + this.tsActionPanel.Size = new System.Drawing.Size(216, 577); + this.tsActionPanel.TabIndex = 2; + this.tsActionPanel.Text = "toolStrip1"; + // + // ServerVariablesPage + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Name = "ServerVariablesPage"; + this.Size = new System.Drawing.Size(800, 600); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.toolStrip2.ResumeLayout(false); + this.toolStrip2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private ContextMenuStrip cmsActionPanel; + private SplitContainer splitContainer1; + private Panel panel2; + private ListView listView1; + private ColumnHeader chName; + private ColumnHeader chEntry; + private ToolStrip toolStrip2; + private ToolStripLabel toolStripLabel3; + private ToolStripComboBox cbGroup; + private Label label2; + private PictureBox pictureBox1; + private Label label3; + private TableLayoutPanel tableLayoutPanel1; + private Panel panel1; + private Label label1; + private ToolStrip tsActionPanel; + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.cs b/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.cs new file mode 100644 index 00000000..4648fe0d --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.cs @@ -0,0 +1,174 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite.Inbound +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Reflection; + using System.Windows.Forms; + + using JexusManager.Properties; + using JexusManager.Services; + + using Microsoft.Web.Management.Client; + using Microsoft.Web.Management.Client.Win32; + + internal partial class ServerVariablesPage : ModuleListPage, IModuleChildPage + { + private sealed class PageTaskList : DefaultTaskList + { + private readonly ServerVariablesPage _owner; + + public PageTaskList(ServerVariablesPage owner) + { + _owner = owner; + } + + public override ICollection GetTaskItems() + { + return new TaskItem[] + { + GetBackTaskItem("Back", "Back to Rules"), + new MethodTaskItem(string.Empty, "-", string.Empty).SetUsage(), + HelpTaskItem + }; + } + + [Obfuscation(Exclude = true)] + public void ShowHelp() + { + _owner.ShowHelp(); + } + + [Obfuscation(Exclude = true)] + public void Back() + { + _owner.Back(); + } + } + + private sealed class VariableListViewItem : ListViewItem + { + public AllowedVariableItem Item { get; } + private readonly ServerVariablesPage _page; + + public VariableListViewItem(AllowedVariableItem item, ServerVariablesPage page) + : base(item.Name) + { + this.Item = item; + _page = page; + this.SubItems.Add(new ListViewSubItem(this, item.Flag)); + } + } + + private TaskList _taskList; + private AllowedVariablesFeature _feature; + + public ServerVariablesPage() + { + this.InitializeComponent(); + } + + protected override void Initialize(object navigationData) + { + base.Initialize(navigationData); + var service = (IConfigurationService)GetService(typeof(IConfigurationService)); + pictureBox1.Image = service.Scope.GetImage(); + + _feature = new AllowedVariablesFeature(Module); + _feature.RewriteSettingsUpdated = this.InitializeListPage; + _feature.Load(); + } + + protected override void InitializeListPage() + { + listView1.Items.Clear(); + foreach (var file in _feature.Items) + { + listView1.Items.Add(new VariableListViewItem(file, this)); + } + + if (_feature.SelectedItem == null) + { + this.Refresh(); + return; + } + + foreach (VariableListViewItem item in listView1.Items) + { + if (item.Item == _feature.SelectedItem) + { + item.Selected = true; + } + } + } + + private void ListView1SelectedIndexChanged(object sender, EventArgs e) + { + _feature.SelectedItem = listView1.SelectedItems.Count > 0 + ? ((VariableListViewItem)listView1.SelectedItems[0]).Item + : null; + this.Refresh(); + } + + private void ListView1KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Delete) + { + _feature.Remove(); + } + } + + protected override void Refresh() + { + this.Tasks.Fill(tsActionPanel, cmsActionPanel); + base.Refresh(); + } + + protected override bool ShowHelp() + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130425&clcid=0x409"); + return true; + } + + private void SplitContainer1SplitterMoved(object sender, SplitterEventArgs e) + { + if (splitContainer1.Panel2.Width > 500) + { + splitContainer1.SplitterDistance = splitContainer1.Width - 500; + } + } + + private void Back() + { + var service = (INavigationService)GetService(typeof(INavigationService)); + service?.NavigateBack(1); + } + + protected override TaskListCollection Tasks + { + get + { + if (_taskList == null) + { + _taskList = new PageTaskList(this); + } + + base.Tasks.Add(_feature.GetTaskList()); + base.Tasks.Add(_taskList); + return base.Tasks; + } + } + + public IModulePage ParentPage { get; set; } + } +} diff --git a/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.resx b/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.resx new file mode 100644 index 00000000..2e9f734f --- /dev/null +++ b/JexusManager.Features.Rewrite/Inbound/ServerVariablesPage.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 143, 17 + + + 556, 56 + + + 661, 56 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/JexusManager.Features.Rewrite.csproj b/JexusManager.Features.Rewrite/JexusManager.Features.Rewrite.csproj new file mode 100644 index 00000000..46ea318f --- /dev/null +++ b/JexusManager.Features.Rewrite/JexusManager.Features.Rewrite.csproj @@ -0,0 +1,366 @@ + + + + + Debug + AnyCPU + {425CEE66-72B3-42C0-989C-685B28B05230} + Library + Properties + JexusManager.Features.Rewrite + JexusManager.Features.Rewrite + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + JexusManager.snk + + + + + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + True + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + True + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + True + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + True + + + + + + + + + + + + + + Form + + + AddAllowedVariableDialog.cs + + + Form + + + AddConditionDialog.cs + + + Form + + + AddMapDialog.cs + + + Form + + + AddMapsDialog.cs + + + Form + + + AddServerVariableDialog.cs + + + + + + + UserControl + + + InboundRulePage.cs + + + + UserControl + + + MapPage.cs + + + + Form + + + MapSettingsDialog.cs + + + + + UserControl + + + MapsPage.cs + + + + UserControl + + + ServerVariablesPage.cs + + + Form + + + NewRewriteRuleDialog.cs + + + Form + + + NewRuleBlockingDialog.cs + + + Form + + + NewRuleWithRewriteMapsDialog.cs + + + Form + + + AddCustomTagDialog.cs + + + Form + + + AddCustomTagsDialog.cs + + + Form + + + AddPreconditionDialog.cs + + + + + + UserControl + + + CustomTagsPage.cs + + + + + UserControl + + + OutboundRulePage.cs + + + + + UserControl + + + PreConditionsPage.cs + + + + True + True + Resources.resx + + + Form + + + RegexTestDialog.cs + + + + + + UserControl + + + RewritePage.cs + + + + + + + AddAllowedVariableDialog.cs + + + AddConditionDialog.cs + + + AddMapDialog.cs + + + AddMapsDialog.cs + + + AddServerVariableDialog.cs + + + InboundRulePage.cs + + + MapPage.cs + + + MapSettingsDialog.cs + + + MapsPage.cs + + + ServerVariablesPage.cs + + + NewRewriteRuleDialog.cs + + + NewRuleBlockingDialog.cs + + + NewRuleWithRewriteMapsDialog.cs + + + AddCustomTagDialog.cs + + + AddCustomTagsDialog.cs + + + AddPreconditionDialog.cs + + + CustomTagsPage.cs + + + OutboundRulePage.cs + + + PreConditionsPage.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + RegexTestDialog.cs + + + RewritePage.cs + + + + + + + + + + {e4040a45-b156-4048-b1a4-bd262ba18047} + JexusManager.Shared + + + {70a25201-8ea4-48f8-a4a6-ed13adf8823c} + CheckBoxComboBox + + + {be76a6fe-225f-4287-9ca0-231f5af4cdc1} + MakarovDev.ExpandCollapsePanel + + + {17e994b0-5a31-4f79-9796-83a45e87853d} + Microsoft.Web.Management + + + {bd24afda-292f-4f41-ba80-c48f21d4d9f2} + Microsoft.Web.Administration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/JexusManager.snk b/JexusManager.Features.Rewrite/JexusManager.snk new file mode 100644 index 00000000..fe6f345a Binary files /dev/null and b/JexusManager.Features.Rewrite/JexusManager.snk differ diff --git a/JexusManager.Features.Rewrite/NewRewriteRuleDialog.Designer.cs b/JexusManager.Features.Rewrite/NewRewriteRuleDialog.Designer.cs new file mode 100644 index 00000000..cfb8f7b7 --- /dev/null +++ b/JexusManager.Features.Rewrite/NewRewriteRuleDialog.Designer.cs @@ -0,0 +1,201 @@ +namespace JexusManager.Features.Rewrite +{ + using System.ComponentModel; + using System.Windows.Forms; + + partial class NewRewriteRuleDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.listView1 = new System.Windows.Forms.ListView(); + this.ilLarge = new System.Windows.Forms.ImageList(this.components); + this.ilSmall = new System.Windows.Forms.ImageList(this.components); + this.txtDescription = new System.Windows.Forms.TextBox(); + this.toolStripContainer1 = new System.Windows.Forms.ToolStripContainer(); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.btnBig = new System.Windows.Forms.ToolStripButton(); + this.btnSmall = new System.Windows.Forms.ToolStripButton(); + this.toolStripContainer1.TopToolStripPanel.SuspendLayout(); + this.toolStripContainer1.SuspendLayout(); + this.toolStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(567, 386); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(471, 386); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(22, 25); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(112, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Select a rule template:"; + // + // listView1 + // + this.listView1.Location = new System.Drawing.Point(25, 41); + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(622, 258); + this.listView1.TabIndex = 3; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.SmallIcon; + // + // ilLarge + // + this.ilLarge.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; + this.ilLarge.ImageSize = new System.Drawing.Size(32, 32); + this.ilLarge.TransparentColor = System.Drawing.Color.Transparent; + // + // ilSmall + // + this.ilSmall.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; + this.ilSmall.ImageSize = new System.Drawing.Size(16, 16); + this.ilSmall.TransparentColor = System.Drawing.Color.Transparent; + // + // txtDescription + // + this.txtDescription.Location = new System.Drawing.Point(25, 299); + this.txtDescription.Multiline = true; + this.txtDescription.Name = "txtDescription"; + this.txtDescription.ReadOnly = true; + this.txtDescription.Size = new System.Drawing.Size(622, 64); + this.txtDescription.TabIndex = 4; + // + // toolStripContainer1 + // + // + // toolStripContainer1.ContentPanel + // + this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(50, 0); + this.toolStripContainer1.Location = new System.Drawing.Point(597, 13); + this.toolStripContainer1.Name = "toolStripContainer1"; + this.toolStripContainer1.Size = new System.Drawing.Size(50, 23); + this.toolStripContainer1.TabIndex = 5; + this.toolStripContainer1.Text = "toolStripContainer1"; + // + // toolStripContainer1.TopToolStripPanel + // + this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.toolStrip1); + // + // toolStrip1 + // + this.toolStrip1.CanOverflow = false; + this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None; + this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.btnBig, + this.btnSmall}); + this.toolStrip1.Location = new System.Drawing.Point(3, 0); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; + this.toolStrip1.Size = new System.Drawing.Size(49, 25); + this.toolStrip1.TabIndex = 0; + // + // btnBig + // + this.btnBig.CheckOnClick = true; + this.btnBig.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnBig.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnBig.Name = "btnBig"; + this.btnBig.Size = new System.Drawing.Size(23, 22); + this.btnBig.Text = "Large icon view"; + // + // btnSmall + // + this.btnSmall.Checked = true; + this.btnSmall.CheckOnClick = true; + this.btnSmall.CheckState = System.Windows.Forms.CheckState.Checked; + this.btnSmall.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnSmall.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnSmall.Name = "btnSmall"; + this.btnSmall.Size = new System.Drawing.Size(23, 22); + this.btnSmall.Text = "Small icon view"; + // + // NewRewriteRuleDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(674, 421); + this.Controls.Add(this.toolStripContainer1); + this.Controls.Add(this.txtDescription); + this.Controls.Add(this.listView1); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewRewriteRuleDialog"; + this.Text = "Add Rule(s)"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRewriteRuleDialogHelpButtonClicked); + this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false); + this.toolStripContainer1.TopToolStripPanel.PerformLayout(); + this.toolStripContainer1.ResumeLayout(false); + this.toolStripContainer1.PerformLayout(); + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private ListView listView1; + private TextBox txtDescription; + private ImageList ilSmall; + private ImageList ilLarge; + private ToolStripContainer toolStripContainer1; + private ToolStrip toolStrip1; + private ToolStripButton btnBig; + private ToolStripButton btnSmall; + } +} diff --git a/JexusManager.Features.Rewrite/NewRewriteRuleDialog.cs b/JexusManager.Features.Rewrite/NewRewriteRuleDialog.cs new file mode 100644 index 00000000..19d173b6 --- /dev/null +++ b/JexusManager.Features.Rewrite/NewRewriteRuleDialog.cs @@ -0,0 +1,150 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace JexusManager.Features.Rewrite +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using Properties; + + using Microsoft.Web.Management.Client.Win32; + + public partial class NewRewriteRuleDialog : DialogForm + { + public NewRewriteRuleDialog(IServiceProvider serviceProvider) + : base(serviceProvider) + { + InitializeComponent(); + btnBig.Image = DefaultTaskList.BasicSettingsImage; + btnSmall.Image = DefaultTaskList.ViewImage; + + ilLarge.Images.Add(Resources.blank_32); + ilLarge.Images.Add(Resources.map_32); + ilLarge.Images.Add(Resources.blocking_32); + ilLarge.Images.Add(Resources.blankout_32); + ilLarge.Images.Add(Resources.lowercase_32); + + ilSmall.Images.Add(Resources.blank_16); + ilSmall.Images.Add(Resources.map_16); + ilSmall.Images.Add(Resources.blocking_16); + ilSmall.Images.Add(Resources.blankout_16); + ilSmall.Images.Add(Resources.lowercase_16); + listView1.LargeImageList = ilLarge; + listView1.SmallImageList = ilSmall; + ListViewGroup listViewGroup1 = new ListViewGroup("Inbound rules", HorizontalAlignment.Left); + ListViewGroup listViewGroup2 = new ListViewGroup("Inbound and Outbound Rules", HorizontalAlignment.Left); + ListViewGroup listViewGroup3 = new ListViewGroup("Outbound rules", HorizontalAlignment.Left); + ListViewGroup listViewGroup4 = new ListViewGroup( + "Search Engine Optimization (SEO)", + HorizontalAlignment.Left); + ListViewItem listViewItem1 = new ListViewItem("Blank rule"); + listViewItem1.ImageIndex = 0; + ListViewItem listViewItem2 = new ListViewItem("Rule with rewrite map"); + listViewItem2.ImageIndex = 1; + ListViewItem listViewItem3 = new ListViewItem("Request blocking"); + listViewItem3.ImageIndex = 2; + ListViewItem listViewItem4 = new ListViewItem("User-friendly URL"); + listViewItem4.ImageIndex = 1; + ListViewItem listViewItem5 = new ListViewItem("Blank rule"); + listViewItem5.ImageIndex = 3; + ListViewItem listViewItem6 = new ListViewItem("Enforce lowercase URLs"); + listViewItem6.ImageIndex = 4; + + listViewGroup1.Header = "Inbound rules"; + listViewGroup1.Name = "lvgIn"; + listViewGroup2.Header = "Inbound and Outbound Rules"; + listViewGroup2.Name = "lvgInOut"; + listViewGroup3.Header = "Outbound rules"; + listViewGroup3.Name = "lvgOut"; + listViewGroup4.Header = "Search Engine Optimization (SEO)"; + listViewGroup4.Name = "lvgSeo"; + listView1.Groups.AddRange(new[] { listViewGroup1, listViewGroup2, listViewGroup3, listViewGroup4 }); + listViewItem1.Group = listViewGroup1; + listViewItem1.ToolTipText = + "Select this template to create a new inbound rule without any preset values. This template opens the \"Edit Rule\" page that you can use to define a new rewrite rule for changing the requested URL address."; + listViewItem2.Group = listViewGroup1; + listViewItem2.ToolTipText = + "Select this template to create a rewrite or redirect rule that uses a rewrite map. The rewrite map for this rule can contain a large number of static mappings between original URLs and rewritten URLs or redirection URLs, depending on whether the rule is rewriting or redirecting requests."; + listViewItem3.Group = listViewGroup1; + listViewItem3.ToolTipText = + "Select this template to create a rule that will block client requests based on ce" + + "rtain text patterns in the URL path, query string, HTTP headers, and server vari" + "ables."; + listViewItem4.Group = listViewGroup2; + listViewItem4.ToolTipText = + "Select this template to create rules for enabling user-friendly URLs for your dynamic Web applications. This template creates rules that rewrite user-friendly URLs to internal URLs that your Web application understands."; + listViewItem5.Group = listViewGroup3; + listViewItem5.ToolTipText = + "Select this template to create a new outbound rule without any preset values. This template opens the \"Edit Rule\" page that you can use to define a new rewrite rule for changing the content of an HTTP response."; + listViewItem6.Group = listViewGroup4; + listViewItem6.ToolTipText = + "Use this template to create a rule that will enforce the use of lowercase letters" + " in the URL."; + listView1.Items.AddRange( + new[] { listViewItem1, listViewItem2, listViewItem3, listViewItem4, listViewItem5, listViewItem6 }); + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(listView1, "SelectedIndexChanged") + .Subscribe(evt => + { + txtDescription.Text = listView1.SelectedItems.Count == 0 + ? string.Empty + : listView1.SelectedItems[0].ToolTipText; + btnOK.Enabled = listView1.SelectedItems.Count > 0; + })); + + var doubleClick = Observable.FromEventPattern(listView1, "DoubleClick"); + container.Add( + doubleClick.Subscribe(evt => + { + if (listView1.SelectedItems.Count == 0) + { + return; + } + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Merge(doubleClick) + .Subscribe(evt => + { + SelectedIndex = listView1.SelectedIndices[0]; + DialogResult = DialogResult.OK; + })); + + var big = Observable.FromEventPattern(btnBig, "Click"); + container.Add( + big.Subscribe(evt => + { + btnSmall.Checked = !btnBig.Checked; + })); + + var small = Observable.FromEventPattern(btnSmall, "Click"); + container.Add( + small.Subscribe(evt => + { + btnBig.Checked = !btnSmall.Checked; + })); + + container.Add( + big.Merge(small).Subscribe(evt => + { + listView1.View = btnBig.Checked ? View.LargeIcon : View.SmallIcon; + })); + } + + public int SelectedIndex { get; private set; } + + private void NewRewriteRuleDialogHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130421&clcid=0x409"); + } + } +} diff --git a/JexusManager.Features.Rewrite/NewRewriteRuleDialog.resx b/JexusManager.Features.Rewrite/NewRewriteRuleDialog.resx new file mode 100644 index 00000000..2424ecae --- /dev/null +++ b/JexusManager.Features.Rewrite/NewRewriteRuleDialog.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 127, 17 + + + 17, 17 + + + 215, 17 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/NewRuleBlockingDialog.Designer.cs b/JexusManager.Features.Rewrite/NewRuleBlockingDialog.Designer.cs new file mode 100644 index 00000000..76c533fe --- /dev/null +++ b/JexusManager.Features.Rewrite/NewRuleBlockingDialog.Designer.cs @@ -0,0 +1,228 @@ +namespace JexusManager.Features.Rewrite +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class NewRuleBlockingDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.cbInput = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.cbMatch = new System.Windows.Forms.ComboBox(); + this.lblPattern = new System.Windows.Forms.Label(); + this.txtPattern = new System.Windows.Forms.TextBox(); + this.lblExample = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.cbMode = new System.Windows.Forms.ComboBox(); + this.label6 = new System.Windows.Forms.Label(); + this.cbResponse = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(282, 361); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(95, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(186, 361); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(90, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 13); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(121, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Block access based on:"; + // + // cbInput + // + this.cbInput.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbInput.FormattingEnabled = true; + this.cbInput.Items.AddRange(new object[] { + "URL Path", + "User-agent Header", + "IP Address", + "Query String", + "Referer", + "Host Header"}); + this.cbInput.Location = new System.Drawing.Point(12, 29); + this.cbInput.Name = "cbInput"; + this.cbInput.Size = new System.Drawing.Size(365, 21); + this.cbInput.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 72); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(96, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Block request that:"; + // + // cbMatch + // + this.cbMatch.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbMatch.FormattingEnabled = true; + this.cbMatch.Items.AddRange(new object[] { + "Matches the Pattern", + "Does Not Match the Pattern"}); + this.cbMatch.Location = new System.Drawing.Point(12, 88); + this.cbMatch.Name = "cbMatch"; + this.cbMatch.Size = new System.Drawing.Size(365, 21); + this.cbMatch.TabIndex = 5; + // + // lblPattern + // + this.lblPattern.AutoSize = true; + this.lblPattern.Location = new System.Drawing.Point(13, 135); + this.lblPattern.Name = "lblPattern"; + this.lblPattern.Size = new System.Drawing.Size(100, 13); + this.lblPattern.TabIndex = 6; + this.lblPattern.Text = "Pattern (URL Path):"; + // + // txtPattern + // + this.txtPattern.Location = new System.Drawing.Point(12, 151); + this.txtPattern.Name = "txtPattern"; + this.txtPattern.Size = new System.Drawing.Size(365, 20); + this.txtPattern.TabIndex = 7; + // + // lblExample + // + this.lblExample.AutoSize = true; + this.lblExample.Location = new System.Drawing.Point(13, 174); + this.lblExample.Name = "lblExample"; + this.lblExample.Size = new System.Drawing.Size(94, 13); + this.lblExample.TabIndex = 8; + this.lblExample.Text = "Example: IMG*.jpg"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(13, 212); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(37, 13); + this.label5.TabIndex = 9; + this.label5.Text = "Using:"; + // + // cbMode + // + this.cbMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbMode.FormattingEnabled = true; + this.cbMode.Items.AddRange(new object[] { + "Regular Expressions", + "Wildcards"}); + this.cbMode.Location = new System.Drawing.Point(12, 228); + this.cbMode.Name = "cbMode"; + this.cbMode.Size = new System.Drawing.Size(365, 21); + this.cbMode.TabIndex = 10; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(13, 271); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(73, 13); + this.label6.TabIndex = 11; + this.label6.Text = "How to block:"; + // + // cbResponse + // + this.cbResponse.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbResponse.FormattingEnabled = true; + this.cbResponse.Items.AddRange(new object[] { + "Send an HTTP 401 (Unauthorized) Response", + "Send an HTTP 403 (Forbidden) Response", + "Send an HTTP 404 (File Not Found) Response", + "Abort Request"}); + this.cbResponse.Location = new System.Drawing.Point(12, 287); + this.cbResponse.Name = "cbResponse"; + this.cbResponse.Size = new System.Drawing.Size(365, 21); + this.cbResponse.TabIndex = 12; + // + // NewRuleBlockingDialog + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(389, 396); + this.Controls.Add(this.cbResponse); + this.Controls.Add(this.label6); + this.Controls.Add(this.cbMode); + this.Controls.Add(this.label5); + this.Controls.Add(this.lblExample); + this.Controls.Add(this.txtPattern); + this.Controls.Add(this.lblPattern); + this.Controls.Add(this.cbMatch); + this.Controls.Add(this.label2); + this.Controls.Add(this.cbInput); + this.Controls.Add(this.label1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.Name = "NewRuleBlockingDialog"; + this.Text = "Add Request Blocking Rule"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.NewRuleBlockingHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button btnCancel; + private Button btnOK; + private Label label1; + private ComboBox cbInput; + private Label label2; + private ComboBox cbMatch; + private Label lblPattern; + private TextBox txtPattern; + private Label lblExample; + private Label label5; + private ComboBox cbMode; + private Label label6; + private ComboBox cbResponse; + } +} diff --git a/JexusManager.Features.Rewrite/NewRuleBlockingDialog.cs b/JexusManager.Features.Rewrite/NewRuleBlockingDialog.cs new file mode 100644 index 00000000..215725f0 --- /dev/null +++ b/JexusManager.Features.Rewrite/NewRuleBlockingDialog.cs @@ -0,0 +1,178 @@ +// Copyright (c) Lex Li. All rights reserved. +// +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// -------------------------------------------------------------------------------------------------------------------- +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace JexusManager.Features.Rewrite +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Linq; + using System.Reactive.Disposables; + using System.Reactive.Linq; + using System.Windows.Forms; + + using JexusManager.Features.Rewrite.Inbound; + + using Microsoft.Web.Management.Client.Win32; + + internal partial class NewRuleBlockingDialog : DialogForm + { + public NewRuleBlockingDialog(IServiceProvider serviceProvider, InboundFeature rewriteFeature) + : base(serviceProvider) + { + InitializeComponent(); + cbMode.SelectedIndex = 1; + cbInput.SelectedIndex = 0; + cbMatch.SelectedIndex = 0; + cbResponse.SelectedIndex = 1; + + var container = new CompositeDisposable(); + FormClosed += (sender, args) => container.Dispose(); + + container.Add( + Observable.FromEventPattern(cbInput, "SelectedIndexChanged") + .Merge(Observable.FromEventPattern(cbMode, "SelectedIndexChanged")) + .Subscribe(evt => + { + switch (cbInput.SelectedIndex) + { + case 0: + lblPattern.Text = "Pattern (URL Path):"; + lblExample.Text = cbMode.SelectedIndex == 1 ? "Example: IMG*.jpg" : "Example: ^IMG.*\\.jpg$"; + break; + case 1: + lblPattern.Text = "Pattern (User-agent Header):"; + lblExample.Text = cbMode.SelectedIndex == 1 ? "Example: Mozilla/4*" : "Example: ^Mozilla/[1234].*"; + break; + case 2: + lblPattern.Text = "Pattern (IP Address):"; + lblExample.Text = cbMode.SelectedIndex == 1 ? "Exmaple: 192.168.1.*" : "Example: 192\\.168\\.1\\.[1-9]"; + break; + case 3: + lblPattern.Text = "Pattern (Query String):"; + lblExample.Text = cbMode.SelectedIndex == 1 ? "Example: *id=*&p=*" : "Example: id=[0-9]+&p=[a-z]+"; + break; + case 4: + lblPattern.Text = "Pattern (Referer):"; + lblExample.Text = cbMode.SelectedIndex == 1 ? "Example: http://*.consoto.com" : "Example: http://(?:www\\.)?contoso\\.com$"; + break; + case 5: + lblPattern.Text = "Pattern (Host Header):"; + lblExample.Text = cbMode.SelectedIndex == 1 ? "Example: *.consoto.com" : "Example: (?:www\\.)?contoso\\.com$"; + break; + } + })); + + container.Add( + Observable.FromEventPattern(txtPattern, "TextChanged") + .Sample(TimeSpan.FromSeconds(1)) + .Subscribe(evt => + { + btnOK.Enabled = !string.IsNullOrWhiteSpace(txtPattern.Text); + })); + + container.Add( + Observable.FromEventPattern(btnOK, "Click") + .Subscribe(evt => + { + var inputType = cbInput.SelectedIndex; + var pattern = txtPattern.Text; + var match = cbMatch.SelectedIndex; + var response = cbResponse.SelectedIndex; + var mode = cbMode.SelectedIndex; + + int index = 0; + string name; + do + { + index++; + name = string.Format("RequestBlockingRule{0}", index); + } + while (rewriteFeature.Items.All(item => item.Name != name)); + var newRule = new InboundRule(null); + newRule.Name = name; + newRule.Input = "URL Path"; + newRule.Enabled = true; + newRule.PatternSyntax = mode == 0 ? 0L : 1L; + newRule.PatternUrl = mode == 0 ? ".*" : "*"; + newRule.Type = response == 3 ? 4L : 3L; + newRule.ActionUrl = "{C:1}"; + newRule.RedirectType = 301; + newRule.StatusCode = GetStatusCode(response); + newRule.SubStatusCode = 0; + newRule.StatusReason = GetReason(response); + newRule.StatusDescription = GetMessage(response); + newRule.Conditions.Add( + new ConditionItem(null) + { + Input = GetInput(inputType), + MatchType = match == 0 ? 4 : 5, + Pattern = pattern, + IgnoreCase = true + }); + rewriteFeature.AddItem(newRule); + DialogResult = DialogResult.OK; + })); + } + + private void NewRuleBlockingHelpButtonClicked(object sender, CancelEventArgs e) + { + Process.Start("http://go.microsoft.com/fwlink/?LinkID=130412&clcid=0x409"); + } + + private string GetInput(int inputType) + { + switch (inputType) + { + case 0: + return "{URL}"; + case 1: + return "{HTTP_USER_AGENT}"; + case 2: + return "{REMOTE_ADDR}"; + case 3: + return "{QUERY_STRING}"; + case 4: + return "{HTTP_REFERER}"; + case 5: + return "{HTTP_HOST}"; + default: + throw new ArgumentOutOfRangeException(nameof(inputType)); + } + } + + private string GetMessage(int response) + { + return response == 0 + ? UnauthorizedText + : response == 1 ? ForbiddenText : response == 2 ? FileNotFoundText : string.Empty; + } + + private string GetReason(int response) + { + return response == 0 + ? "Unauthorized: Access is denied due to invalid credentials" + : response == 1 + ? "Forbidden: Access is denied." + : response == 2 ? "File or directory not found." : string.Empty; + } + + private uint GetStatusCode(int response) + { + return response == 0 ? 401U : response == 1 ? 403U : response == 2 ? 404U : 0U; + } + + private const string UnauthorizedText = "You do not have permission to view this directory or page using the credentials that you supplied."; + + private const string ForbiddenText = + "You do not have permission to view this directory or page using the credentials that you supplied."; + + private const string FileNotFoundText = "The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable."; + } +} diff --git a/JexusManager.Features.Rewrite/NewRuleBlockingDialog.resx b/JexusManager.Features.Rewrite/NewRuleBlockingDialog.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/JexusManager.Features.Rewrite/NewRuleBlockingDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/JexusManager.Features.Rewrite/NewRuleWithRewriteMapsDialog.Designer.cs b/JexusManager.Features.Rewrite/NewRuleWithRewriteMapsDialog.Designer.cs new file mode 100644 index 00000000..46a645b8 --- /dev/null +++ b/JexusManager.Features.Rewrite/NewRuleWithRewriteMapsDialog.Designer.cs @@ -0,0 +1,144 @@ +namespace JexusManager.Features.Rewrite +{ + using System.ComponentModel; + using System.Windows.Forms; + + internal partial class NewRuleWithRewriteMapsDialog + { + /// + /// Required designer variable. + /// + private IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(NewRuleWithRewriteMapsDialog)); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.cbAction = new System.Windows.Forms.ComboBox(); + this.cbMap = new System.Windows.Forms.ComboBox(); + this.lblDescription = new System.Windows.Forms.Label(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 25); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(124, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Selection the rule action:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(9, 84); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(115, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Select the rewrite map:"; + // + // cbAction + // + this.cbAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cbAction.FormattingEnabled = true; + this.cbAction.Items.AddRange(new object[] { + "Rewrite", + "Redirect"}); + this.cbAction.Location = new System.Drawing.Point(12, 41); + this.cbAction.Name = "cbAction"; + this.cbAction.Size = new System.Drawing.Size(395, 21); + this.cbAction.TabIndex = 3; + // + // cbMap + // + this.cbMap.FormattingEnabled = true; + this.cbMap.Location = new System.Drawing.Point(12, 100); + this.cbMap.Name = "cbMap"; + this.cbMap.Size = new System.Drawing.Size(395, 21); + this.cbMap.TabIndex = 4; + this.cbMap.Text = "