diff --git a/System/Computer/Computer.cs b/System/Computer/Computer.cs index 81ccc982..8919341a 100644 --- a/System/Computer/Computer.cs +++ b/System/Computer/Computer.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Runtime.Serialization; +using Vanara.Security; +using static Vanara.PInvoke.NetApi32; namespace Vanara { @@ -15,6 +18,7 @@ namespace Vanara /// The local computer connected by the current account. public static readonly Computer Local = new Computer(); + private SharedDevices devices; private bool initializing; private string targetServer; private bool targetServerSet; @@ -22,7 +26,6 @@ namespace Vanara private bool userNameSet; private string userPassword; private bool userPasswordSet; - private SharedDevices devices; /// Creates a new instance of a TaskService connecting to the local machine as the current user. public Computer() => Connect(); @@ -56,6 +59,19 @@ namespace Vanara EndInit(); } + //public IEnumerable LocalUsers => LocalUser.GetEnum(Target, UserName, UserPassword); + + //public IEnumerable LocalGroups => LocalGroup.GetEnum(Target, UserName, UserPassword); + + /// Gets the open files associated with this device. + /// Returns a value. + public IEnumerable OpenFiles => Identity.Run(() => NetFileEnum(Target).Select(i => new OpenFile(i))); + + /// Gets the shared devices defined for this computer. + /// Returns a value. + [Browsable(false)] + public SharedDevices SharedDevices => devices ?? (devices = new SharedDevices(this)); + /// Gets or sets the name of the computer that the user is connected to. [Category("Data"), DefaultValue(null), Description("The name of the computer to connect to.")] public string Target @@ -73,15 +89,6 @@ namespace Vanara } } - /// Gets the shared devices defined for this computer. - /// Returns a value. - [Browsable(false)] - public SharedDevices SharedDevices => devices ?? (devices = new SharedDevices(this)); - - //public IEnumerable LocalUsers => LocalUser.GetEnum(Target, UserName, UserPassword); - - //public IEnumerable LocalGroups => LocalGroup.GetEnum(Target, UserName, UserPassword); - /// Gets or sets the user name to be used when connecting to the . /// The user name. [Category("Data"), DefaultValue(null), Description("The user name to be used when connecting.")] @@ -118,6 +125,25 @@ namespace Vanara } } + internal System.Security.Principal.WindowsIdentity Identity + { + get + { + if (UserName is null) + return null; + + var nonUpnIdx = UserName.IndexOf('\\'); + var un = UserName; + string dn = null; + if (nonUpnIdx >= 0) + { + dn = UserName.Substring(0, nonUpnIdx); + un = UserName.Substring(nonUpnIdx + 1); + } + return new Vanara.Security.Principal.WindowsLoggedInIdentity(un, dn, UserPassword).AuthenticatedIdentity; + } + } + /// Signals the object that initialization is starting. public void BeginInit() => initializing = true; @@ -168,24 +194,5 @@ namespace Vanara private bool ShouldSerializeTargetServer() => targetServer != null && !targetServer.Trim('\\').Equals(Environment.MachineName.Trim('\\'), StringComparison.InvariantCultureIgnoreCase); private bool ShouldSerializeUserName() => userName != null && !userName.Equals(Environment.UserName, StringComparison.InvariantCultureIgnoreCase); - - internal System.Security.Principal.WindowsIdentity Identity - { - get - { - if (UserName is null) - return null; - - var nonUpnIdx = UserName.IndexOf('\\'); - var un = UserName; - string dn = null; - if (nonUpnIdx >= 0) - { - dn = UserName.Substring(0, nonUpnIdx); - un = UserName.Substring(nonUpnIdx + 1); - } - return new Vanara.Security.Principal.WindowsLoggedInIdentity(un, dn, UserPassword).AuthenticatedIdentity; - } - } } } \ No newline at end of file diff --git a/System/Computer/SharedDevice.cs b/System/Computer/SharedDevice.cs index ff593db7..deb45733 100644 --- a/System/Computer/SharedDevice.cs +++ b/System/Computer/SharedDevice.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Security.AccessControl; using System.Security.Principal; @@ -28,27 +27,101 @@ namespace Vanara None = SHI1005_FLAGS.CSC_CACHE_NONE, } + /// Represents an open file associated with a share. + public class OpenFile + { + private FILE_INFO_3 fi; + + internal OpenFile(in FILE_INFO_3 i) => fi = i; + + /// Gets the number of file locks on the file, device, or pipe. + /// Returns a value. + public int FileLockCount => (int)fi.fi3_num_locks; + + /// Gets the path of the opened resource. + /// Returns a value. + public string FullPath => fi.fi3_pathname; + + /// Gets the identification number assigned to the resource when it is opened. + /// Returns a value. + public int Id => (int)fi.fi3_id; + + /// + /// Gets the string that specifies which user (on servers that have user-level security) or which computer (on servers that have + /// share-level security) opened the resource. Note that Windows does not support share-level security. + /// + /// The name of the user. + public string UserName => fi.fi3_username; + } + + /// Represents a connection to a shared device. + public class ShareConnection + { + private CONNECTION_INFO_1 ci; + private SharedDevice share; + + internal ShareConnection(in CONNECTION_INFO_1 ci, SharedDevice dev) + { + this.ci = ci; share = dev; + } + + /// + /// If the server sharing the resource is running with user-level security, this value describes which user made the connection. If + /// the server is running with share-level security, this value describes which computer (computername) made the connection. Note + /// that Windows does not support share-level security. + /// + /// Returns a value. + public string ConnectedUser => ci.coni1_username; + + /// Gets the number of users on the connection. + /// Returns a value. + public int ConnectedUserCount => (int)ci.coni1_num_users; + + /// Gets the duration that the connection has been established. + /// The duration of the connection. + public TimeSpan ConnectionDuration => TimeSpan.FromSeconds(ci.coni1_time); + + /// Gets the connection identification number. + /// Returns a value. + public int Id => (int)ci.coni1_id; + + /// Gets the number of files currently open as a result of the connection. + /// Returns a value. + public int OpenFileCount => (int)ci.coni1_num_opens; + + /// Gets the open files associated with this share. + /// Returns a value. + public IEnumerable OpenFiles => share.Id.Run(() => NetFileEnum(share.Target, share.Path).Where(i => share.Path?.Length > 0 || i.fi3_pathname.StartsWith("\\")).Select(i => new OpenFile(i))); + } + /// Represents a shared device on a computer. /// public class SharedDevice : INamedEntity { - private readonly WindowsIdentity identity; - private readonly string target; private STYPE type = (STYPE)uint.MaxValue; internal SharedDevice(string target, string netname, WindowsIdentity accessIdentity) { - identity = accessIdentity; - this.target = target; + Id = accessIdentity; + Target = target; Name = netname; } + private delegate void Setter(ref T value); + + /// + /// Lists all connections made to this shared resource on the target server. If there is more than one user using this connection, + /// then it is possible to get more than one structure for the same connection, but with a different user name. + /// + /// Returns a value. + public IEnumerable Connections => Id.Run(() => NetConnectionEnum(Target, Name).Select(ci => new ShareConnection(ci, this))); + /// Gets or sets an optional comment about the shared resource. /// The resource description. public string Description { get => GetInfo().shi1_remark; - set => SetInfo(new SHARE_INFO_1004 { shi1004_remark = value }); + set => SetInfo((ref SHARE_INFO_1004 i) => i.shi1004_remark = value, false); } /// Gets a value indicating whether this instance is communication device. @@ -87,12 +160,7 @@ namespace Vanara public ShareOfflineSettings OfflineSettings { get => (ShareOfflineSettings)(GetInfo().shi1005_flags & SHI1005_FLAGS.CSC_MASK_EXT); - set - { - var i = GetInfo(); - i.shi1005_flags = i.shi1005_flags & ~SHI1005_FLAGS.CSC_MASK | (SHI1005_FLAGS)value; - SetInfo(i); - } + set => SetInfo((ref SHARE_INFO_1005 i) => i.shi1005_flags = i.shi1005_flags & ~SHI1005_FLAGS.CSC_MASK | (SHI1005_FLAGS)value); } /// @@ -104,16 +172,8 @@ namespace Vanara /// public string Path { - get - { - try { return GetInfo().shi2_path; } catch { return string.Empty; } - } - set - { - var i = GetInfo(); - i.shi2_path = value; - SetInfo(i); - } + get { try { return GetInfo().shi2_path; } catch { return string.Empty; } } + set => SetInfo((ref SHARE_INFO_2 i) => i.shi2_path = value); } /// Gets or sets the permissions of the shared resource. @@ -122,16 +182,8 @@ namespace Vanara /// public RawSecurityDescriptor Permissions { - get - { - try { return GetInfo().shi502_security_descriptor.ToManaged(); } catch { return null; } - } - set - { - var i = GetInfo(); - i.shi502_security_descriptor = value.ToNative(); - SetInfo(i); - } + get { try { return GetInfo().shi502_security_descriptor.ToManaged(); } catch { return null; } } + set => SetInfo((ref SHARE_INFO_502 i) => i.shi502_security_descriptor = value.ToNative()); } /// @@ -141,18 +193,13 @@ namespace Vanara /// The maximum number of concurrent connections. public int UserLimit { - get - { - try { return unchecked((int)GetInfo().shi2_max_uses); } catch { return -1; } - } - set - { - var i = GetInfo(); - i.shi2_max_uses = unchecked((uint)value); - SetInfo(i); - } + get { try { return unchecked((int)GetInfo().shi2_max_uses); } catch { return -1; } } + set => SetInfo((ref SHARE_INFO_2 i) => i.shi2_max_uses = unchecked((uint)value)); } + internal WindowsIdentity Id { get; private set; } + internal string Target { get; private set; } + /// Gets the shared resource's permissions for servers running with share-level security. /// Returns a value. private ShareLevelAccess Access => GetInfo().shi2_permissions; @@ -175,37 +222,49 @@ namespace Vanara Create(target, name, comment, path, STYPE.STYPE_DISKTREE, null); /// Creates the specified target. - /// A string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is - /// , the local computer is used. + /// + /// A string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is + /// , the local computer is used. + /// /// The share name of a resource. /// An optional comment about the shared resource. - /// The local path for the shared resource. For disks, this is the path being shared. For print queues, this is the name of the print - /// queue being shared. + /// + /// The local path for the shared resource. For disks, this is the path being shared. For print queues, this is the name of the print + /// queue being shared. + /// /// A combination of values that specify the type of the shared resource. /// The identity. - /// - /// On success, a new instance of represented a newly created shared resource. - /// + /// On success, a new instance of represented a newly created shared resource. internal static SharedDevice Create(string target, string name, string comment, string path, STYPE type, WindowsIdentity identity) { identity.Run(() => NetShareAdd(target, new SHARE_INFO_2 { shi2_netname = name, shi2_remark = comment, shi2_path = path, shi2_max_uses = unchecked((uint)-1), shi2_type = type })); return new SharedDevice(target, name, identity); } - private T GetInfo() where T : struct => identity.Run(() => NetShareGetInfo(target, Name)); + private T GetInfo() where T : struct => Id.Run(() => NetShareGetInfo(Target, Name)); - private void SetInfo(T s) where T : struct => identity.Run(() => NetShareSetInfo(target, Name, s)); + private void SetInfo(Setter f, bool getFirst = true) where T : struct + { + Id.Run(() => + { + var value = getFirst ? GetInfo() : default; + f(ref value); + NetShareSetInfo(Target, Name, value); + }); + } } /// Represents all the shared devices on a computers. public class SharedDevices : Collections.VirtualDictionary { - private readonly string target = null; private readonly WindowsIdentity identity; + private readonly string target = null; - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// Name of the computer from which to retrieve and manage the shared devices. - /// The Windows identity used to access the shared device information. If this value , the current identity is used. + /// + /// The Windows identity used to access the shared device information. If this value , the current identity is used. + /// public SharedDevices(string serverName = null, WindowsIdentity accessIdentity = null) : base(false) { target = serverName; diff --git a/System/Vanara.SystemServices.csproj b/System/Vanara.SystemServices.csproj index 509b9d73..75933094 100644 --- a/System/Vanara.SystemServices.csproj +++ b/System/Vanara.SystemServices.csproj @@ -78,6 +78,7 @@ BackgroundCopyACLFlags, BackgroundCopyCost, BackgroundCopyErrorContext, Backgrou + diff --git a/UnitTests/System/ComputerTests.cs b/UnitTests/System/ComputerTests.cs new file mode 100644 index 00000000..84e2ce42 --- /dev/null +++ b/UnitTests/System/ComputerTests.cs @@ -0,0 +1,20 @@ +using System; +using Vanara.Diagnostics; +using NUnit.Framework; +using System.Text; +using System.Linq; + +namespace Vanara.Diagnostics.Tests +{ + [TestFixture] + public class ComputerTests + { + [Test] + public void EnumSharesTest() + { + TestContext.WriteLine(string.Join(", ", Computer.Local.SharedDevices.Values.Select(d => d.Name))); + //var remote = new Computer(@"\\HALLAN-SVR", "dahall@byu.net", "msitsdav1d"); + //TestContext.WriteLine(string.Join(", ", remote.SharedDevices.Values.Select(d => d.Name))); + } + } +} \ No newline at end of file diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 7c908e95..a72e9b30 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -135,6 +135,7 @@ +