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 ServerManager(Current)
            {
                Mode = WorkingMode.IisExpress
            };

            _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 ModulesModule();

            module.TestInitialize(_serviceContainer, null);

            _feature = new ModulesFeature(module);
            _feature.Load();
        }
Exemplo n.º 2
0
        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();
            }));
        }
Exemplo n.º 3
0
        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();
            }));
        }
Exemplo n.º 4
0
        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 <EventArgs>(btnOK, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .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 <EventArgs>(txtName, "TextChanged")
                .Merge(Observable.FromEventPattern <EventArgs>(txtExecutable, "TextChanged"))
                .Merge(Observable.FromEventPattern <EventArgs>(txtPath, "TextChanged"))
                .Sample(TimeSpan.FromSeconds(1))
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) &&
                                !string.IsNullOrWhiteSpace(txtModule.Text) &&
                                !string.IsNullOrWhiteSpace(txtPath.Text);
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnRestrictions, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                var dialog = new RestrictionsDialog(ServiceProvider, Item);
                if (dialog.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnBrowse, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                DialogHelper.ShowFileDialog(txtExecutable, "|*.dll||*.exe");
            }));
        }
        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 <EventArgs>(btnOK, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                Item.ScriptProcessor = txtExecutable.Text;
                Item.Modules         = txtModule.Text;
                Item.Name            = txtName.Text;
                Item.Path            = txtPath.Text;
                if ((txtModule.Text == "FastCgiModule" || txtModule.Text == "CgiModule") &&
                    !string.Equals(Path.GetExtension(Item.ScriptProcessor), ".exe", StringComparison.OrdinalIgnoreCase))
                {
                    ShowMessage("The executable specified for the CgiModule or the FastCgiModule should be a .exe file.");
                    return;
                }

                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;
                    }

                    var path         = txtExecutable.Text;
                    string arguments = string.Empty;
                    var index        = path.IndexOf('|');
                    if (index > -1)
                    {
                        arguments = path.Substring(index + 1);
                        path      = path.Substring(0, index);
                    }

                    if (!string.IsNullOrWhiteSpace(path))
                    {
                        var ext = Path.GetExtension(path);
                        if (!string.Equals(ext, ".dll", StringComparison.OrdinalIgnoreCase) &&
                            !string.Equals(ext, ".exe", StringComparison.OrdinalIgnoreCase))
                        {
                            // TODO: test path with spaces case.
                            ShowMessage("The specific executable for the handler must be a .dll or .exe file. If the path to the script processor (only in the case of a .exe file) has spaces, use double quotation marks to specify the executable.",
                                        MessageBoxButtons.OK,
                                        MessageBoxIcon.Warning,
                                        MessageBoxDefaultButton.Button1);
                            return;
                        }

                        if (!File.Exists(path))
                        {
                            ShowMessage(
                                "The specific executable does not exist on the server.",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error,
                                MessageBoxDefaultButton.Button1);
                            return;
                        }
                    }

                    if (txtModule.Text == "FastCgiModule")
                    {
                        // IMPORTANT: it is IIS Manager behavior to prompt before verifying duplicate items.
                        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);
                            fastCgi.Load();
                            if (fastCgi.Items.All(item => item.Path != path && item.Arguments != arguments))
                            {
                                fastCgi.AddItem(new FastCgiItem(null)
                                {
                                    Path = path, Arguments = arguments
                                });
                            }
                        }
                    }
                }

                try
                {
                    if (txtModule.Text != "FastCgiModule" && txtModule.Text != "CgiModule")
                    {
                        var bit32Condition = "bitness32";
                        var bit64Condition = "bitness64";
                        var bit32          = DialogHelper.GetImageArchitecture(txtExecutable.Text);
                        if (bit32 && !Item.PreConditions.Contains(bit32Condition))
                        {
                            Item.PreConditions.Add(bit32Condition);
                        }
                        else if (!bit32 && !Item.PreConditions.Contains(bit64Condition))
                        {
                            Item.PreConditions.Add(bit64Condition);
                        }
                    }
                }
                catch (Exception)
                {
                    ShowMessage(
                        "The specific executable is invalid.",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error,
                        MessageBoxDefaultButton.Button1);
                    return;
                }

                DialogResult = DialogResult.OK;
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(txtName, "TextChanged")
                .Merge(Observable.FromEventPattern <EventArgs>(txtExecutable, "TextChanged"))
                .Merge(Observable.FromEventPattern <EventArgs>(txtPath, "TextChanged"))
                .Sample(TimeSpan.FromSeconds(0.5))
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                btnOK.Enabled = !string.IsNullOrWhiteSpace(txtName.Text) &&
                                !string.IsNullOrWhiteSpace(txtModule.Text) &&
                                !string.IsNullOrWhiteSpace(txtPath.Text);
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnRestrictions, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                var dialog = new RestrictionsDialog(ServiceProvider, Item);
                if (dialog.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnBrowse, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                DialogHelper.ShowOpenFileDialog(txtExecutable, "(*.dll)|*.dll|(*.exe)|*.exe", null);
            }));
        }
Exemplo n.º 6
0
        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();
            }));
        }