cmIISConfig MainForm

cmIISConfig MainForm

using System;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Management.Instrumentation;
using System.Net;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.Web.Administration;
using PdMagic.Windows;

namespace cmIISConfig
{
    public partial class MainForm : Form
    {
        private PdIISManager mIISManager;
        private CmConst mConst;
        private bool mUpdating = false;

        public MainForm()
        {
            InitializeComponent();
        }

        private string DialogTitle => $"{mConst.ApplicationName} Install and Configuration Tool v" +
                                      System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;

        private void Form1_Shown(object sender, EventArgs e)
        {
            mIISManager = new PdIISManager();
            mConst = new CmConst(uxConfigureInstanceList.Text);

            // Populate controls from default CmConst properties
            uxInstallerFolder.Text = mConst.InstallersFolder;
            uxNewInstance.Text = mConst.DefaultInstanceName;

            RePopulateInstallers(uxInstallerFolder.Text);

            RePopulateInstanceList(uxUpgradeInstanceList.Items);
            if (uxUpgradeInstanceList.Items.Count > 0)
            {
                uxUpgradeInstanceList.SelectedIndex = 0;
            }
            RePopulateInstanceList(uxConfigureInstanceList.Items);
            if (uxConfigureInstanceList.Items.Count > 0)
            {
                uxConfigureInstanceList.SelectedIndex = 0;
            }

            UpdateControls();
        }

        private void ReCreatecmConst(bool aReportChange)
        {
            if (uxConfigureInstanceList.Text != string.Empty)
            {
                if (aReportChange)
                {
                    AppendToStatusLog(
                        $"All variable names recalculated to reflect Instance={uxConfigureInstanceList.Text}");
                }

                mConst = new CmConst(uxConfigureInstanceList.Text);

                uxUser.Text = mConst.InstanceUserName;
                uxUserGroup.Text = mConst.InstanceUserGroupName;
                uxPassword.Text = "";
            }
        }

        private void RePopulateInstanceList(IList aList)
        {
            aList.Clear();
            
            // list all CrashMagicOnline* folders - these are instances - The suffix is the instance name.
            // No suffix is the "[Default]" instance.

            var lFiles = Directory.GetDirectories($@"{mConst.WindowsProgramFiles}\{mConst.CompanyFolder}",
                $"{mConst.ApplicationFolderBaseName}*");

            foreach (var lFile in lFiles)
            {
                var lFileName = Path.GetFileName(lFile);
                if (lFileName != null)
                {
                    var lInstance = lFileName.Substring(mConst.ApplicationFolderBaseName.Length,
                                                        lFileName.Length - mConst.ApplicationFolderBaseName.Length);
                    if (mConst.InstanceExistsP(lInstance))
                    {
                        aList.Add(lInstance == string.Empty ? mConst.DefaultInstanceName : lInstance);
                    }
                }
            }
        }

        private void RePopulateInstallers(string aFolder)
        {
            uxInstallerList.Items.Clear();
            List<String> lInstallers = Directory.EnumerateFiles(aFolder, "cmoInstall*.zip")
                .Select(Path.GetFileName)
                .ToList();
            foreach (var lInstaller in lInstallers)
            {
                uxInstallerList.Items.Add(lInstaller);
            }

            if (uxInstallerList.Items.Count > 0)
            {
                uxInstallerList.SelectedIndex = 0;
            }
        }

        private void ShowMessageBox(string aMessage, Exception aException = null)
        {
            var lMessage = aException == null
                ? $"{aMessage}"
                : $"{aMessage}:\n{PdActiveDirectory.CleanedException(aException)}";
            MessageBox.Show(lMessage, DialogTitle, MessageBoxButtons.OK, MessageBoxIcon.Hand);
        }

        private void AppendToStatusLog(string aLine)
        {
            uxStatusLog.AppendText(aLine + System.Environment.NewLine);
        }
        private void AppendToStatusLog(List<string> aList)
        {
            foreach (string lLine in aList)
            {
                uxStatusLog.AppendText(lLine + System.Environment.NewLine);
            }
        }

        public void AppendToStatusLog(string text, Color color)
        {
            uxStatusLog.SelectionStart = uxStatusLog.TextLength;
            uxStatusLog.SelectionLength = 0;

            uxStatusLog.SelectionColor = color;
            uxStatusLog.AppendText(text);
            uxStatusLog.SelectionColor = uxStatusLog.ForeColor;
        }


        private void ShowException(string aLine, Exception aException)
        {
            AppendToStatusLog("Exception: " + aLine + System.Environment.NewLine, Color.Crimson);
            var lCurEx = aException;
            while (lCurEx != null)
            {
                AppendToStatusLog(lCurEx.Message, Color.Crimson);
                lCurEx = lCurEx.InnerException;
            }
        }

        private void UpdateControls()
        {
            if (mUpdating)
            {
                return;
            }

            try
            {
                mUpdating = true;
                Text = DialogTitle;
                try
                {
                    int lIndex;

                    // Remember prior selections
                    string lWebSite = uxWebSite.Text;
                    string lApplication = uxApplication.Text;

                    uxWebSite.Items.Clear();
                    uxWebSite.Items.Add("");
                    uxApplication.Items.Clear();
                    uxApplicationPool.Items.Clear();
                    uxApplicationPool.Items.Add("");

                    try
                    {
                        uxWebSite.Items.AddRange(mIISManager.Sites.ToArray());
                        // Reset prior webSite selection
                        lIndex = uxWebSite.FindStringExact(lWebSite);
                        if (lIndex >= 0)
                        {
                            uxWebSite.SelectedIndex = lIndex;
                        }
                    }
                    catch (Exception lException)
                    {
                        ShowException($"Add websites and set current ({lWebSite})", lException);
                    }

                    try
                    {
                        if (lWebSite != string.Empty)
                        {
                            uxApplication.Items.AddRange(mIISManager.Sites[lWebSite].Applications.ToArray());
                            lIndex = uxApplication.FindStringExact(lApplication);
                            uxApplication.SelectedIndex = lIndex >= 0 ? lIndex : 0;
                        }
                    }
                    catch (Exception lException)
                    {
                        ShowException($"Add applications and select current ({lApplication})", lException);
                    }

                    string lPool = "";
                    try
                    {
                        foreach (ApplicationPool lAp in mIISManager.ApplicationPools)
                        {
                            uxApplicationPool.Items.Add(lAp.Name);
                        }

                        var lSlashPos = uxApplication.Text.IndexOf("/");
                        if (lSlashPos >= 0)
                        {
                            var lApplicationPath = uxApplication.Text.Substring(lSlashPos);
                            if (lWebSite != string.Empty)
                            {
                                lPool = mIISManager.Sites[lWebSite].Applications[lApplicationPath].ApplicationPoolName;
                                if (lPool != string.Empty)
                                {
                                    lIndex = uxApplicationPool.FindStringExact(lPool);
                                    if (lIndex >= 0)
                                    {
                                        uxApplicationPool.SelectedIndex = lIndex;
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception lException)
                    {
                        ShowException($"List application pools and set current ({lPool})", lException);
                    }
                }

                finally
                {
                    mUpdating = false;
                }
            }
            catch (Exception lUpdateControls)
            {
                ShowException("UpdateControls", lUpdateControls);
            }
        }

        private bool ReportMissingParameters()
        {
            bool lMissing = false;

            if ((uxUserGroup.Text.Trim() == string.Empty) | (uxUser.Text.Trim() == string.Empty) |
                (uxPassword.Text.Trim() == string.Empty))
            {
                ShowMessageBox("Please enter a group, login and password before executing this operation");
                lMissing = true;
            }

            return lMissing;
        }

        private void PrepareSiteAndApplication(string aSiteName, string aApplicationPath)
        {
            if (ReportMissingParameters())
                return;

            var lStatus = new List<string>();
            try
            {
                mIISManager.CreateISAPIHandler(aSiteName, aApplicationPath, aSiteName + "_Handler",
                    mConst.IsapiFolder + $@"\{mConst.DllFileName}", lStatus);
                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                ShowException("CreateISAPIHandler - Note: Editing this section of IIS configuration " +
                              "requires that Windows feature 'ASP.NET 4.x' be installed.", lException);
            }

            try
            {
                lStatus.Clear();
                mIISManager.VerifyDLLAllowed("Crash Magic", $@"{mConst.IsapiFolder}\{mConst.DllFileName}",
                    $"{mConst.ApplicationName} {mConst.InstanceName}", lStatus);
                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                ShowException("VerifyDLLAllowed", lException);
            }

            try
            {
                lStatus.Clear();
                mIISManager.SetApplicationUser(aSiteName, aApplicationPath, uxUser.Text, uxPassword.Text,lStatus);
                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                ShowException("SetApplicationUser", lException);
            }

            try
            {
                lStatus.Clear();
                mIISManager.VerifyMimeTypes(aSiteName, aApplicationPath, mConst.MimeEntries, lStatus);
                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                ShowException("Add Mime Types", lException);
            }

            try
            {
                lStatus.Clear();
                mIISManager.VerifyDefaultDocument(aSiteName, aApplicationPath, $"{mConst.DllFileName}", lStatus);
                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                ShowException("AddDefaultDocument", lException);
            }

            try
            {
                lStatus.Clear();
                mIISManager.SetMaxAllowedContentLength(aSiteName, 524288000, lStatus);
                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                ShowException("Set MaxAllowedContentLength", lException);
            }

            try
            {
                lStatus.Clear();
                mIISManager.SetServerRuntimeValues(aSiteName, 2147483647, 4294967295, lStatus);
                AppendToStatusLog( lStatus);
            }
            catch (Exception lException)
            {
                ShowException("Set uploadReadAheadSize, maxRequestEntityAllowed", lException);
            }

            try
            {
                lStatus.Clear();
                mIISManager.VerifyVirtualFolders(aSiteName, aApplicationPath, mConst.VirtualFolders, lStatus);
                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                
                ShowException("VerifyVirtualDirectory", lException);
            }

            UpdateControls();
        }


        private void uxAddNewSite_Click(object sender, EventArgs e)
        {
            try
            {
                var lNewSiteName = mConst.InstanceSiteName;
                if (PdInput.Show(this, "New site name", DialogTitle, ref lNewSiteName) != DialogResult.OK)
                    return;

                var lStatus = new List<string>();
                mIISManager.AddNewSite(lNewSiteName, mConst.IsapiFolder, lStatus);
                AppendToStatusLog( lStatus);

                UpdateControls();
                uxWebSite.SelectedIndex = uxWebSite.FindStringExact(lNewSiteName);
                UpdateControls();
            }
            catch (Exception lException)
            {
                ShowMessageBox("Failed to add new site", lException);
            }
        }

        private void uxCreateNewApp_Click(object sender, EventArgs e)
        {
            try
            {
                var lNewAppName = mConst.InstanceAppName;
                if (PdInput.Show(this, "New application name", DialogTitle, ref lNewAppName) != DialogResult.OK)
                    return;

                var lStatus = new List<string>();
                mIISManager.AddNewApplication(uxWebSite.Text, lNewAppName, mConst.IsapiFolder, lStatus);
                UpdateControls();
                uxApplication.SelectedIndex = uxApplication.FindStringExact(uxWebSite.Text + lNewAppName);
                UpdateControls();

                AppendToStatusLog(lStatus);
            }
            catch (Exception lException)
            {
                ShowMessageBox("Failed to add new application", lException);
            }
        }

        private void uxCreateNewAppPool_Click(object sender, EventArgs e)
        {
            if (ReportMissingParameters())
                return;

            try
            {
                var lNewAppPoolName = mConst.InstanceAppPoolName;
                if (PdInput.Show(this, "New application pool name", DialogTitle, ref lNewAppPoolName) != DialogResult.OK)
                    return;

                var lApplicationPath = uxApplication.Text.Substring(uxApplication.Text.IndexOf("/", StringComparison.Ordinal));
                var lStatus = new List<string>();
                mIISManager.AddNewApplicationPool(lNewAppPoolName, lStatus);
                AppendToStatusLog(lStatus);

                lStatus.Clear();
                mIISManager.AttachSiteToApplicationPool(uxWebSite.Text, lApplicationPath, lNewAppPoolName, lStatus);
                AppendToStatusLog(lStatus);

                UpdateControls();
            }
            catch (Exception lException)
            {
                ShowMessageBox("Failed to add and associate new application pool", lException);
            }
        }

        private void uxSetupPool_Click(object sender, EventArgs e)
        {
            try
            {
                var lStatus = new List<string>();

                mIISManager.SetUpApplicationPool(uxApplicationPool.Text, uxUser.Text, uxPassword.Text, lStatus);
                AppendToStatusLog(lStatus);

                UpdateControls();
            }
            catch (Exception lException)
            {
                ShowMessageBox("Failed to set up application pool", lException);
            }
        }

        private void uxCreateUserAndGroup_Click(object sender, EventArgs e)
        {
            if (ReportMissingParameters())
                return;

            var lAd = new PdActiveDirectory();

            try
            {
                AppendToStatusLog("CREATE USER AND GROUP");
                var lUserGroup = uxUserGroup.Text;
                var lUser = uxUser.Text;

                lAd.CreateLocalUserGroup(lUserGroup, $"{mConst.ApplicationName} User Group");
                AppendToStatusLog($"* Created user group ({lUserGroup})");

                var lPassword = uxPassword.Text;
                lAd.CreateLocalUser(lUser, lPassword, $"{mConst.ApplicationName} User");
                AppendToStatusLog($"* Created user ({lUser})");

                lAd.AddUserToGroup(lUser, lUserGroup);
                AppendToStatusLog($"* Added user ({lUser}) to group ({lUserGroup})");

                const string lIUSRS = "IIS_IUSRS";
                if (lAd.GroupExists(lIUSRS))
                {
                    lAd.AddUserToGroup(lUser, lIUSRS);
                    AppendToStatusLog($"* Added user ({lUser}) to group ({lIUSRS})");
                }
                else
                {
                    AppendToStatusLog($"* ({lIUSRS}) group not found.  (Is IIS installed?)");
                }

                if (lAd.GroupExists("DB2USERS"))
                {
                    lAd.AddUserToGroup(lUser, "DB2USERS");
                    AppendToStatusLog("* Added user ({lUser}) to group DB2USERS");
                }

            }
            catch (Exception lException)
            {
                ShowMessageBox("cmAddAccounts Failed", lException);
            }
        }

        private void StatusCallback(string aStatus)
        {
            uxStatus.Text = aStatus;
            uxStatus.Update();
        }

        private void uxAssignFolderSecurity_Click(object sender, EventArgs e)
        {
            var lAd = new PdActiveDirectory();
            var lOldCursor = Cursor.Current;
            Cursor.Current = Cursors.WaitCursor;
            try
            {
                try
                {
                    var lUserGroup = uxUserGroup.Text;
                    AppendToStatusLog("ASSIGN FOLDER SECURITY");

                    try
                    {
                        lAd.AddDirectorySecurity(mConst.PubFolder, lUserGroup,
                            FileSystemRights.Modify | FileSystemRights.ListDirectory |
                            FileSystemRights.Read | FileSystemRights.Write,
                            AccessControlType.Allow, StatusCallback);
                        AppendToStatusLog($"* Gave group {lUserGroup} modify/list/read/write permissions to {mConst.PubFolder}");
                    }
                    catch (Exception ePubFolder)
                    {
                        ShowException(
                            $"Failed to gave group {lUserGroup} modify/list/read/write permissions to {mConst.PubFolder}",
                            ePubFolder);
                        throw;
                    }
                    try
                    {
                        lAd.AddDirectorySecurity(mConst.IsapiFolder, lUserGroup,
                            FileSystemRights.ListDirectory | FileSystemRights.ReadAndExecute,
                            AccessControlType.Allow, StatusCallback);
                        AppendToStatusLog($"* Gave group {lUserGroup} read .&. execute permissions to {mConst.IsapiFolder}");
                    }
                    catch (Exception eIsapiFolder)
                    {
                        ShowException(
                            $"Failed to gave group {lUserGroup} modify/list/read/write permissions to {mConst.IsapiFolder}",
                            eIsapiFolder);
                        throw;
                    }
                }
                catch (Exception lException)
                {
                    ShowMessageBox("cmAddAccounts Failed", lException);
                }
            }
            finally
            {
                Cursor.Current = lOldCursor;
            }
        }


        private void uxRefresh_Click(object sender, EventArgs e)
        {
            mIISManager.ReLoad();
            UpdateControls();
        }

        private void uxPrepareSiteAndApplication_Click(object sender, EventArgs e)
        {
            try
            {
                PrepareSiteAndApplication(uxWebSite.Text, uxApplication.Text.Substring(uxApplication.Text.IndexOf("/", StringComparison.Ordinal)));
            }
            catch (Exception lException)
            {
                var lCurEx = lException;
                while (lCurEx != null)
                {
                    AppendToStatusLog(lCurEx.Message);
                    lCurEx = lCurEx.InnerException;
                }
            }

        }

        private void uxWebSite_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateControls();
        }

        private void uxApplication_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateControls();
        }

        private void uxClear_Click(object sender, EventArgs e)
        {
            uxStatusLog.Clear();
        }

        private void uxInstanceName_Leave(object sender, EventArgs e)
        {
            ReCreatecmConst(true);
        }

        private void uxInstanceName_TextChanged(object sender, EventArgs e)
        {
            ReCreatecmConst(true);
        }
        private bool RunningAsAdmin()
        {
            try
            {
                var lIdentity = WindowsIdentity.GetCurrent();
                var lPrincipal = new WindowsPrincipal(lIdentity);
                return lPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
            }
            catch
            {
                return false;
            }
        }

        private void uxInstanceName_DropDown(object sender, EventArgs e)
        {
            RePopulateInstanceList(uxConfigureInstanceList.Items);
        }

        private void uxInstallerFolder_Click(object sender, EventArgs e)
        {
            uxSelectInstallerFolder.RootFolder = Environment.SpecialFolder.CommonApplicationData;

            uxSelectInstallerFolder.SelectedPath = mConst.DefaultInstallersFolder;
            if (uxSelectInstallerFolder.ShowDialog() == DialogResult.OK)
            {
                mConst.InstallersFolder = uxSelectInstallerFolder.SelectedPath;
                uxInstallerFolder.Text = mConst.InstallersFolder;
            }
        }

        private void uxInstallerFolder_TextChanged(object sender, EventArgs e)
        {
            mConst.InstallersFolder = uxInstallerFolder.Text;

            RePopulateInstallers( mConst.InstallersFolder);
            if (uxInstallerList.Items.Count > 0)
            {
                uxInstallerList.SelectedIndex = 0;
            }
        }

        private void InstallUpgradeChanged(object sender, EventArgs e)
        {
            if (uxSelectNewInstance.Checked)
            {
                uxNewInstanceControls.Show();
                uxNewInstanceNameControls.Show();
                uxUpgradeInstanceNameControls.Hide();
            }
            else
            {
                RePopulateInstanceList(uxUpgradeInstanceList.Items);
                if (uxUpgradeInstanceList.Items.Count > 0)
                {
                    uxUpgradeInstanceList.SelectedIndex = 0;
                }

                uxNewInstanceControls.Hide();
                uxNewInstanceNameControls.Hide();
                uxUpgradeInstanceNameControls.Show();
            }
            UpdateCopyFilesDescription();
        }

        private void uxUseDefaultInstance_CheckedChanged(object sender, EventArgs e)
        {
            if (uxUseDefaultInstance.Checked)
            {

                uxNewInstance.Text = mConst.DefaultInstanceName;
                uxNewInstance.Visible = false;
            }
            else
            {
                uxNewInstance.Text = string.Empty;
                uxNewInstance.Visible = true;
            }

            UpdateCopyFilesDescription();
        }

        private void uxNewInstance_TextChanged(object sender, EventArgs e)
        {
            if (mConst.InstanceExistsP(uxNewInstance.Text))
            {
                uxNewInstance.BackColor = Color.LightPink;
            }
            else if (!mConst.ValidNewInstanceNameP(uxNewInstance.Text))
            {
                uxNewInstance.BackColor = Color.LightPink;
            }
            else
            {
                uxNewInstance.BackColor = Color.LightGreen;
            }
            UpdateCopyFilesDescription();
        }

        private void uxConfigureInstanceList_DropDownClosed(object sender, EventArgs e)
        {
            if (uxConfigureInstanceList.Text == string.Empty)
            {
                uxConfigureInstanceList.Text = mConst.DefaultInstanceName;
            }
        }

        private void UpdateCopyFilesDescription()
        {
            var lInstanceName = uxSelectNewInstance.Checked
                ? uxNewInstance.Text
                : uxUpgradeInstanceList.Text;
            var lNewVersion = ExtractZipVersion($@"{uxInstallerFolder.Text}\{uxInstallerList.Text}");

            var lIsapiFileName = $@"{mConst.IsapiFolderP(lInstanceName)}\CrashMagicOnline_ISAPI.dll";
            string lExistingVersion;
            if (File.Exists(lIsapiFileName))
            {
                var lFileVersion = FileVersionInfo.GetVersionInfo(lIsapiFileName);
                lExistingVersion = lFileVersion.FileVersion;
            }
            else
            {
                lExistingVersion = "Existing program version not available";
            }


            uxCopyFilesDescription.Text = uxSelectNewInstance.Checked
                ? $"Install new instance of Crash Magic (v{lNewVersion}) named '{mConst.FullInstanceNameP(lInstanceName)}'"
                : $"Upgrade existing (v{lExistingVersion}) instance of Crash Magic named '{mConst.FullInstanceNameP(lInstanceName)}' to v{lNewVersion}";
        }

        private bool ValidControlValues()
        {
            string lInstanceName = uxSelectNewInstance.Checked ? uxNewInstance.Text : uxUpgradeInstanceList.Text;
            string lInstallerPackage = uxInstallerList.Text;

            if (lInstallerPackage == string.Empty)
            {
                MessageBox.Show("Please select an install package and try again.", "Crash Magic Online Installation",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            if (uxSelectNewInstance.Checked)
            {
                if (mConst.InstanceExistsP(lInstanceName))
                {
                    MessageBox.Show(
                        "The specified instance already exists.  Please select a different instance name or upgrade an existing one.",
                        "Crash Magic Online Installation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                else if (!mConst.ValidNewInstanceNameP(lInstanceName))
                {
                    MessageBox.Show("Instance names may only contain letters and numbers, no special characters.",
                        "Crash Magic Online Installation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }

            }
            else if (uxSelectUpgradeInstance.Checked)
            {
                if (uxUpgradeInstanceList.SelectedIndex < 0)
                {
                    MessageBox.Show(
                        "Please select an instance to upgrade, or choose to Install a New Crash Magic Instance.",
                        "Crash Magic Online Installation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
            }


            if (uxSelectNewInstance.Checked)
            {
                if (MessageBox.Show($"Install {uxInstallerList.Text} as a new instanced called {lInstanceName}?",
                        "Crash Magic Online Installation", MessageBoxButtons.YesNo) != DialogResult.Yes)
                {
                    return false;
                }
            }
            else //(uxSelectUpgradeInstance.Checked)
            {
                if (MessageBox.Show($"Upgrade existing instance {lInstanceName} to {uxInstallerList.Text}?",
                        "Crash Magic Online Installation", MessageBoxButtons.YesNo) != DialogResult.Yes)
                {
                    return false;
                }
            }

            return true;
        }

        private string ExtractZipVersion(string aFileName)
        {
            var lVersion = "INSTALL PACKAGE VERSION NOT FOUND";

            try
            {
                using (ZipArchive lArchive = ZipFile.OpenRead(aFileName))
                {
                    var lResult = from currEntry in lArchive.Entries
                        where Path.GetDirectoryName(currEntry.FullName) == "ProgramData"
                        where currEntry.Name == "cmVersionNumber.txt"
                        select currEntry;
                    foreach (ZipArchiveEntry lEntry in lResult)
                    {
                        var lFileName = Path.GetTempFileName();
                        lEntry.ExtractToFile(lFileName, true);
                        lVersion = File.ReadAllText(lFileName);
                        File.Delete(lFileName);
                    }
                }
            }
            catch( Exception lExtract)
            {
                lVersion = "ERROR READING INSTALL PACKAGE: " + lExtract.Message;
            }

            return lVersion;
        }
        private void uxExtractFiles_Click(object sender, EventArgs e)
        {
            if (!ValidControlValues())
            {
                return;
            }

            string lInstanceName = uxSelectNewInstance.Checked ? uxNewInstance.Text : uxUpgradeInstanceList.Text;
            lInstanceName = mConst.NameWithInstanceP("", lInstanceName );

            string lInstallerPackage = uxInstallerList.Text;

            string lZipFileName = $@"{uxInstallerFolder.Text}\{uxInstallerList.Text}";

            try
            {
                using (var lArchive = ZipFile.OpenRead(lZipFileName))
                {
                    uxProgress.Minimum = 0;
                    uxProgress.Maximum = lArchive.Entries.Count;
                    uxProgress.Step = 1;

                    var lDestinationBasePath = string.Empty;


                    foreach (var lEntry in lArchive.Entries)
                    {
                        uxProgress.PerformStep();
                        uxProgress.Text = lEntry.Name;

                        var lCleanedFullName = lEntry.FullName.Replace("/", "\\");

                        if (lEntry.FullName.StartsWith("Common"))
                        {
                            lDestinationBasePath =
                                $@"{Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)}\PdMagic\Common";
                            lCleanedFullName = lCleanedFullName.Substring("Common".Length + 1);
                        }
                        else if (lEntry.FullName.StartsWith("ProgramData"))
                        {
                            lDestinationBasePath =
                                $@"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}\PdMagic\CrashMagicOnline{lInstanceName}";
                            lCleanedFullName = lCleanedFullName.Substring("ProgramData".Length + 1);
                        }
                        else if (lEntry.FullName.StartsWith("ProgramFiles"))
                        {
                            lDestinationBasePath =
                                $@"{Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)}\PdMagic\CrashMagicOnline{lInstanceName}";
                            lCleanedFullName = lCleanedFullName.Substring("ProgramFiles".Length + 1);
                        }
                        else if (lEntry.FullName.StartsWith("System32"))
                        {
                            lDestinationBasePath = $@"{Environment.GetFolderPath(Environment.SpecialFolder.SystemX86)}";
                            lCleanedFullName = lCleanedFullName.Substring("System32".Length + 1);
                        }

                        var lFullFileName = Path.Combine(lDestinationBasePath, lCleanedFullName);
                        var lJustPath = Path.GetDirectoryName(lFullFileName);

                        if ((lJustPath != null) .&. (lJustPath != string.Empty))
                        {
                            Directory.CreateDirectory(lJustPath);
                        }

                        lEntry.ExtractToFile(lFullFileName, true);
                    }
                }
                MessageBox.Show("File copy completed");
            }
            catch (Exception eZip)
            {
                MessageBox.Show("File copy failed: " + eZip.Message);
            }
            uxProgress.Value = 0;
        }

        private void uxVersions_Click(object sender, EventArgs e)
        {
            var lVersionsDlg = new NewVersionDownloads();
            lVersionsDlg.Initialize( mConst );
            lVersionsDlg.ShowDialog();
            RePopulateInstallers( mConst.InstallersFolder );
        }

        private void uxInstallerList_SelectedValueChanged(object sender, EventArgs e)
        {
            UpdateCopyFilesDescription();
        }

        private void uxUpgradeInstanceList_SelectedValueChanged(object sender, EventArgs e)
        {
            UpdateCopyFilesDescription();
        }

        private void uxInstallerList_DropDown(object sender, EventArgs e)
        {
            RePopulateInstallers(mConst.InstallersFolder);
        }
    }
}