/* ======================================================================== * Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; using System.IO; using System.Linq; using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Opc.Ua.Client.Controls { /// /// A class that provide various common utility functions and shared resources. /// public partial class GuiUtils : UserControl { /// /// Initializes a new instance of the class. /// public GuiUtils() { InitializeComponent(); } /// /// The list of icon images. /// public System.Windows.Forms.ImageList ImageList; /// /// Displays the details of an exception. /// public static void HandleException(string caption, MethodBase method, Exception e) { if (String.IsNullOrEmpty(caption)) { caption = method.Name; } ExceptionDlg.Show(caption, e); } /// /// Defines names for the available 16x16 icons. /// public static class Icons { /// /// An attribute /// public const string Attribute = "SimpleItem"; /// /// A property /// public const string Property = "Property"; /// /// A variable /// public const string Variable = "Variable"; /// /// An object /// public const string Object = "Object"; /// /// A method /// public const string Method = "Method"; /// /// A single computer. /// public const string Computer = "Computer"; /// /// A computer network. /// public const string Network = "Network"; /// /// A folder. /// public const string Folder = "Folder"; /// /// A selected folder. /// public const string SelectedFolder = "SelectedFolder"; /// /// A process or application. /// public const string Process = "Process"; /// /// A certificate /// public const string Certificate = "Certificate"; /// /// An invalid certificate /// public const string InvalidCertificate = "InvalidCertificate"; /// /// A certificate store /// public const string CertificateStore = "CertificateStore"; /// /// A group of users. /// public const string Users = "Users"; /// /// A service. /// public const string Service = "Service"; /// /// A logical drive. /// public const string Drive = "Drive"; /// /// The computer desktop. /// public const string Desktop = "Desktop"; /// /// A single user. /// public const string SingleUser = "SingleUser"; /// /// A group of services. /// public const string ServiceGroup = "ServiceGroup"; /// /// A group of users. /// public const string UserGroup = "UserGroup"; /// /// A green check /// public const string GreenCheck = "GreenCheck"; /// /// A red cross /// public const string RedCross = "RedCross"; /// /// A users icon with a red cross through it. /// public const string UsersRedCross = "UsersRedCross"; } /// /// Uses the command line to override the UA TCP implementation specified in the configuration. /// /// The configuration instance that stores the configurable information for a UA application. /// public static void OverrideUaTcpImplementation(ApplicationConfiguration configuration) { // check if UA TCP configuration included. TransportConfiguration transport = null; for (int ii = 0; ii < configuration.TransportConfigurations.Count; ii++) { if (configuration.TransportConfigurations[ii].UriScheme == Utils.UriSchemeOpcTcp) { transport = configuration.TransportConfigurations[ii]; break; } } } /// /// Displays the UA-TCP configuration in the form. /// /// The form to display the UA-TCP configuration. /// The configuration instance that stores the configurable information for a UA application. public static void DisplayUaTcpImplementation(Form form, ApplicationConfiguration configuration) { // check if UA TCP configuration included. TransportConfiguration transport = null; for (int ii = 0; ii < configuration.TransportConfigurations.Count; ii++) { if (configuration.TransportConfigurations[ii].UriScheme == Utils.UriSchemeOpcTcp) { transport = configuration.TransportConfigurations[ii]; break; } } // check if UA TCP implementation explicitly specified. if (transport != null) { string text = form.Text; int index = text.LastIndexOf("(UA TCP - "); if (index >= 0) { text = text.Substring(0, index); } form.Text = Utils.Format("{0} (UA TCP - C#)", text); } } /// /// Handles a domain validation error. /// /// The caller's text is used as the caption of the shown to provide details about the error. public static bool HandleDomainCheckError(string caption, ServiceResult serviceResult, X509Certificate2 certificate = null) { StringBuilder buffer = new StringBuilder(); buffer.AppendFormat("Certificate could not be validated!\r\n"); buffer.AppendFormat("Validation error(s): \r\n"); buffer.AppendFormat("\t{0}\r\n", serviceResult.StatusCode); if (certificate != null) { buffer.AppendFormat("\r\nSubject: {0}\r\n", certificate.Subject); buffer.AppendFormat("Issuer: {0}\r\n", X509Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer) ? "Self-signed" : certificate.Issuer); buffer.AppendFormat("Valid From: {0}\r\n", certificate.NotBefore); buffer.AppendFormat("Valid To: {0}\r\n", certificate.NotAfter); buffer.AppendFormat("Thumbprint: {0}\r\n\r\n", certificate.Thumbprint); var domains = X509Utils.GetDomainsFromCertficate(certificate); if (domains.Count > 0) { bool comma = false; buffer.AppendFormat("Domains:"); foreach (var domain in domains) { if (comma) { buffer.Append(","); } buffer.AppendFormat(" {0}", domain); comma = true; } buffer.AppendLine(); } } buffer.Append("This certificate validation error indicates that the hostname used to connect"); buffer.Append(" is not listed as a valid hostname in the server certificate."); buffer.Append("\r\n\r\nIgnore error and disable the hostname verification?"); if (MessageBox.Show(buffer.ToString(), caption, MessageBoxButtons.YesNo) == DialogResult.Yes) { return true; } return false; } /// /// Handles a certificate validation error. /// /// The caller's form is used as the caption of the shown to provide details about the error. /// The validator (not used). /// The instance event arguments provided when a certificate validation error occurs. public static void HandleCertificateValidationError(Form form, CertificateValidator validator, CertificateValidationEventArgs e) { HandleCertificateValidationError(form.Text, validator, e); } /// /// Handles a certificate validation error. /// /// The caller's text is used as the caption of the shown to provide details about the error. /// The validator (not used). /// The instance event arguments provided when a certificate validation error occurs. public static void HandleCertificateValidationError(string caption, CertificateValidator validator, CertificateValidationEventArgs e) { StringBuilder buffer = new StringBuilder(); buffer.Append("Certificate could not be validated!\r\n"); buffer.Append("Validation error(s): \r\n"); ServiceResult error = e.Error; while (error != null) { buffer.AppendFormat("- {0}\r\n", error.ToString().Split('\r', '\n').FirstOrDefault()); error = error.InnerResult; } buffer.AppendFormat("\r\nSubject: {0}\r\n", e.Certificate.Subject); buffer.AppendFormat("Issuer: {0}\r\n", (e.Certificate.Subject == e.Certificate.Issuer) ? "Self-signed" : e.Certificate.Issuer); buffer.AppendFormat("Valid From: {0}\r\n", e.Certificate.NotBefore); buffer.AppendFormat("Valid To: {0}\r\n", e.Certificate.NotAfter); buffer.AppendFormat("Thumbprint: {0}\r\n\r\n", e.Certificate.Thumbprint); buffer.Append("Certificate validation errors may indicate an attempt to intercept any data you send "); buffer.Append("to a server or to allow an untrusted client to connect to your server."); buffer.Append("\r\n\r\nAccept anyway?"); if (MessageBox.Show(buffer.ToString(), caption, MessageBoxButtons.YesNo) == DialogResult.Yes) { e.AcceptAll = true; } } /// /// Returns a default value for the data type. /// public static object GetDefaultValue(NodeId datatypeId, int valueRank) { Type type = TypeInfo.GetSystemType(datatypeId, EncodeableFactory.GlobalFactory); if (type == null) { return null; } if (valueRank < 0) { if (type == typeof(String)) { return System.String.Empty; } if (type == typeof(byte[])) { return new byte[0]; } if (type == typeof(NodeId)) { return Opc.Ua.NodeId.Null; } if (type == typeof(ExpandedNodeId)) { return Opc.Ua.ExpandedNodeId.Null; } if (type == typeof(QualifiedName)) { return Opc.Ua.QualifiedName.Null; } if (type == typeof(LocalizedText)) { return Opc.Ua.LocalizedText.Null; } if (type == typeof(Guid)) { return System.Guid.Empty; } if (type == typeof(System.Xml.XmlElement)) { System.Xml.XmlDocument document = new System.Xml.XmlDocument(); document.InnerXml = ""; return document.DocumentElement; } return Activator.CreateInstance(type); } return Array.CreateInstance(type, new int[valueRank]); } /// /// Displays a dialog that allows a use to edit a value. /// public static object EditValue(Session session, object value) { TypeInfo typeInfo = TypeInfo.Construct(value); if (typeInfo != null) { return EditValue(session, value, (uint)typeInfo.BuiltInType, typeInfo.ValueRank); } return null; } /// /// Displays a dialog that allows a use to edit a value. /// public static object EditValue(Session session, object value, NodeId datatypeId, int valueRank) { if (value == null) { value = GetDefaultValue(datatypeId, valueRank); } if (valueRank >= 0) { return new ComplexValueEditDlg().ShowDialog(value); } BuiltInType builtinType = TypeInfo.GetBuiltInType(datatypeId, session.TypeTree); switch (builtinType) { case BuiltInType.Boolean: case BuiltInType.Byte: case BuiltInType.SByte: case BuiltInType.Int16: case BuiltInType.UInt16: case BuiltInType.Int32: case BuiltInType.UInt32: case BuiltInType.Int64: case BuiltInType.UInt64: case BuiltInType.Float: case BuiltInType.Double: case BuiltInType.Enumeration: { return new NumericValueEditDlg().ShowDialog(value, TypeInfo.GetSystemType(builtinType, valueRank)); } case BuiltInType.Number: { return new NumericValueEditDlg().ShowDialog(value, TypeInfo.GetSystemType(BuiltInType.Double, valueRank)); } case BuiltInType.Integer: { return new NumericValueEditDlg().ShowDialog(value, TypeInfo.GetSystemType(BuiltInType.Int64, valueRank)); } case BuiltInType.UInteger: { return new NumericValueEditDlg().ShowDialog(value, TypeInfo.GetSystemType(BuiltInType.UInt64, valueRank)); } case BuiltInType.NodeId: { return new NodeIdValueEditDlg().ShowDialog(session, (NodeId)value); } case BuiltInType.ExpandedNodeId: { return new NodeIdValueEditDlg().ShowDialog(session, (ExpandedNodeId)value); } case BuiltInType.DateTime: { DateTime datetime = (DateTime)value; if (new DateTimeValueEditDlg().ShowDialog(ref datetime)) { return datetime; } return null; } case BuiltInType.QualifiedName: { QualifiedName qname = (QualifiedName)value; string name = new StringValueEditDlg().ShowDialog(qname.Name); if (name != null) { return new QualifiedName(name, qname.NamespaceIndex); } return null; } case BuiltInType.String: { return new StringValueEditDlg().ShowDialog((string)value); } case BuiltInType.LocalizedText: { LocalizedText ltext = (LocalizedText)value; string text = new StringValueEditDlg().ShowDialog(ltext.Text); if (text != null) { return new LocalizedText(ltext.Locale, text); } return null; } } return new ComplexValueEditDlg().ShowDialog(value); } /// /// Returns to display icon for the target of a reference. /// public static string GetTargetIcon(Session session, ReferenceDescription reference) { return GetTargetIcon(session, reference.NodeClass, reference.TypeDefinition); } /// /// Returns to display icon for the target of a reference. /// public static string GetTargetIcon(Session session, NodeClass nodeClass, ExpandedNodeId typeDefinitionId) { // make sure the type definition is in the cache. INode typeDefinition = session.NodeCache.Find(typeDefinitionId); switch (nodeClass) { case NodeClass.Object: { if (session.TypeTree.IsTypeOf(typeDefinitionId, ObjectTypes.FolderType)) { return "Folder"; } return "Object"; } case NodeClass.Variable: { if (session.TypeTree.IsTypeOf(typeDefinitionId, VariableTypes.PropertyType)) { return "Property"; } return "Variable"; } } return nodeClass.ToString(); } #region Private Methods #endregion } }