public async Task SetUp() { const string Original = @"original.config"; const string OriginalMono = @"original.mono.config"; if (Helper.IsRunningOnMono()) { File.Copy("Website1/original.config", "Website1/web.config", true); File.Copy(OriginalMono, Current, true); } else { File.Copy("Website1\\original.config", "Website1\\web.config", true); File.Copy(Original, Current, true); } Environment.SetEnvironmentVariable( "JEXUS_TEST_HOME", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); _server = new IisExpressServerManager(Current); _serviceContainer = new ServiceContainer(); _serviceContainer.RemoveService(typeof(IConfigurationService)); _serviceContainer.RemoveService(typeof(IControlPanel)); var scope = ManagementScope.Server; _serviceContainer.AddService(typeof(IControlPanel), new ControlPanel()); _serviceContainer.AddService(typeof(IConfigurationService), new ConfigurationService(null, _server.GetApplicationHostConfiguration(), scope, _server, null, null, null, null, null)); _serviceContainer.RemoveService(typeof(IManagementUIService)); var mock = new Mock <IManagementUIService>(); mock.Setup( action => action.ShowMessage( It.IsAny <string>(), It.IsAny <string>(), It.IsAny <MessageBoxButtons>(), It.IsAny <MessageBoxIcon>(), It.IsAny <MessageBoxDefaultButton>())).Returns(DialogResult.Yes); _serviceContainer.AddService(typeof(IManagementUIService), mock.Object); var module = new HandlersModule(); module.TestInitialize(_serviceContainer, null); _feature = new HandlersFeature(module); _feature.Load(); }
public PhpDiagDialog(IServiceProvider provider, ServerManager server) : base(provider) { InitializeComponent(); var knownPhpVersions = new Dictionary <string, PhpVersion> { { "5.6", new PhpVersion("5.6", new DateTime(2018, 12, 31), new Version(11, 0)) }, { "7.0", new PhpVersion("7.0", new DateTime(2018, 12, 3), new Version(14, 0)) }, { "7.1", new PhpVersion("7.1", new DateTime(2019, 12, 1), new Version(14, 0)) }, { "7.2", new PhpVersion("7.2", new DateTime(2020, 11, 30), new Version(14, 11)) } }; var container = new CompositeDisposable(); FormClosed += (sender, args) => container.Dispose(); container.Add( Observable.FromEventPattern <EventArgs>(btnGenerate, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); try { Warn("IMPORTANT: This report might contain confidential information. Mask such before sharing to others."); Warn("-----"); Debug($"System Time: {DateTime.Now}"); Debug($"Processor Architecture: {Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")}"); Debug($"OS: {Environment.OSVersion}"); Debug($"Server Type: {server.Mode.AsString(EnumFormat.Description)}"); Debug(string.Empty); var modules = new ModulesFeature((Module)provider); modules.Load(); Debug($"Scan {modules.Items.Count} installed module(s)."); if (modules.Items.All(item => item.Name != "FastCgiModule")) { Error($"FastCGI module is not installed as part of IIS. Please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); return; } else { Debug("FastCGI module is installed."); } Debug(string.Empty); var handlers = new HandlersFeature((Module)provider); handlers.Load(); var foundPhpHandler = new List <HandlersItem>(); Debug($"Scan {handlers.Items.Count} registered handler(s)."); foreach (var item in handlers.Items) { if (item.Modules == "FastCgiModule") { Debug($"* Found FastCGI handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Handler: {item.TypeString}, Entry Type: {item.Flag} }}."); foundPhpHandler.Add(item); } } if (foundPhpHandler.Count == 0) { Error($"No FastCGI handler is registered for this web site."); Error($" * To run PHP on IIS, please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); Error($" * To run Python on IIS, please refer to https://pypi.org/project/wfastcgi/ or use HttpPlatformHandler https://docs.microsoft.com/en-us/iis/extensions/httpplatformhandler/httpplatformhandler-configuration-reference."); return; } Debug(string.Empty); var fastCgiFeature = new FastCgiFeature((Module)provider); fastCgiFeature.Load(); Debug($"Scan {fastCgiFeature.Items.Count} registered FastCGI application(s)."); var foundPhp = new List <FastCgiItem>(); foreach (var item in fastCgiFeature.Items) { var combination = string.IsNullOrWhiteSpace(item.Arguments) ? item.Path : item.Path + '|' + item.Arguments; foreach (var handler in foundPhpHandler) { if (string.Equals(combination, handler.ScriptProcessor, StringComparison.OrdinalIgnoreCase)) { Debug($"* Found FastCGI application registered as {{ Full path: {item.Path}, Arguments: {item.Arguments} }}."); foundPhp.Add(item); break; } } } if (foundPhp.Count == 0) { Error($"No suitable FastCGI appilcation is registered on this server."); Error($" * To run PHP on IIS, please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); Error($" * To run Python on IIS, please refer to https://pypi.org/project/wfastcgi/ or use HttpPlatformHandler https://docs.microsoft.com/en-us/iis/extensions/httpplatformhandler/httpplatformhandler-configuration-reference."); return; } Debug(Environment.NewLine); Debug($"Verify web stack installation versions."); foreach (var item in foundPhp) { var path = item.Path; if (path.TrimEnd('"').EndsWith("php-cgi.exe", StringComparison.OrdinalIgnoreCase)) { // PHP var info = FileVersionInfo.GetVersionInfo(path); var version = $"{info.FileMajorPart}.{info.FileMinorPart}"; if (knownPhpVersions.ContainsKey(version)) { var matched = knownPhpVersions[version]; if (matched.ExpiringDate <= DateTime.Now) { Error($"* PHP {info.FileVersion} ({path}) is unknown or obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else if (matched.ExpiringDate > DateTime.Now && (matched.ExpiringDate - DateTime.Now).TotalDays < 180) { Warn($"* PHP {version} ({path}) will soon be obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else { Debug($"* PHP {version} ({path}) is supported."); } var x86 = new PeNet.PeFile(path).Is32Bit; var cppFile = Path.Combine( Environment.GetFolderPath(x86 ? Environment.SpecialFolder.SystemX86 : Environment.SpecialFolder.System), $"msvcp{matched.CppVersion.Major}0.dll"); if (File.Exists(cppFile)) { var cpp = FileVersionInfo.GetVersionInfo(cppFile); if (cpp.FileMinorPart >= matched.CppVersion.Minor) { Debug($" Visual C++ runtime is detected (expected: {matched.CppVersion}, detected: {cpp.FileVersion})."); } else { Error($" Visual C++ runtime {matched.CppVersion} is not detected. Please install it following the tips on https://windows.php.net/download/."); } } else { Error($" Visual C++ {matched.CppVersion} runtime is not detected. Please install it following the tips on https://windows.php.net/download/."); } } else { Error($"* PHP {info.FileVersion} ({path}) is unknown or obsolete. Please refer to http://php.net/supported-versions.php for more details."); } } else if (path.TrimEnd('"').EndsWith("python.exe", StringComparison.OrdinalIgnoreCase)) { // Python } } Debug(string.Empty); var systemPath = Environment.GetEnvironmentVariable("Path"); Debug($"Windows Path environment variable: {systemPath}."); Debug(string.Empty); string[] paths = systemPath.Split(new char[1] { Path.PathSeparator }); foreach (var item in foundPhp) { var path = item.Path; if (path.TrimEnd('"').EndsWith("php-cgi.exe", StringComparison.OrdinalIgnoreCase)) { var rootFolder = Path.GetDirectoryName(path); Debug($"[{rootFolder}]"); if (!Directory.Exists(rootFolder)) { Error("Invalid root folder is found. Skip."); continue; } var config = Path.Combine(rootFolder, "php.ini"); if (File.Exists(config)) { Info($"Found PHP config file {config}."); var parser = new ConcatenateDuplicatedKeysIniDataParser(); parser.Configuration.ConcatenateSeparator = " "; var data = parser.Parse(File.ReadAllText(config)); var extensionFolder = data["PHP"]["extension_dir"]; if (extensionFolder == null) { extensionFolder = "ext"; } var fullPath = Path.Combine(rootFolder, extensionFolder); Info($"PHP loadable extension folder: {fullPath}"); var extesionNames = data["PHP"]["extension"]; if (extesionNames == null) { Info("No extension to verify."); } else { var extensions = extesionNames.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); Info($"Found {extensions.Length} extension(s) to verify."); var noError = true; foreach (var name in extensions) { var fileName = Path.Combine(fullPath, $"php_{name}.dll"); if (!File.Exists(fileName)) { Error($"* Extension {name} is listed, but on disk the file cannot be found {fileName}"); noError = false; } } if (noError) { Info("All extension(s) listed can be found on disk."); } } } else { Warn($"Cannot find PHP config file {config}. Default settings are used."); } var matched = false; foreach (var system in paths) { if (string.Equals(rootFolder, system, StringComparison.OrdinalIgnoreCase)) { matched = true; break; } } if (matched) { Debug($"PHP installation has been added to Windows Path environment variable."); } else { Error($"PHP installation is not yet added to Windows Path environment variable. Please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); Warn($"Restart Jexus Manager and rerun PHP Diagnostics after changing Windows Path environment variable."); } Debug(string.Empty); // TODO: verify other configuration in php.info. } else if (path.TrimEnd('"').EndsWith("python.exe", StringComparison.OrdinalIgnoreCase)) { } } } catch (Exception ex) { Debug(ex.ToString()); Rollbar.RollbarLocator.RollbarInstance.Error(ex); } })); container.Add( Observable.FromEventPattern <EventArgs>(btnSave, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { var fileName = DialogHelper.ShowSaveFileDialog(null, "Text Files|*.txt|All Files|*.*"); if (string.IsNullOrEmpty(fileName)) { return; } File.WriteAllText(fileName, txtResult.Text); })); container.Add( Observable.FromEventPattern <EventArgs>(btnVerify, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); })); }
public KestrelDiagDialog(IServiceProvider provider, Application application) : base(provider) { InitializeComponent(); using (var client = new WebClient()) { var latest = client.DownloadString("https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/releases-index.json"); JObject content; if (string.IsNullOrWhiteSpace(latest)) { // fallback to resources. using var bytes = new MemoryStream(Resources.releases_index); using var stream = new StreamReader(bytes); using var json = new JsonTextReader(stream); content = (JObject)JToken.ReadFrom(json); } else { content = JObject.Parse(latest); } var releases = content["releases-index"]; foreach (var release in releases) { var link = release["releases.json"]; var info = client.DownloadString(link.Value <string>()); JObject details = JObject.Parse(info); foreach (var actual in details["releases"]) { var runtimeObject = actual["runtime"]; if (runtimeObject == null) { // skip no runtime release. continue; } if (runtimeObject["version"] == null) { continue; } var longVersion = runtimeObject["version"].Value <string>(); var aspNetRuntime = actual["aspnetcore-runtime"]; if (aspNetRuntime == null || !aspNetRuntime.HasValues) { continue; } var aspNetCoreModuleObject = aspNetRuntime["version-aspnetcoremodule"]; if (aspNetCoreModuleObject == null || !aspNetCoreModuleObject.HasValues) { // skip no ASP.NET Core module release. continue; } var aspNetCoreModule = aspNetCoreModuleObject.Values <string>().First(); var phase = release["support-phase"].Value <string>(); var expired = phase == "eol"; if (phase == "preview" || longVersion.Contains("-")) { // skip preview release. continue; } var runtime = Version.Parse(longVersion); if (mappings.ContainsKey(runtime)) { Console.WriteLine($"{runtime}: new {aspNetCoreModule}: old {mappings[runtime].Item1}"); } else { mappings.Add(runtime, new Tuple <Version, bool>(Version.Parse(aspNetCoreModule), expired)); } } } } var container = new CompositeDisposable(); FormClosed += (sender, args) => container.Dispose(); container.Add( Observable.FromEventPattern <EventArgs>(btnGenerate, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); try { Warn("IMPORTANT: This report might contain confidential information. Mask such before sharing to others."); Warn("-----"); Debug($"System Time: {DateTime.Now}"); Debug($"Processor Architecture: {Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")}"); Debug($"OS: {Environment.OSVersion}"); Debug($"Server Type: {application.Server.Mode.AsString(EnumFormat.Description)}"); Debug(string.Empty); var root = application.VirtualDirectories[0].PhysicalPath.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()); if (string.IsNullOrWhiteSpace(root)) { Error("Invalid site root directory is detected."); return; } // check ANCM. var appHost = application.Server.GetApplicationHostConfiguration(); var definitions = new List <SectionDefinition>(); appHost.RootSectionGroup.GetAllDefinitions(definitions); if (!definitions.Any(item => item.Path == "system.webServer/aspNetCore")) { Error($"ASP.NET Core module is not installed as part of IIS. Please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle for more details."); return; } var modules = new ModulesFeature((Module)provider); modules.Load(); Debug($"Scan {modules.Items.Count} installed module(s)."); var hasV1 = modules.Items.FirstOrDefault(item => item.Name == "AspNetCoreModule"); var hasV2 = modules.Items.FirstOrDefault(item => item.Name == "AspNetCoreModuleV2"); if (hasV1 == null && hasV2 == null) { Error($"ASP.NET Core module is not installed as part of IIS. Please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle for more details."); return; } Version ancmVersion = null; if (hasV2 != null) { var file = hasV2.GlobalModule.Image.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()); if (File.Exists(file)) { var info = FileVersionInfo.GetVersionInfo(file); ancmVersion = new Version(info.FileMajorPart, info.FileMinorPart, info.FileBuildPart, info.FilePrivatePart); Info($"ASP.NET Core module version 2 is installed for .NET Core 2.2 and above: {file} ({info.FileVersion})."); } else { Error("ASP.NET Core module version 2 is not installed properly."); } } else { var file = hasV1.GlobalModule.Image.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()); if (File.Exists(hasV1.GlobalModule.Image.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()))) { var info = FileVersionInfo.GetVersionInfo(file); ancmVersion = new Version(info.FileMajorPart, info.FileMinorPart, info.FileBuildPart, info.FilePrivatePart); Info($"ASP.NET Core module version 1 is installed for .NET Core 1.0-2.1: {file} ({info.FileVersion})"); } else { Error("ASP.NET Core module version 2 is not installed properly."); } } // check handler. Debug(string.Empty); var handlers = new HandlersFeature((Module)provider); handlers.Load(); var foundHandlers = new List <HandlersItem>(); Debug($"Scan {handlers.Items.Count} registered handler(s)."); foreach (var item in handlers.Items) { if (item.Modules == "AspNetCoreModule") { if (hasV1 != null) { Info($"* Found a valid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }}."); foundHandlers.Add(item); } else { Error($"* Found an invalid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }} because ASP.NET Core module version 1 is missing."); } } else if (item.Modules == "AspNetCoreModuleV2") { if (hasV2 != null) { Info($"* Found a valid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }}."); foundHandlers.Add(item); } else { Error($"* Found an invalid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }} because ASP.NET Core module version 2 is missing."); } } } if (foundHandlers.Count == 0) { Error($"No valid ASP.NET Core handler is registered for this web site."); Error($"To run ASP.NET Core on IIS, please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index for more details."); return; } var name = application.ApplicationPoolName; var pool = application.Server.ApplicationPools.FirstOrDefault(item => item.Name == name); if (pool == null) { Error($"The application pool '{name}' cannot be found."); return; } var x86 = pool.Enable32BitAppOnWin64; // check VC++ 2015. var cppFile = Path.Combine( Environment.GetFolderPath(x86 ? Environment.SpecialFolder.SystemX86 : Environment.SpecialFolder.System), $"msvcp140.dll"); if (File.Exists(cppFile)) { var cpp = FileVersionInfo.GetVersionInfo(cppFile); if (cpp.FileMinorPart >= 0) { Info($" Visual C++ runtime is detected (expected: 14.0, detected: {cpp.FileVersion}): {cppFile}."); } else { Error($" Visual C++ runtime 14.0 is not detected. Please install it following the tips on https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle."); } } else { Error($" Visual C++ 14.0 runtime is not detected. Please install it following the tips on https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle."); } Info($"The application pool '{name}' is used."); // check ASP.NET version. if (pool.ManagedRuntimeVersion != ApplicationPool.ManagedRuntimeVersionNone) { Error($"The application pool '{name}' is using .NET CLR {pool.ManagedRuntimeVersion}. Please set it to 'No Managed Code'."); } Info($"Pool identity is {pool.ProcessModel}"); var user = application.Server.Mode == WorkingMode.IisExpress ? $"{Environment.UserDomainName}\\{Environment.UserName}" : pool.ProcessModel.ToString(); Warn($"Please ensure pool identity has read access to the content folder {application.PhysicalPath}."); var poolBitness = x86 ? "32" : "64"; Info($"Pool bitness is {poolBitness} bit"); var config = application.GetWebConfiguration(); var section = config.GetSection("system.webServer/aspNetCore"); var processPath = (string)section["processPath"]; var arguments = (string)section["arguments"]; var hostingModel = (string)section["hostingModel"]; Debug($"Scan aspNetCore section."); Debug($" \"processPath\": {processPath}."); Debug($" \"arguments\": {arguments}."); Debug($" \"hostingModel\": {hostingModel}."); if (string.Equals("InProcess", hostingModel, StringComparison.OrdinalIgnoreCase)) { Warn("In-process hosting model is detected. To avoid 500.xx errors, make sure that the bitness of published artifacts matches the application pool bitness"); if (string.Equals("dotnet", processPath, StringComparison.OrdinalIgnoreCase)) { Info("Framework dependent deployment is detected. Skip bitness check."); } else { Info("Self-contained deployment is detected. Check bitness."); var path = processPath; if (!File.Exists(path)) { path = Path.Combine(application.PhysicalPath, path); } if (!File.Exists(path)) { Error($"Cannot locate executable: {path}"); } else { var bit32 = DialogHelper.GetImageArchitecture(path); if (bit32 == x86) { Info($"Published artifacts bitness matches."); } else { var artifactBitness = bit32 ? "32" : "64"; Error($"Published artifacts bitness is {artifactBitness} bit. Mismatch detected."); } } } } if (string.IsNullOrWhiteSpace(processPath) && string.IsNullOrWhiteSpace(arguments)) { Warn("There is no ASP.NET Core web app to analyze."); } else if (string.Equals(processPath, "%LAUNCHER_PATH%", StringComparison.OrdinalIgnoreCase) || string.Equals(arguments, "%LAUNCHER_ARGS%", StringComparison.OrdinalIgnoreCase)) { Warn("Value of processPath or arguments is placeholder used by Visual Studio. This site can only be run from within Visual Studio."); } else { try { var fileName = Path.GetFileName(processPath); string executable; if (string.Equals(fileName, "dotnet.exe", StringComparison.OrdinalIgnoreCase) || string.Equals(fileName, "dotnet", StringComparison.OrdinalIgnoreCase)) { if (arguments.StartsWith("exec ", StringComparison.OrdinalIgnoreCase)) { arguments = arguments.Substring("exec ".Length).Replace("\"", null); } executable = Path.GetFileNameWithoutExtension(arguments); } else { executable = Path.GetFileNameWithoutExtension(processPath); } var runtime = Path.Combine(root, executable + ".deps.json"); if (File.Exists(runtime)) { var reader = JObject.Parse(File.ReadAllText(runtime)); var targetName = (string)reader["runtimeTarget"]["name"]; Debug($"\"runtimeTarget\": {targetName}."); var slash = targetName.IndexOf('/'); if (slash > -1) { targetName = targetName.Substring(0, slash); } var actual = reader["targets"][targetName]; Version aspNetCoreVersion = null; foreach (var item in actual.Children()) { if (item is JProperty prop) { if (prop.Name.Contains("Microsoft.AspNetCore.All/")) { Info($"Runtime is {prop.Name}."); Version.TryParse(prop.Name.Substring(prop.Name.IndexOf('/') + 1), out aspNetCoreVersion); } else if (prop.Name.Contains("Microsoft.AspNetCore.App/")) { Info($"Runtime is {prop.Name}."); Version.TryParse(prop.Name.Substring(prop.Name.IndexOf('/') + 1), out aspNetCoreVersion); } } } if (aspNetCoreVersion != null && aspNetCoreVersion >= Version.Parse("2.1.0")) { if (mappings.ContainsKey(aspNetCoreVersion)) { var expired = mappings[aspNetCoreVersion].Item2; if (expired) { Error($".NET Core version {aspNetCoreVersion} is end-of-life. Please upgrade to a supported version."); } var minimal = mappings[aspNetCoreVersion].Item1; if (ancmVersion == null || ancmVersion < minimal) { Error($"Runtime {aspNetCoreVersion} does not work with ASP.NET Core module version {ancmVersion}. Minimal version is {minimal}."); } if (ancmVersion > minimal && (ancmVersion.Major != minimal.Major || ancmVersion.Minor != minimal.Minor)) { Warn($"Runtime {aspNetCoreVersion} requires ASP.NET Core module version {minimal}. Installed version {ancmVersion} might not be compatible."); } } } } else { Error($"Cannot locate runtime config file {runtime}."); } } catch (Exception ex) { Error("Cannot analyze ASP.NET Core web app successfully."); Rollbar.RollbarLocator.RollbarInstance.Error(ex, new Dictionary <string, object> { { "source", "web app" } }); } } Warn($"Please refer to pages such as https://dotnet.microsoft.com/download/dotnet-core/3.1 to verify that ASP.NET Core version {ancmVersion} matches the runtime of the web app."); } catch (COMException ex) { Error("A generic exception occurred."); Error($"To run ASP.NET Core on IIS, please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index for more details."); Debug(ex.ToString()); Rollbar.RollbarLocator.RollbarInstance.Error(ex); } catch (Exception ex) { Debug(ex.ToString()); Rollbar.RollbarLocator.RollbarInstance.Error(ex); } })); container.Add( Observable.FromEventPattern <EventArgs>(btnSave, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { var fileName = DialogHelper.ShowSaveFileDialog(null, "Text Files|*.txt|All Files|*.*", application.GetActualExecutable()); if (string.IsNullOrEmpty(fileName)) { return; } File.WriteAllText(fileName, txtResult.Text); })); container.Add( Observable.FromEventPattern <EventArgs>(btnVerify, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); })); }
public PhpDiagDialog(IServiceProvider provider, ServerManager server) : base(provider) { InitializeComponent(); var container = new CompositeDisposable(); FormClosed += (sender, args) => container.Dispose(); container.Add( Observable.FromEventPattern <EventArgs>(btnGenerate, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); try { Debug($"System Time: {DateTime.Now}"); Debug($"Processor Architecture: {Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")}"); Debug($"OS: {Environment.OSVersion}"); Debug($"{server.Type}"); Debug(Environment.NewLine); var handlers = new HandlersFeature((Module)provider); handlers.Load(); var foundPhpHandler = false; Debug($"Scan {handlers.Items.Count} registered handler(s)."); foreach (var item in handlers.Items) { if (string.Equals(item.Path, "*.php", StringComparison.OrdinalIgnoreCase)) { Debug($"* Found PHP handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Handler: {item.TypeString}, Entry Type: {item.Flag} }}."); foundPhpHandler = true; } } if (!foundPhpHandler) { Error($"No PHP handler is registered for this web site. Please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); return; } Debug(Environment.NewLine); var fastCgiFeature = new FastCgiFeature((Module)provider); fastCgiFeature.Load(); Debug($"Scan {fastCgiFeature.Items.Count} registered FastCGI application(s)."); var foundPhp = new List <string>(); foreach (var item in fastCgiFeature.Items) { if (item.Path.TrimEnd('"').EndsWith("php-cgi.exe", StringComparison.OrdinalIgnoreCase)) { Debug($"* Found PHP FastCGI application registered as {{ Full path: {item.Path}, Arguments: {item.Arguments} }}."); foundPhp.Add(item.Path); } } if (foundPhp.Count == 0) { Error($"No PHP FastCGI appilcation is registered on this server. Please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); return; } Debug(Environment.NewLine); Debug($"Verify PHP installation versions."); foreach (var path in foundPhp) { var info = FileVersionInfo.GetVersionInfo(path); if (info.FileMajorPart < 5) { Error($"* PHP {info.FileVersion} ({path}) is obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else if (info.FileMajorPart == 5) { if (info.FileMinorPart < 6) { Error($"* PHP {info.FileVersion} ({path}) is obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else if (info.FileMinorPart == 6) { Warn($"* PHP {info.FileVersion} ({path}) will soon be obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else { Error($"* PHP {info.FileVersion} ({path}) is unknown. Please refer to http://php.net/supported-versions.php for more details."); } } else if (info.FileMajorPart == 7) { if (info.FileMinorPart == 0) { Warn($"* PHP {info.FileVersion} ({path}) will soon be obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else { Debug($"* PHP {info.FileVersion} ({path}) is supported. Please refer to http://php.net/supported-versions.php for more details."); } } else { Error($"* PHP {info.FileVersion} ({path}) is unknown. Please refer to http://php.net/supported-versions.php for more details."); } } Debug(Environment.NewLine); var systemPath = Environment.GetEnvironmentVariable("PATH"); Debug($"Scan Windows PATH: {systemPath}."); string[] paths = systemPath.Split(new char[1] { Path.PathSeparator }); foreach (var path in foundPhp) { var folder = Path.GetDirectoryName(path); var matched = false; foreach (var system in paths) { if (string.Equals(folder, system, StringComparison.OrdinalIgnoreCase)) { matched = true; break; } } if (matched) { Debug($"* PHP installation {folder} has been added to Windows PATH."); } else { Error($"* PHP installation {folder} is not yet added to Windows PATH."); } } } catch (Exception ex) { Debug(ex.ToString()); RollbarDotNet.Rollbar.Report(ex); } })); container.Add( Observable.FromEventPattern <EventArgs>(btnSave, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { var fileName = DialogHelper.ShowSaveFileDialog(null, "Text Files|*.txt|All Files|*.*"); if (string.IsNullOrEmpty(fileName)) { return; } File.WriteAllText(fileName, txtResult.Text); })); container.Add( Observable.FromEventPattern <EventArgs>(btnVerify, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); })); }
public PhpDiagDialog(IServiceProvider provider, ServerManager server) : base(provider) { InitializeComponent(); var container = new CompositeDisposable(); FormClosed += (sender, args) => container.Dispose(); container.Add( Observable.FromEventPattern <EventArgs>(btnGenerate, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); try { Debug($"System Time: {DateTime.Now}"); Debug($"Processor Architecture: {Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")}"); Debug($"OS: {Environment.OSVersion}"); Debug($"{server.Type}"); Debug(string.Empty); var handlers = new HandlersFeature((Module)provider); handlers.Load(); var foundPhpHandler = false; Debug($"Scan {handlers.Items.Count} registered handler(s)."); foreach (var item in handlers.Items) { if (string.Equals(item.Path, "*.php", StringComparison.OrdinalIgnoreCase)) { Debug($"* Found PHP handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Handler: {item.TypeString}, Entry Type: {item.Flag} }}."); foundPhpHandler = true; } } if (!foundPhpHandler) { Error($"No PHP handler is registered for this web site. Please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); return; } Debug(string.Empty); var fastCgiFeature = new FastCgiFeature((Module)provider); fastCgiFeature.Load(); Debug($"Scan {fastCgiFeature.Items.Count} registered FastCGI application(s)."); var foundPhp = new List <string>(); foreach (var item in fastCgiFeature.Items) { if (item.Path.TrimEnd('"').EndsWith("php-cgi.exe", StringComparison.OrdinalIgnoreCase)) { Debug($"* Found PHP FastCGI application registered as {{ Full path: {item.Path}, Arguments: {item.Arguments} }}."); foundPhp.Add(item.Path); } } if (foundPhp.Count == 0) { Error($"No PHP FastCGI appilcation is registered on this server. Please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); return; } Debug(Environment.NewLine); Debug($"Verify PHP installation versions."); foreach (var path in foundPhp) { var info = FileVersionInfo.GetVersionInfo(path); if (info.FileMajorPart < 5) { Error($"* PHP {info.FileVersion} ({path}) is obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else if (info.FileMajorPart == 5) { if (info.FileMinorPart < 6) { Error($"* PHP {info.FileVersion} ({path}) is obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else if (info.FileMinorPart == 6) { Warn($"* PHP {info.FileVersion} ({path}) will soon be obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else { Error($"* PHP {info.FileVersion} ({path}) is unknown. Please refer to http://php.net/supported-versions.php for more details."); } } else if (info.FileMajorPart == 7) { if (info.FileMinorPart == 0) { Warn($"* PHP {info.FileVersion} ({path}) will soon be obsolete. Please refer to http://php.net/supported-versions.php for more details."); } else { Debug($"* PHP {info.FileVersion} ({path}) is supported."); } } else { Error($"* PHP {info.FileVersion} ({path}) is unknown. Please refer to http://php.net/supported-versions.php for more details."); } } Debug(string.Empty); var systemPath = Environment.GetEnvironmentVariable("Path"); Debug($"Windows Path: {systemPath}."); Debug(string.Empty); string[] paths = systemPath.Split(new char[1] { Path.PathSeparator }); foreach (var path in foundPhp) { var rootFolder = Path.GetDirectoryName(path); Debug($"[{rootFolder}]"); var config = Path.Combine(rootFolder, "php.ini"); if (File.Exists(config)) { Info($"Found PHP config file {config}."); var parser = new ConcatenateDuplicatedKeysIniDataParser(); parser.Configuration.ConcatenateSeparator = " "; var data = parser.Parse(File.ReadAllText(config)); var extensionFolder = data["PHP"]["extension_dir"]; if (extensionFolder == null) { extensionFolder = "ext"; } var fullPath = Path.Combine(rootFolder, extensionFolder); Info($"PHP loadable extension folder: {fullPath}"); var extesionNames = data["PHP"]["extension"]; if (extesionNames == null) { Info("No extension to verify."); } else { var extensions = extesionNames.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); Info($"Found {extensions.Length} extension(s) to verify."); var noError = true; foreach (var name in extensions) { var fileName = Path.Combine(fullPath, $"php_{name}.dll"); if (!File.Exists(fileName)) { Error($"* Extension {name} is listed, but on disk the file cannot be found {fileName}"); noError = false; } } if (noError) { Info("All extension(s) listed can be found on disk."); } } } else { Warn($"Cannot find PHP config file {config}. Default settings are used."); } var matched = false; foreach (var system in paths) { if (string.Equals(rootFolder, system, StringComparison.OrdinalIgnoreCase)) { matched = true; break; } } if (matched) { Debug($"PHP installation has been added to Windows Path environment."); } else { Error($"PHP installation is not yet added to Windows Path environment. Please refer to https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php#13-download-and-install-php-manually for more details."); } Debug(string.Empty); } // TODO: verify other configurations in php.info. } catch (Exception ex) { Debug(ex.ToString()); RollbarDotNet.Rollbar.Report(ex); } })); container.Add( Observable.FromEventPattern <EventArgs>(btnSave, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { var fileName = DialogHelper.ShowSaveFileDialog(null, "Text Files|*.txt|All Files|*.*"); if (string.IsNullOrEmpty(fileName)) { return; } File.WriteAllText(fileName, txtResult.Text); })); container.Add( Observable.FromEventPattern <EventArgs>(btnVerify, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); })); }
public KestrelDiagDialog(IServiceProvider provider, Application application) : base(provider) { InitializeComponent(); var container = new CompositeDisposable(); FormClosed += (sender, args) => container.Dispose(); container.Add( Observable.FromEventPattern <EventArgs>(btnGenerate, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); try { Warn("IMPORTANT: This report might contain confidential information. Mask such before sharing to others."); Warn("-----"); Debug($"System Time: {DateTime.Now}"); Debug($"Processor Architecture: {Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")}"); Debug($"OS: {Environment.OSVersion}"); Debug($"Server Type: {application.Server.Mode.AsString(EnumFormat.Description)}"); Debug(string.Empty); var root = application.VirtualDirectories[0].PhysicalPath.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()); if (string.IsNullOrWhiteSpace(root)) { Error("Invalid site root directory is detected."); return; } // check ANCM. var appHost = application.Server.GetApplicationHostConfiguration(); var definitions = new List <SectionDefinition>(); appHost.RootSectionGroup.GetAllDefinitions(definitions); if (!definitions.Any(item => item.Path == "system.webServer/aspNetCore")) { Error($"ASP.NET Core module is not installed as part of IIS. Please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle for more details."); return; } var modules = new ModulesFeature((Module)provider); modules.Load(); Debug($"Scan {modules.Items.Count} installed module(s)."); var hasV1 = modules.Items.FirstOrDefault(item => item.Name == "AspNetCoreModule"); var hasV2 = modules.Items.FirstOrDefault(item => item.Name == "AspNetCoreModuleV2"); if (hasV1 == null && hasV2 == null) { Error($"ASP.NET Core module is not installed as part of IIS. Please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle for more details."); return; } Version ancmVersion = null; if (hasV2 != null) { var file = hasV2.GlobalModule.Image.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()); if (File.Exists(file)) { var info = FileVersionInfo.GetVersionInfo(file); ancmVersion = new Version(info.FileMajorPart, info.FileMinorPart, info.FileBuildPart, info.FilePrivatePart); Info($"ASP.NET Core module version 2 is installed for .NET Core 2.2 and above: {file} ({info.FileVersion})."); } else { Error("ASP.NET Core module version 2 is not installed properly."); } } else { var file = hasV1.GlobalModule.Image.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()); if (File.Exists(hasV1.GlobalModule.Image.ExpandIisExpressEnvironmentVariables(application.GetActualExecutable()))) { var info = FileVersionInfo.GetVersionInfo(file); ancmVersion = new Version(info.FileMajorPart, info.FileMinorPart, info.FileBuildPart, info.FilePrivatePart); Info($"ASP.NET Core module version 1 is installed for .NET Core 1.0-2.1: {file} ({info.FileVersion})"); } else { Error("ASP.NET Core module version 2 is not installed properly."); } } // check handler. Debug(string.Empty); var handlers = new HandlersFeature((Module)provider); handlers.Load(); var foundHandlers = new List <HandlersItem>(); Debug($"Scan {handlers.Items.Count} registered handler(s)."); foreach (var item in handlers.Items) { if (item.Modules == "AspNetCoreModule") { if (hasV1 != null) { Info($"* Found a valid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }}."); foundHandlers.Add(item); } else { Error($"* Found an invalid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }} because ASP.NET Core module version 1 is missing."); } } else if (item.Modules == "AspNetCoreModuleV2") { if (hasV2 != null) { Info($"* Found a valid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }}."); foundHandlers.Add(item); } else { Error($"* Found an invalid ASP.NET Core handler as {{ Name: {item.Name}, Path: {item.Path}, State: {item.GetState(handlers.AccessPolicy)}, Module: {item.TypeString}, Entry Type: {item.Flag} }} because ASP.NET Core module version 2 is missing."); } } } if (foundHandlers.Count == 0) { Error($"No valid ASP.NET Core handler is registered for this web site."); Error($"To run ASP.NET Core on IIS, please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index for more details."); return; } var name = application.ApplicationPoolName; var pool = application.Server.ApplicationPools.FirstOrDefault(item => item.Name == name); if (pool == null) { Error($"The application pool '{name}' cannot be found."); } else { // check VC++ 2015. var x86 = pool.Enable32BitAppOnWin64; var cppFile = Path.Combine( Environment.GetFolderPath(x86 ? Environment.SpecialFolder.SystemX86 : Environment.SpecialFolder.System), $"msvcp140.dll"); if (File.Exists(cppFile)) { var cpp = FileVersionInfo.GetVersionInfo(cppFile); if (cpp.FileMinorPart >= 0) { Info($" Visual C++ runtime is detected (expected: 14.0, detected: {cpp.FileVersion}): {cppFile}."); } else { Error($" Visual C++ runtime 14.0 is not detected. Please install it following the tips on https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle."); } } else { Error($" Visual C++ 14.0 runtime is not detected. Please install it following the tips on https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index#install-the-net-core-hosting-bundle."); } // check ASP.NET version. if (pool.ManagedRuntimeVersion != ApplicationPool.ManagedRuntimeVersionNone) { Error($"The application pool '{name}' is using .NET CLR {pool.ManagedRuntimeVersion}. Please set it to 'No Managed Code'."); } } var config = application.GetWebConfiguration(); var section = config.GetSection("system.webServer/aspNetCore"); var processPath = (string)section["processPath"]; var arguments = (string)section["arguments"]; var hostingModel = (string)section["hostingModel"]; Debug($"Scan aspNetCore section."); Debug($" \"processPath\": {processPath}."); Debug($" \"arguments\": {arguments}."); Debug($" \"hostingModel\": {hostingModel}."); if (string.IsNullOrWhiteSpace(processPath) && string.IsNullOrWhiteSpace(arguments)) { Warn("There is no ASP.NET Core web app to analyze."); } else { try { var fileName = Path.GetFileName(processPath); string executable; if (string.Equals(fileName, "dotnet.exe", StringComparison.OrdinalIgnoreCase) || string.Equals(fileName, "dotnet", StringComparison.OrdinalIgnoreCase)) { if (arguments.StartsWith("exec ", StringComparison.OrdinalIgnoreCase)) { arguments = arguments.Substring("exec ".Length).Replace("\"", null); } executable = Path.GetFileNameWithoutExtension(arguments); } else { executable = Path.GetFileNameWithoutExtension(processPath); } var runtime = Path.Combine(root, executable + ".deps.json"); if (File.Exists(runtime)) { var reader = JObject.Parse(File.ReadAllText(runtime)); var targetName = (string)reader["runtimeTarget"]["name"]; Debug($"\"runtimeTarget\": {targetName}."); var slash = targetName.IndexOf('/'); if (slash > -1) { targetName = targetName.Substring(0, slash); } var actual = reader["targets"][targetName]; Version aspNetCoreVersion = null; foreach (var item in actual.Children()) { if (item is JProperty prop) { if (prop.Name.Contains("Microsoft.AspNetCore.All/")) { Info($"Runtime is {prop.Name}."); Version.TryParse(prop.Name.Substring(prop.Name.IndexOf('/') + 1), out aspNetCoreVersion); } else if (prop.Name.Contains("Microsoft.AspNetCore.App/")) { Info($"Runtime is {prop.Name}."); Version.TryParse(prop.Name.Substring(prop.Name.IndexOf('/') + 1), out aspNetCoreVersion); } } } if (aspNetCoreVersion != null && aspNetCoreVersion > Version.Parse("2.1.0")) { if (mappings.ContainsKey(aspNetCoreVersion)) { var minimal = mappings[aspNetCoreVersion]; if (ancmVersion == null || ancmVersion < minimal) { Error($"Runtime {aspNetCoreVersion} does not work with ASP.NET Core module version {ancmVersion}. Minimal version is {minimal}."); } if (ancmVersion > minimal && (ancmVersion.Major != minimal.Major || ancmVersion.Minor != minimal.Minor)) { Warn($"Runtime {aspNetCoreVersion} requires ASP.NET Core module version {minimal}. Installed version {ancmVersion} might not be compatible."); } } } } else { Error($"Cannot locate runtime config file {runtime}."); } } catch (Exception ex) { Error("Cannot analyze ASP.NET Core web app successfully."); Rollbar.RollbarLocator.RollbarInstance.Error(ex, new Dictionary <string, object> { { "source", "web app" } }); } } Warn($"Please refer to pages such as https://dotnet.microsoft.com/download/dotnet-core/2.2 to verify that ASP.NET Core version {ancmVersion} matches the runtime of the web app."); } catch (COMException ex) { Error("A generic exception occurred."); Error($"To run ASP.NET Core on IIS, please refer to https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index for more details."); Debug(ex.ToString()); Rollbar.RollbarLocator.RollbarInstance.Error(ex); } catch (Exception ex) { Debug(ex.ToString()); Rollbar.RollbarLocator.RollbarInstance.Error(ex); } })); container.Add( Observable.FromEventPattern <EventArgs>(btnSave, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { var fileName = DialogHelper.ShowSaveFileDialog(null, "Text Files|*.txt|All Files|*.*", application.GetActualExecutable()); if (string.IsNullOrEmpty(fileName)) { return; } File.WriteAllText(fileName, txtResult.Text); })); container.Add( Observable.FromEventPattern <EventArgs>(btnVerify, "Click") .ObserveOn(System.Threading.SynchronizationContext.Current) .Subscribe(evt => { txtResult.Clear(); })); }