diff --git a/README.md b/README.md index 86835572e9..3df7bca9b4 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ This process is identical to the above steps in command line mode. You can alway 1. [Bind the SSL certificate to the port](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate) - e.g. `netsh http add sslcert ipport=0.0.0.0: certhash= appid={F32EDA25-0855-411C-AF5E-F0D042917E2D}` - The `appid` GUID actually doesn't matter, but for sanity, you should use the GUID of TGServerService.exe as printed above + - Power shell users remember to quit the appid: `netsh http add sslcert ipport=0.0.0.0: certhash= appid="{F32EDA25-0855-411C-AF5E-F0D042917E2D}"` as {} has special meaning in powershell 1. Ensure the port can be acccessed from the internet 1. Log in from any computer using a username and password from the service computer in either the CLI or GUI diff --git a/TGCommandLine/Program.cs b/TGCommandLine/Program.cs index 9c5cfd3472..b455578e7c 100644 --- a/TGCommandLine/Program.cs +++ b/TGCommandLine/Program.cs @@ -200,11 +200,9 @@ namespace TGCommandLine return (int)RunCommandLine(argsAsList); } //interactive mode - Interface.SetBadCertificateHandler(BadCertificateInteractive); Console.WriteLine("Type 'instance' to connect to a server instance"); Console.WriteLine("Type 'remote' to connect to a remote service"); - while (true) { Console.Write("Enter command: "); diff --git a/TGControlPanel/ControlPanel/RepoPage.cs b/TGControlPanel/ControlPanel/RepoPage.cs index af30f1f6b7..bdae725354 100644 --- a/TGControlPanel/ControlPanel/RepoPage.cs +++ b/TGControlPanel/ControlPanel/RepoPage.cs @@ -230,7 +230,7 @@ namespace TGControlPanel } void DoAsyncOp(RepoAction ra, string message) { - if (ra != RepoAction.Wait && RepoBusyCheck()) + if (RepoBGW.IsBusy || (ra != RepoAction.Wait && RepoBusyCheck())) return; CurrentRevisionLabel.Visible = false; diff --git a/TGControlPanel/Login.cs b/TGControlPanel/Login.cs index 23a9710d93..4174c9d85f 100644 --- a/TGControlPanel/Login.cs +++ b/TGControlPanel/Login.cs @@ -35,6 +35,7 @@ namespace TGControlPanel { var Config = Properties.Settings.Default; Config.RemoteIP = IPTextBox.Text; + Config.RemotePort = (ushort)PortSelector.Value; Config.RemoteUsername = UsernameTextBox.Text; if (SavePasswordCheckBox.Checked) { diff --git a/TGServiceInterface/Interface.cs b/TGServiceInterface/Interface.cs index e663ed7be3..1d3cb74ad5 100644 --- a/TGServiceInterface/Interface.cs +++ b/TGServiceInterface/Interface.cs @@ -1,446 +1,446 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Reflection; -using System.Security.Principal; -using System.ServiceModel; -using TGServiceInterface.Components; - -namespace TGServiceInterface -{ - /// - /// Main inteface class for the service - /// - sealed public class Interface : IDisposable - { - /// - /// List of s that can be used with and - /// - public static readonly IList ValidInterfaces = CollectComponents(); - - /// - /// The maximum message size to and from a local server - /// - public const long TransferLimitLocal = Int32.MaxValue; //2GB can't go higher - - /// - /// The maximum message size to and from a remote server - /// - public const long TransferLimitRemote = 10485760; //10 MB - - /// - /// Base name of communication URLs - /// - public const string MasterInterfaceName = "TGStationServerService"; - /// - /// Base name of instance URLs - /// - public const string InstanceInterfaceName = MasterInterfaceName + "/Instance"; - - - /// - /// The name of the current instance in use. Defaults to - /// - public string InstanceName { get; private set; } - - /// - /// If this is set, we will try and connect to an HTTPS server running at this address - /// - readonly string HTTPSURL; - - /// - /// The port used by the service - /// - readonly ushort HTTPSPort; - - /// - /// Username for remote operations - /// - readonly string HTTPSUsername; - - /// - /// Password for remote operations - /// - readonly string HTTPSPassword; - - /// - /// Associated list of open s keyed by type name. A in this list may close or fault at any time. Must be locked before being accessed - /// - IDictionary ChannelFactoryCache = new Dictionary(); - - /// - /// Returns a of s that can be used with the service - /// - /// A of s that can be used with the service - static IList CollectComponents() - { - var ServiceComponent = typeof(ITGSService); //this is special - //find all interfaces in this assembly in this namespace that have the service contract attribute - var query = from t in Assembly.GetExecutingAssembly().GetTypes() - where t.IsInterface - && t.Namespace == ServiceComponent.Namespace - && t.GetCustomAttribute(typeof(ServiceContractAttribute)) != null - && t != ServiceComponent - select t; - return query.ToList(); - } - - /// - /// Construct an for a local connection - /// - public Interface() { } - - /// - /// Construct an for a remote connection - /// - /// The address of the remote server - /// The port the remote server runs on - /// Windows account username for the remote server - /// Windows account password for the remote server - public Interface(string address, ushort port, string username, string password) - { - HTTPSURL = address; - HTTPSPort = port; - HTTPSUsername = username; - HTTPSPassword = password; - } - - /// - /// Constructs an that connects to the same as some - /// - /// - public Interface(Interface other) : this(other.HTTPSURL, other.HTTPSPort, other.HTTPSUsername, other.HTTPSPassword) { } - - /// - /// Targets as the instance to use with . Closes all connections to any previous instance - /// - /// The name of the instance to connect to - /// The apporopriate - public ConnectivityLevel ConnectToInstance(string instanceName = null) - { - if (instanceName == null) - instanceName = InstanceName; - return ConnectToInstanceImpl(instanceName, false); - } - - /// - /// Targets as the instance to use with . Closes all connections to any previous instance. Sets - /// - /// The name of the instance to connect to - /// If set to , skips the connectivity and authentication checks and returns - /// The apporopriate - internal ConnectivityLevel ConnectToInstanceImpl(string instanceName, bool skipAuthChecks) - { - if (!ConnectionStatus().HasFlag(ConnectivityLevel.Connected)) - return ConnectivityLevel.None; - var prevInstance = InstanceName; - if (prevInstance != instanceName) - CloseAllChannels(false); - InstanceName = instanceName; - if (skipAuthChecks) - return ConnectivityLevel.Connected; - try - { - GetComponent().VerifyConnection(); - } - catch - { - InstanceName = prevInstance; - return ConnectivityLevel.None; - } - try - { - GetComponent().ServerDirectory(); - } - catch - { - return ConnectivityLevel.Connected; - } - try - { - GetComponent().GetCurrentAuthorizedGroup(); - return ConnectivityLevel.Administrator; - } - catch - { - return ConnectivityLevel.Authenticated; - } - } - - /// - /// Sets the function called when a remote login fails due to the server having an invalid SSL cert - /// - /// The to be called when a remote login is attempted while the server posesses a bad certificate. Passed a of error information about the and should return if it the connection should be made anyway - public static void SetBadCertificateHandler(Func handler) - { - ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) => - { - string ErrorMessage; - switch (error) - { - case SslPolicyErrors.None: - return true; - case SslPolicyErrors.RemoteCertificateChainErrors: - ErrorMessage = "There are certificate chain errors."; - break; - case SslPolicyErrors.RemoteCertificateNameMismatch: - ErrorMessage = "The certificate name does not match."; - break; - case SslPolicyErrors.RemoteCertificateNotAvailable: - ErrorMessage = "The certificate doesn't exist in the trust store."; - break; - default: - ErrorMessage = "An unknown error occurred."; - break; - } - ErrorMessage = String.Format("The server's certificate failed to verify! Error: {0} Cert: {1}", ErrorMessage, cert.ToString()); - return handler(ErrorMessage); - }; - } - - /// - /// Closes all s stored in and clears it - /// - /// If set to , doesn't clear the channels that are used by - void CloseAllChannels(bool includingRoot) - { - string[] RootThings = { typeof(ITGSService).Name, 'S' + typeof(ITGConnectivity).Name }; - lock (ChannelFactoryCache) - { - foreach (var I in ChannelFactoryCache) - { - if (RootThings.Contains(I.Key)) - continue; - var cf = I.Value; - try - { - cf.Closed += ChannelFactory_Closed; - cf.Close(); - } - catch - { - cf.Abort(); - } - ChannelFactoryCache.Remove(I); - } - } - } - - /// - /// Returns if the interface being used to connect to a service does not have the same release version as the service - /// - /// An error message to display to the user should this function return - /// if the interface being used to connect to a service does not have the same release version as the service - public bool VersionMismatch(out string errorMessage) - { - var splits = GetService().Version().Split(' '); - var theirs = new Version(splits[splits.Length - 1].Substring(1)); - var ours = new Version(FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion); - if(theirs.Major != ours.Major || theirs.Minor != ours.Minor || theirs.Revision != ours.Revision) //don't care about the patch level - { - errorMessage = String.Format("Version mismatch between interface version ({0}) and service version ({1}). Some functionality may crash this program.", ours, theirs); - return true; - } - errorMessage = null; - return false; - } - - /// - /// Disposes a closed - /// - /// The channel factory that was closed - /// The event arguments - static void ChannelFactory_Closed(object sender, EventArgs e) - { - (sender as IDisposable).Dispose(); - } - - /// - /// Returns the requested component for the instance . This does not guarantee a successful connection. s created this way are recycled for minimum latency and bandwidth usage - /// - /// The component to retrieve - /// The correct component - public T GetComponent() - { - var ToT = typeof(T); - if (!ValidInterfaces.Contains(ToT) && ToT != typeof(ITGSService)) - throw new Exception("Invalid type!"); - return GetComponentImpl(true); - } - - T GetComponentImpl(bool useInstanceName) - { - if (useInstanceName & InstanceName == null) - throw new Exception("Instance not selected!"); - var actualToT = typeof(T); - var tot = actualToT.Name; - if (actualToT == typeof(ITGConnectivity) && !useInstanceName) - tot = 'S' + tot; - ChannelFactory cf; - - lock (ChannelFactoryCache) - { - if (ChannelFactoryCache.ContainsKey(tot)) - try - { - cf = ((ChannelFactory)ChannelFactoryCache[tot]); - if (cf.State != CommunicationState.Opened) - throw new Exception(); - return cf.CreateChannel(); - } - catch - { - ChannelFactoryCache[tot].Abort(); - ChannelFactoryCache.Remove(tot); - } - cf = CreateChannel(useInstanceName ? InstanceName : null); - ChannelFactoryCache[tot] = cf; - } - return cf.CreateChannel(); - } - - /// - /// Returns the component for the service - /// - /// The component for the service - public ITGSService GetService() - { - return GetComponentImpl(false); - } - - /// - /// Directly creates a for without caching. This should be eventually closed by the caller - /// - /// The component of the channel to be created - /// The correct - /// Thrown if isn't a valid component - ChannelFactory CreateChannel(string instanceName) - { - var accessPath = instanceName == null ? MasterInterfaceName : String.Format("{0}/{1}", InstanceInterfaceName, instanceName); - - var InterfaceName = typeof(T).Name; - if (HTTPSURL == null) - { - var res2 = new ChannelFactory( - new NetNamedPipeBinding { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = TransferLimitLocal }, new EndpointAddress(String.Format("net.pipe://localhost/{0}/{1}", accessPath, InterfaceName))); //10 megs - res2.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation; - return res2; - } - - //okay we're going over - var binding = new WSHttpBinding() - { - SendTimeout = new TimeSpan(0, 0, 40), - MaxReceivedMessageSize = TransferLimitRemote - }; - var requireAuth = InterfaceName != typeof(ITGConnectivity).Name; - binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; - binding.Security.Mode = requireAuth ? SecurityMode.TransportWithMessageCredential : SecurityMode.Transport; //do not require auth for a connectivity check - binding.Security.Message.ClientCredentialType = requireAuth ? MessageCredentialType.UserName : MessageCredentialType.None; - var address = new EndpointAddress(String.Format("https://{0}:{1}/{2}/{3}", HTTPSURL, HTTPSPort, accessPath, InterfaceName)); - var res = new ChannelFactory(binding, address); - if (requireAuth) - { - res.Credentials.UserName.UserName = HTTPSUsername; - res.Credentials.UserName.Password = HTTPSPassword; - } - return res; - } - - /// - /// Used to test if the is avaiable on the target machine. Note that state can change at any time and any call into the may throw an exception because of communcation errors - /// - /// on successful connection, error message on failure - public ConnectivityLevel ConnectionStatus() - { - return ConnectionStatus(out string unused); - } - - /// - /// Used to test if the is avaiable on the target machine. Note that state can change at any time and any call into the may throw an exception because of communcation errors - /// - /// String of the error that prevented an elevated connectivity level - /// The apporopriate - public ConnectivityLevel ConnectionStatus(out string error) - { - try - { - GetComponentImpl(false).VerifyConnection(); - } - catch (CommunicationException e) - { - error = e.ToString(); - return ConnectivityLevel.None; - } - var service = GetService(); - try - { - service.Version(); - } - catch(Exception e) - { - error = e.ToString(); - return ConnectivityLevel.Connected; - } - try - { - // TODO - - error = null; - return ConnectivityLevel.Administrator; - } - catch(Exception e) - { - error = e.ToString(); - return ConnectivityLevel.Authenticated; - } - } - - #region IDisposable Support - /// - /// To detect redundant calls - /// - private bool disposedValue = false; - - /// - /// Implements the pattern. Calls - /// - /// if was called manually, if it was from the finalizer - void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - CloseAllChannels(true); - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~Interface() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - /// - /// Implements the pattern - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Reflection; +using System.Security.Principal; +using System.ServiceModel; +using TGServiceInterface.Components; + +namespace TGServiceInterface +{ + /// + /// Main inteface class for the service + /// + sealed public class Interface : IDisposable + { + /// + /// List of s that can be used with and + /// + public static readonly IList ValidInterfaces = CollectComponents(); + + /// + /// The maximum message size to and from a local server + /// + public const long TransferLimitLocal = Int32.MaxValue; //2GB can't go higher + + /// + /// The maximum message size to and from a remote server + /// + public const long TransferLimitRemote = 10485760; //10 MB + + /// + /// Base name of communication URLs + /// + public const string MasterInterfaceName = "TGStationServerService"; + /// + /// Base name of instance URLs + /// + public const string InstanceInterfaceName = MasterInterfaceName + "/Instance"; + + + /// + /// The name of the current instance in use. Defaults to + /// + public string InstanceName { get; private set; } + + /// + /// If this is set, we will try and connect to an HTTPS server running at this address + /// + readonly string HTTPSURL; + + /// + /// The port used by the service + /// + readonly ushort HTTPSPort; + + /// + /// Username for remote operations + /// + readonly string HTTPSUsername; + + /// + /// Password for remote operations + /// + readonly string HTTPSPassword; + + /// + /// Associated list of open s keyed by type name. A in this list may close or fault at any time. Must be locked before being accessed + /// + IDictionary ChannelFactoryCache = new Dictionary(); + + /// + /// Returns a of s that can be used with the service + /// + /// A of s that can be used with the service + static IList CollectComponents() + { + var ServiceComponent = typeof(ITGSService); //this is special + //find all interfaces in this assembly in this namespace that have the service contract attribute + var query = from t in Assembly.GetExecutingAssembly().GetTypes() + where t.IsInterface + && t.Namespace == ServiceComponent.Namespace + && t.GetCustomAttribute(typeof(ServiceContractAttribute)) != null + && t != ServiceComponent + select t; + return query.ToList(); + } + + /// + /// Sets the function called when a remote login fails due to the server having an invalid SSL cert + /// + /// The to be called when a remote login is attempted while the server posesses a bad certificate. Passed a of error information about the and should return if it the connection should be made anyway + public static void SetBadCertificateHandler(Func handler) + { + ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) => + { + string ErrorMessage; + switch (error) + { + case SslPolicyErrors.None: + return true; + case SslPolicyErrors.RemoteCertificateChainErrors: + ErrorMessage = "There are certificate chain errors."; + break; + case SslPolicyErrors.RemoteCertificateNameMismatch: + ErrorMessage = "The certificate name does not match."; + break; + case SslPolicyErrors.RemoteCertificateNotAvailable: + ErrorMessage = "The certificate doesn't exist in the trust store."; + break; + default: + ErrorMessage = "An unknown error occurred."; + break; + } + ErrorMessage = String.Format("The server's certificate failed to verify! Error: {0} Cert: {1}", ErrorMessage, cert.ToString()); + return handler(ErrorMessage); + }; + } + + /// + /// Construct an for a local connection + /// + public Interface() { } + + /// + /// Construct an for a remote connection + /// + /// The address of the remote server + /// The port the remote server runs on + /// Windows account username for the remote server + /// Windows account password for the remote server + public Interface(string address, ushort port, string username, string password) + { + HTTPSURL = address; + HTTPSPort = port; + HTTPSUsername = username; + HTTPSPassword = password; + } + + /// + /// Constructs an that connects to the same as some + /// + /// + public Interface(Interface other) : this(other.HTTPSURL, other.HTTPSPort, other.HTTPSUsername, other.HTTPSPassword) { } + + /// + /// Targets as the instance to use with . Closes all connections to any previous instance + /// + /// The name of the instance to connect to + /// The apporopriate + public ConnectivityLevel ConnectToInstance(string instanceName = null) + { + if (instanceName == null) + instanceName = InstanceName; + return ConnectToInstanceImpl(instanceName, false); + } + + /// + /// Targets as the instance to use with . Closes all connections to any previous instance. Sets + /// + /// The name of the instance to connect to + /// If set to , skips the connectivity and authentication checks and returns + /// The apporopriate + internal ConnectivityLevel ConnectToInstanceImpl(string instanceName, bool skipAuthChecks) + { + if (!ConnectionStatus().HasFlag(ConnectivityLevel.Connected)) + return ConnectivityLevel.None; + var prevInstance = InstanceName; + if (prevInstance != instanceName) + CloseAllChannels(false); + InstanceName = instanceName; + if (skipAuthChecks) + return ConnectivityLevel.Connected; + try + { + GetComponent().VerifyConnection(); + } + catch + { + InstanceName = prevInstance; + return ConnectivityLevel.None; + } + try + { + GetComponent().ServerDirectory(); + } + catch + { + return ConnectivityLevel.Connected; + } + try + { + GetComponent().GetCurrentAuthorizedGroup(); + return ConnectivityLevel.Administrator; + } + catch + { + return ConnectivityLevel.Authenticated; + } + } + + /// + /// Closes all s stored in and clears it + /// + /// If set to , doesn't clear the channels that are used by + void CloseAllChannels(bool includingRoot) + { + string[] RootThings = { typeof(ITGSService).Name, 'S' + typeof(ITGConnectivity).Name }; + lock (ChannelFactoryCache) + { + foreach (var I in ChannelFactoryCache) + { + if (RootThings.Contains(I.Key)) + continue; + var cf = I.Value; + try + { + cf.Closed += ChannelFactory_Closed; + cf.Close(); + } + catch + { + cf.Abort(); + } + ChannelFactoryCache.Remove(I); + } + } + } + + /// + /// Returns if the interface being used to connect to a service does not have the same release version as the service + /// + /// An error message to display to the user should this function return + /// if the interface being used to connect to a service does not have the same release version as the service + public bool VersionMismatch(out string errorMessage) + { + var splits = GetService().Version().Split(' '); + var theirs = new Version(splits[splits.Length - 1].Substring(1)); + var ours = new Version(FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion); + if(theirs.Major != ours.Major || theirs.Minor != ours.Minor || theirs.Revision != ours.Revision) //don't care about the patch level + { + errorMessage = String.Format("Version mismatch between interface version ({0}) and service version ({1}). Some functionality may crash this program.", ours, theirs); + return true; + } + errorMessage = null; + return false; + } + + /// + /// Disposes a closed + /// + /// The channel factory that was closed + /// The event arguments + static void ChannelFactory_Closed(object sender, EventArgs e) + { + (sender as IDisposable).Dispose(); + } + + /// + /// Returns the requested component for the instance . This does not guarantee a successful connection. s created this way are recycled for minimum latency and bandwidth usage + /// + /// The component to retrieve + /// The correct component + public T GetComponent() + { + var ToT = typeof(T); + if (!ValidInterfaces.Contains(ToT) && ToT != typeof(ITGSService)) + throw new Exception("Invalid type!"); + return GetComponentImpl(true); + } + + T GetComponentImpl(bool useInstanceName) + { + if (useInstanceName & InstanceName == null) + throw new Exception("Instance not selected!"); + var actualToT = typeof(T); + var tot = actualToT.Name; + if (actualToT == typeof(ITGConnectivity) && !useInstanceName) + tot = 'S' + tot; + ChannelFactory cf; + + lock (ChannelFactoryCache) + { + if (ChannelFactoryCache.ContainsKey(tot)) + try + { + cf = ((ChannelFactory)ChannelFactoryCache[tot]); + if (cf.State != CommunicationState.Opened) + throw new Exception(); + return cf.CreateChannel(); + } + catch + { + ChannelFactoryCache[tot].Abort(); + ChannelFactoryCache.Remove(tot); + } + cf = CreateChannel(useInstanceName ? InstanceName : null); + ChannelFactoryCache[tot] = cf; + } + return cf.CreateChannel(); + } + + /// + /// Returns the component for the service + /// + /// The component for the service + public ITGSService GetService() + { + return GetComponentImpl(false); + } + + /// + /// Directly creates a for without caching. This should be eventually closed by the caller + /// + /// The component of the channel to be created + /// The correct + /// Thrown if isn't a valid component + ChannelFactory CreateChannel(string instanceName) + { + var accessPath = instanceName == null ? MasterInterfaceName : String.Format("{0}/{1}", InstanceInterfaceName, instanceName); + + var InterfaceName = typeof(T).Name; + if (HTTPSURL == null) + { + var res2 = new ChannelFactory( + new NetNamedPipeBinding { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = TransferLimitLocal }, new EndpointAddress(String.Format("net.pipe://localhost/{0}/{1}", accessPath, InterfaceName))); //10 megs + res2.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation; + return res2; + } + + //okay we're going over + var binding = new WSHttpBinding() + { + SendTimeout = new TimeSpan(0, 0, 40), + MaxReceivedMessageSize = TransferLimitRemote + }; + var requireAuth = InterfaceName != typeof(ITGConnectivity).Name; + binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; + binding.Security.Mode = requireAuth ? SecurityMode.TransportWithMessageCredential : SecurityMode.Transport; //do not require auth for a connectivity check + binding.Security.Message.ClientCredentialType = requireAuth ? MessageCredentialType.UserName : MessageCredentialType.None; + var address = new EndpointAddress(String.Format("https://{0}:{1}/{2}/{3}", HTTPSURL, HTTPSPort, accessPath, InterfaceName)); + var res = new ChannelFactory(binding, address); + if (requireAuth) + { + res.Credentials.UserName.UserName = HTTPSUsername; + res.Credentials.UserName.Password = HTTPSPassword; + } + return res; + } + + /// + /// Used to test if the is avaiable on the target machine. Note that state can change at any time and any call into the may throw an exception because of communcation errors + /// + /// on successful connection, error message on failure + public ConnectivityLevel ConnectionStatus() + { + return ConnectionStatus(out string unused); + } + + /// + /// Used to test if the is avaiable on the target machine. Note that state can change at any time and any call into the may throw an exception because of communcation errors + /// + /// String of the error that prevented an elevated connectivity level + /// The apporopriate + public ConnectivityLevel ConnectionStatus(out string error) + { + try + { + GetComponentImpl(false).VerifyConnection(); + } + catch (CommunicationException e) + { + error = e.ToString(); + return ConnectivityLevel.None; + } + var service = GetService(); + try + { + service.Version(); + } + catch(Exception e) + { + error = e.ToString(); + return ConnectivityLevel.Connected; + } + try + { + // TODO + + error = null; + return ConnectivityLevel.Administrator; + } + catch(Exception e) + { + error = e.ToString(); + return ConnectivityLevel.Authenticated; + } + } + + #region IDisposable Support + /// + /// To detect redundant calls + /// + private bool disposedValue = false; + + /// + /// Implements the pattern. Calls + /// + /// if was called manually, if it was from the finalizer + void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + CloseAllChannels(true); + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~Interface() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + /// + /// Implements the pattern + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion + } +} diff --git a/Version.cs b/Version.cs index 459e6ec13c..8255119c01 100644 --- a/Version.cs +++ b/Version.cs @@ -12,6 +12,6 @@ using System.Reflection; // [assembly: AssemblyVersion("1.0.*")] //It's impossible to make these a define, don't say I didn't warn you -[assembly: AssemblyVersion("3.1.6.4")] -[assembly: AssemblyFileVersion("3.1.6.4")] -[assembly: AssemblyInformationalVersion("3.1.6.4")] +[assembly: AssemblyVersion("3.1.6.5")] +[assembly: AssemblyFileVersion("3.1.6.5")] +[assembly: AssemblyInformationalVersion("3.1.6.5")]