using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Timers; using System.Net; using System.IO; using PlutoServer.MSL; using Yaulw.Net; using Yaulw.Thread; using Yaulw.Assembly; using Yaulw.Tools; using System.Diagnostics; namespace RegistrationAPI_Tester { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } // Use this internal Channel internal const uint INTERNAL_CHANNEL_PORT = 1945; // The Mobile Service Name internal const string MOBILE_API_SERVICE_NAME = "McKesson MSL Mobile Api Server"; // The Mobile Service Exe internal const string MOBILE_API_SERVICE_EXE = "PlutoServer.MSL.exe"; // Use this host to check, if we are on the McKesson Network internal const string MCK_HOST_TO_CHECK_ON_MCK_NETWORK = "ndh1fs01.mckesson.com"; // Used to display non-available fields internal const string NOT_AVAILABLE = "N/A"; // Did the service statup on the McKesson Network? Initialized upon Service Start internal static bool IsOnMcKessonNetwork = false; // To keep track when we last time started the service private DateTime LastTimeServiceWasStarted = DateTime.MinValue; // The current Directory the exe is running from internal static string curRunningDirectory = ""; /// /// Retrieve the LocalFileNameNPath for SetupMini File (TEMP FOLDER) /// /// private static string MiniSetupFileNameNPath { get { string LocalFileNameNPath = Yaulw.Tools.PathNaming.PathEndsWithSlash(Path.GetTempPath()) + "SetupMini.exe"; return LocalFileNameNPath; } } /// /// Retrieve the LocalFileNameNPath for Setup File (TEMP FOLDER) /// /// private static string SetupFileNameNPath { get { string LocalFileNameNPath = Yaulw.Tools.PathNaming.PathEndsWithSlash(Path.GetTempPath()) + "Setup.exe"; return LocalFileNameNPath; } } #region Dispatcher /// /// Dispatcher Timer /// private TTimerDisp dispTimer = null; /// /// Dispatch Event Handler /// /// /// private void ElapsedEventHandler(object sender, ElapsedEventArgs e) { System.Timers.Timer timer = (System.Timers.Timer) sender; timer.Enabled = false; CheckServiceStatus(); timer.Enabled = true; } #endregion #region Set Service Status /// /// Set the Service Status /// /// private void SetServiceStatus(string strStatus, System.Drawing.Color color) { this.lblServiceStatus.Text = strStatus; this.lblServiceStatus.ForeColor = color; } /// /// Set the Service Status /// /// private void SetServiceStatusGood(string strStatus) { SetServiceStatus(strStatus, System.Drawing.Color.Green); } /// /// Set the Service Status (Bad) Disables all readonly text fields and buttons) /// /// private void SetServiceStatusBad(string strStatus) { SetServiceStatus(strStatus, System.Drawing.Color.Red); ClearAllReadOnlyTextFields(); DisableAllButtons(); } /// /// Clear all ReadOnly Text Fields (we are in a bad state) /// private void ClearAllReadOnlyTextFields() { textHostGUID.Text = ""; textExternalIP.Text = ""; textInternalIP.Text = ""; textExternalPort.Text = ""; lblServiceVersion.Text = ""; lblLatestVersion.Text = ""; textDetectedInternalIP.Text = ""; textDetectedExternalIP.Text = ""; } /// /// Disable all buttons (we are in a bad state) /// private void DisableAllButtons() { btnServiceStartStop.Enabled = false; btnUpdateService.Enabled = false; btnUpdateHost.Enabled = false; btnIsServerReachable.Enabled = false; } #endregion #region Versioning /// /// Fetch the Latest Version that is Online /// /// valid version object or null if error occured internal static Version FetchOnlineVerion() { string fetchVersion = Yaulw.Net.WCHelper.ScreenScrapeFromURL(Configuration.CHECK_VERSION_URL); if (!String.IsNullOrEmpty(fetchVersion)) { fetchVersion = fetchVersion.Replace("\n", ""); fetchVersion = fetchVersion.Replace("\r", ""); fetchVersion = fetchVersion.Trim(); try { Version version = new Version(fetchVersion); return version; } catch (Exception) { /* ignore */ } } return null; } /// /// Quick Check to see if the Local Service is out of date /// /// internal static bool IsLocalServiceVersionOutOfDate(string LocalVersion, string OnlineVersion) { if (!String.IsNullOrEmpty(LocalVersion) && !String.IsNullOrEmpty(OnlineVersion)) { Version local = new Version(LocalVersion); Version online = new Version(OnlineVersion); if (online.Major == local.Major && online > local) return true; } return false; } #endregion /// /// Main Status Logic /// private void CheckServiceStatus() { try { // Disable Reinstall by default, enable only in certain states EnableReinstallServiceButton(false); bool bHasConnection = Yaulw.Net.IPHostHelper.HasConnection(); if (!bHasConnection) { SetServiceStatusBad("Network Disconnected"); return; } // Enable the stop button, but only if Last Started has been longer than 2 minutes TimeSpan ts = DateTime.Now - LastTimeServiceWasStarted; bool bEnableStopButton = (ts.TotalMinutes >= 2.0); bool bIsServiceRunning = Yaulw.Process.ServiceW.IsServiceRunning(MOBILE_API_SERVICE_NAME); if (!bIsServiceRunning) { SetServiceStatusBad("Service not running"); btnServiceStartStop.Text = "Start Service"; btnServiceStartStop.Enabled = true; // Enable Update button, if we can access fileVersion of Pluto Service if (File.Exists(curRunningDirectory + MOBILE_API_SERVICE_EXE)) { FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(curRunningDirectory + MOBILE_API_SERVICE_EXE); if (fvi != null) { lblServiceVersion.Text = fvi.FileVersion; EnableButtonIfServiceIsOutOfDate(); } // Enable Reinstall EnableReinstallServiceButton(true); } return; } bool bCanConnectViaPort = Yaulw.Net.IPHostHelper.IsPortOpen(IPAddress.Loopback, (int) INTERNAL_CHANNEL_PORT); if (!bCanConnectViaPort) { SetServiceStatusBad("DiagnoseMobile can't open the port to the Service"); // Service is running so enable the stop button btnServiceStartStop.Text = "Stop Service"; btnServiceStartStop.Enabled = bEnableStopButton; return; } // Service is running so enable the stop button btnServiceStartStop.Text = "Stop Service"; btnServiceStartStop.Enabled = bEnableStopButton; // Now let's start gathering Pluto Data textHostGUID.Text = Pluto.MSL.Api.API.GetHostGUID(); // if we got here, then the above call didn't throw an exception, // so we are doing good SetServiceStatusGood("Running"); textInternalIP.Text = Pluto.MSL.Api.API.GetLocalIP(); // External IP could not be set, so don't confuse the customer, // use N/A instead of the default values IPAddress externalIP = IPAddress.None; if(IPAddress.TryParse(Pluto.MSL.Api.API.GetExternalIP(), out externalIP)) { if (IsOnMcKessonNetwork || externalIP == IPAddress.None) textExternalIP.Text = NOT_AVAILABLE; else textExternalIP.Text = externalIP.ToString(); } // External Port could not be set, so don't confuse the customer, // use N/A instead of the default values int nPort = Pluto.MSL.Api.API.GetExternalPort(); if(nPort == -1) textExternalPort.Text = NOT_AVAILABLE; else textExternalPort.Text = nPort.ToString(); // Check the Versions lblServiceVersion.Text = Pluto.MSL.Api.API.GetAPIVersion(); EnableButtonIfServiceIsOutOfDate(); // Detect thee IP stuff textDetectedInternalIP.Text = Yaulw.Net.IPHostHelper.GetFirstLocalIPAddress().ToString(); externalIP = Yaulw.Net.IPHostHelper.GetExternalIP(); if (IsOnMcKessonNetwork || externalIP == IPAddress.None) textDetectedExternalIP.Text = NOT_AVAILABLE; else textDetectedExternalIP.Text = externalIP.ToString(); // Enable buttons btnUpdateHost.Enabled = true; btnIsServerReachable.Enabled = true; // Enable Reinstall EnableReinstallServiceButton(true); } catch (Exception e) { SetServiceStatusBad(e.Message); } } /// /// Responsible for fetching the online version and enabling the Update Button if /// service is out of date. The lblServiceVersion.Text needs to be set prior to calling /// this function. Will be used to compare. /// private void EnableButtonIfServiceIsOutOfDate() { if (!String.IsNullOrEmpty(lblServiceVersion.Text)) { Version OnlineVersion = FetchOnlineVerion(); if(OnlineVersion != null) { lblLatestVersion.Text = OnlineVersion.ToString(); bool bIsOutOfDate = IsLocalServiceVersionOutOfDate(lblServiceVersion.Text, lblLatestVersion.Text); btnUpdateService.Enabled = bIsOutOfDate; lblUpdate.Visible = bIsOutOfDate; } } } /// /// Responsible for enabling the reinstall Service Button (should only /// be enabled in certain states) /// /// private void EnableReinstallServiceButton(bool bEnable) { lblReinstall.Visible = bEnable; btnReinstall.Enabled = bEnable; } /// /// Main Form OnLoad() /// /// /// private void MainForm_Load(object sender, EventArgs e) { try { // Load the PlutoServer.MSL.config File * Helps us to better test * Configuration.Load(false); // IMP! Set the Determine External IP Address URL Yaulw.Net.IPHostHelper.WHAT_IS_MY_IP_AUTOMATION_URL = Configuration.DETERMINE_EXTERNAL_IP_ADDRESS_URL; // Set the Current Run-time directory curRunningDirectory = PathNaming.PathEndsWithSlash(Path.GetDirectoryName(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry))); // Are we on the McKesson Network? IsOnMcKessonNetwork = Yaulw.Net.IPHostHelper.CanResolveHost(MCK_HOST_TO_CHECK_ON_MCK_NETWORK, true); //IsOnMcKessonNetwork = false; // For Debugging lblIsOnMckessonNetwork.Visible = IsOnMcKessonNetwork; // Set the Pluto API Pluto.MSL.Api.API.SetNetworkSettings(IPAddress.Loopback.ToString(), INTERNAL_CHANNEL_PORT); // Set the Registration API RegistrationAPI.API.SetNetworkSettings(Configuration.REGISTRATION_HOST_URL, (uint)Configuration.REGISTRATION_CHANNEL_PORT); // By Default check the status CheckServiceStatus(); // then start the Status Dispatcher, every 15 seconds dispTimer = new TTimerDisp(ElapsedEventHandler, (int)TimeSpan.FromSeconds(15).TotalMilliseconds, true); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); this.Close(); } } /// /// Check if the IP is a valid IP and also check that the Port is valid /// /// IP Address as a string /// Port as a string /// true, if IP and Port is valid private bool ValidIPandPort(string IP, string Port) { if (!String.IsNullOrEmpty(IP) && !String.IsNullOrEmpty(Port)) { IPAddress ip = IPAddress.Any; uint nPort = 0; bool bValidConfig = IPAddress.TryParse(IP, out ip) && uint.TryParse(Port, out nPort) && (nPort > 0) && IPHostHelper.IsValidIPv4Address(ip, false); return bValidConfig; } return false; } /// /// Update The Service if Needed (auto-closes the app) /// /// /// private void btnUpdateService_Click(object sender, EventArgs e) { try { // Important Sanity Clean-up, this way we know that the service or DiagnoseMobile // always downloads the latest setup and runs the latest setup try { if (File.Exists(MiniSetupFileNameNPath)) File.Delete(MiniSetupFileNameNPath); } catch (Exception) { Yaulw.WinForms.MsgBox.ShowError(this, "Updating the Service failed", String.Format("Failed to delete temporary setup file '{0}'. Please delete the file Manually.", MiniSetupFileNameNPath), MessageBoxButtons.OK); return; } if (Yaulw.Net.WCHelper.DownloadFileFromURL(Configuration.DOWNLOAD_MINISETUP_URL, MiniSetupFileNameNPath, true)) { // * Important * sanity check. Kumar/Jeff found this. State can happen that we can reach the // version url but don't actually download the file which then causes the service to crash when // calling PSetupSpwan (might as well also make sure that the new file is equal to the file we expect) if (File.Exists(MiniSetupFileNameNPath)) { // Stop the service if it exists Yaulw.Installer.Common.StopService(MOBILE_API_SERVICE_NAME, 30); // The auto-update is also forcing a close (however we will also try to committ suicide) Yaulw.Installer.Common.PSetupSpwan(MiniSetupFileNameNPath, "", false); // * Double Knock-out * Force the Service to close here a kill itself here as well, don't know if this even get's called // but might as well make sure and double punch dispTimer.Stop(); Application.Exit(); } } } catch (Exception) { /* ignore */} Yaulw.WinForms.MsgBox.ShowError(this, "Updating the Service failed", String.Format("Failed to download the file to update the service, please download and install manually: '{0}'", Configuration.DOWNLOAD_MINISETUP_URL), MessageBoxButtons.OK); } /// /// Update the IPs /// /// /// private void btnUpdateHost_Click(object sender, EventArgs e) { try { // Always make sure that the HOST_GUID Exists online on the Gateway Server // ~otherwise the update server will always fails and we don't know why, this way the user now knows bool bHostExists = RegistrationAPI.API.DoesServerHostGUIDExist(new Guid(textHostGUID.Text)); if (!bHostExists) { Yaulw.WinForms.MsgBox.ShowInfo(this, "No Practices associated with the Server Host Guid", "No Practices have been associated with the McKesson Mobile Gateway. Use Help->Mobile About to retrieve an Api Key and Api Code for a practice to register a practice with the McKesson Mobile Gateway", MessageBoxButtons.OK); return; } if (IsOnMcKessonNetwork) { bool bIsValidInternalConfig = ValidIPandPort(textDetectedInternalIP.Text, INTERNAL_CHANNEL_PORT.ToString()); if (bIsValidInternalConfig) { if (RegistrationAPI.API.UpdateServer(new Guid(textHostGUID.Text), textDetectedInternalIP.Text, String.Empty, (uint)INTERNAL_CHANNEL_PORT)) Yaulw.WinForms.MsgBox.ShowInfo(this, "Update IP Successful", "Updating the local IP succeeded with the McKesson Mobile Gateway. You are located on the McKesson Network.", MessageBoxButtons.OK); else Yaulw.WinForms.MsgBox.ShowError(this, "Update IP Failed", "Updating the local IP failed with the McKesson Mobile Gateway. You are located on the McKesson Network.", MessageBoxButtons.OK); } else { Yaulw.WinForms.MsgBox.ShowError(this, "Internal IP or Port not valid", String.Format("The internal IP or the Port are invalid internal IP:{0} on Port:{1}", textDetectedInternalIP.Text, INTERNAL_CHANNEL_PORT.ToString()), MessageBoxButtons.OK); } } else { // Check to make sure external IP and Port are valid bool bIsValidExternalConfig = ValidIPandPort(textDetectedExternalIP.Text, textExternalPort.Text); bool bIsValidInternalConfig = ValidIPandPort(textDetectedInternalIP.Text, INTERNAL_CHANNEL_PORT.ToString()); if (bIsValidExternalConfig && bIsValidInternalConfig) { if (RegistrationAPI.API.UpdateServer(new Guid(textHostGUID.Text), textDetectedInternalIP.Text, textDetectedExternalIP.Text, uint.Parse(textExternalPort.Text))) Yaulw.WinForms.MsgBox.ShowInfo(this, "Update IPs Successful", "Updating the local IP and external IP succeeded with the McKesson Mobile Gateway.", MessageBoxButtons.OK); else Yaulw.WinForms.MsgBox.ShowError(this, "Update IPs Failed", "Updating the local IP and external IP failed with the McKesson Mobile Gateway.", MessageBoxButtons.OK); } else if (bIsValidInternalConfig) { if (RegistrationAPI.API.UpdateServer(new Guid(textHostGUID.Text), textDetectedInternalIP.Text, String.Empty, (uint)INTERNAL_CHANNEL_PORT)) Yaulw.WinForms.MsgBox.ShowInfo(this, "Update IP Successful", "Updating the local IP succeeded with the McKesson Mobile Gateway.", MessageBoxButtons.OK); else Yaulw.WinForms.MsgBox.ShowError(this, "Update IP Failed", "Updating the local IP failed with the McKesson Mobile Gateway.", MessageBoxButtons.OK); } else { Yaulw.WinForms.MsgBox.ShowError(this, "External IP/Internal IP/ or Port not valid", String.Format("The external IP / internal IP or the Port are invalid external IP:{0} internal IP:{1} on Port:{2}", textExternalIP.Text, textDetectedInternalIP.Text, textExternalPort.Text), MessageBoxButtons.OK); } } } catch (Exception ex) { Yaulw.WinForms.MsgBox.ShowError(this, "Update IP Exception Thrown", ex.Message, MessageBoxButtons.OK); } } /// /// Is this Server reachable? /// /// /// private void btnIsServerReachable_Click(object sender, EventArgs e) { try { if (IsOnMcKessonNetwork) { Yaulw.WinForms.MsgBox.ShowInfo(this, "McKesson Network", "Server won't be accessible from the Internet. You are located on the McKesson Network.", MessageBoxButtons.OK); } else { bool bValidConfig = ValidIPandPort(textDetectedExternalIP.Text, textExternalPort.Text); if (bValidConfig) { if (RegistrationAPI.API.IsServerReachable(textExternalIP.Text, uint.Parse(textExternalPort.Text))) Yaulw.WinForms.MsgBox.ShowInfo(this, "Communication Success", String.Format("McKesson's Mobile Gateway successfully communicated with IP:{0} on Port:{1}", textExternalIP.Text, textExternalPort.Text), MessageBoxButtons.OK); else Yaulw.WinForms.MsgBox.ShowError(this, "Communication Failed", String.Format("McKesson's Mobile Gateway was unsuccessful in communicating with IP:{0} on Port:{1}", textExternalIP.Text, textExternalPort.Text), MessageBoxButtons.OK); } else { Yaulw.WinForms.MsgBox.ShowError(this, "External IP or Port not valid", String.Format("The external IP or the external Port are invalid IP:{0} on Port:{1}", textExternalIP.Text, textExternalPort.Text), MessageBoxButtons.OK); } } } catch (Exception ex) { Yaulw.WinForms.MsgBox.ShowError(this, "ServerReachable Exception Thrown", ex.Message, MessageBoxButtons.OK); } } /// /// Allows the User to Start/Stop the Service (easily without having to go to SCM to do it) /// /// /// private void btnServiceStartStop_Click(object sender, EventArgs e) { bool bStart = btnServiceStartStop.Text.StartsWith("Start"); if (bStart) { LastTimeServiceWasStarted = DateTime.Now; bool bSuccess = Yaulw.Process.ServiceW.StartService(MOBILE_API_SERVICE_NAME); if (bSuccess) Yaulw.WinForms.MsgBox.ShowInfo(this, MOBILE_API_SERVICE_NAME, "The service successfully started.", MessageBoxButtons.OK); else Yaulw.WinForms.MsgBox.ShowError(this, MOBILE_API_SERVICE_NAME, "The service start was unsuccessful.", MessageBoxButtons.OK); } else { bool bSuccess = Yaulw.Process.ServiceW.StopService(MOBILE_API_SERVICE_NAME); if(bSuccess) Yaulw.WinForms.MsgBox.ShowInfo(this, MOBILE_API_SERVICE_NAME, "The service successfully stopped.", MessageBoxButtons.OK); else Yaulw.WinForms.MsgBox.ShowError(this, MOBILE_API_SERVICE_NAME, "The service stop was unsuccessful.", MessageBoxButtons.OK); } // Refresh the form right aways, since the service start/stop occured may as well, // otherwise it looks a little clumsy CheckServiceStatus(); } /// /// Force a Re-install of the service on this machine (Trouble shooting step) /// /// /// private void btnReinstall_Click(object sender, EventArgs e) { try { DialogResult result = Yaulw.WinForms.MsgBox.ShowInfo(this, "Do you want to continue?", String.Format("You are about to delete the '{0}' and download the complete setup.exe online from:'{1}' and then re-install it. The download may take some time, depending on the speed of your internet connection.", MOBILE_API_SERVICE_NAME, Configuration.DOWNLOAD_SETUP_URL), MessageBoxButtons.YesNo); if (result != System.Windows.Forms.DialogResult.Yes) return; // Important Sanity Clean-up, this way we know that the service or DiagnoseMobile // always downloads the latest setup and runs the latest setup try { if (File.Exists(SetupFileNameNPath)) File.Delete(SetupFileNameNPath); } catch(Exception) { Yaulw.WinForms.MsgBox.ShowError(this, "Reinstalling the Service failed", String.Format("Failed to delete temporary setup file '{0}'. Please delete the file Manually.", SetupFileNameNPath), MessageBoxButtons.OK); return; } if (Yaulw.Net.WCHelper.DownloadFileFromURL(Configuration.DOWNLOAD_SETUP_URL, SetupFileNameNPath, true)) { // * Important * sanity check. Kumar/Jeff found this. State can happen that we can reach the // version url but don't actually download the file which then causes the service to crash when // calling PSetupSpwan (might as well also make sure that the new file is equal to the file we expect) if (File.Exists(SetupFileNameNPath)) { // Stop the service if it exists Yaulw.Installer.Common.StopService(MOBILE_API_SERVICE_NAME, 30); // The auto-update is also forcing a close (however we will also try to committ suicide) Yaulw.Installer.Common.PSetupSpwan(SetupFileNameNPath, "", false); // * Double Knock-out * Force the Service to close here a kill itself here as well, don't know if this even get's called // but might as well make sure and double punch dispTimer.Stop(); Application.Exit(); } } } catch (Exception) { /* ignore */} Yaulw.WinForms.MsgBox.ShowError(this, "Reinstalling the Service failed", String.Format("Failed to download the file to update the service, please download and install manually: '{0}'", Configuration.DOWNLOAD_SETUP_URL), MessageBoxButtons.OK); } } }