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