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 @@
+
+
+
+
+
+
+
+
+