using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; using Vanara.Extensions; using Vanara.Security; using static Vanara.PInvoke.NetApi32; namespace Vanara { /// Offline settings for a shared folder. public enum ShareOfflineSettings { /// Only the files and programs that users specify are available offline. OnlySpecified = SHI1005_FLAGS.CSC_CACHE_MANUAL_REINT, /// All files and programs that users open from the shared folder are automatically available offline. All = SHI1005_FLAGS.CSC_CACHE_AUTO_REINT, /// /// All files and programs that users open from the shared folder are automatically available offline and are cached for performance. /// AllOptimized = SHI1005_FLAGS.CSC_CACHE_VDO, /// No files or programs from the shared folder are available offline. 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 STYPE type = (STYPE)uint.MaxValue; internal SharedDevice(string target, string netname, WindowsIdentity accessIdentity) { 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((ref SHARE_INFO_1004 i) => i.shi1004_remark = value, false); } /// Gets a value indicating whether this instance is communication device. /// if this instance is communication device; otherwise, . public bool IsCommDevice => (Type & STYPE.STYPE_MASK) == STYPE.STYPE_DEVICE; /// Gets a value indicating whether this instance is disk drive. /// if this instance is disk drive; otherwise, . public bool IsDiskVolume => (Type & STYPE.STYPE_MASK) == STYPE.STYPE_DISKTREE; /// Gets a value indicating whether this instance is Interprocess Communication. /// if this instance is Interprocess Communication; otherwise, . public bool IsIPC => (Type & STYPE.STYPE_MASK) == STYPE.STYPE_IPC; /// Gets a value indicating whether this instance is a print queue. /// if this instance is a print queue; otherwise, . public bool IsPrintQueue => (Type & STYPE.STYPE_MASK) == STYPE.STYPE_PRINTQ; /// /// Gets a value indicating a special share reserved for interprocess communication (IPC$) or remote administration of the server /// (ADMIN$). Can also refer to administrative shares such as C$, D$, E$, and so forth. /// /// if this instance is special; otherwise, . public bool IsSpecial => Type.IsFlagSet(STYPE.STYPE_SPECIAL); /// Gets a value indicating whether this instance is temporary. /// if this instance is temporary; otherwise, . public bool IsTemporary => Type.IsFlagSet(STYPE.STYPE_TEMPORARY); /// Gets the share name of a resource. /// Returns a value. public string Name { get; } /// Gets or sets the offline settings associated with a disk volume share. /// The offline settings. public ShareOfflineSettings OfflineSettings { get => (ShareOfflineSettings)(GetInfo().shi1005_flags & SHI1005_FLAGS.CSC_MASK_EXT); set => SetInfo((ref SHARE_INFO_1005 i) => i.shi1005_flags = i.shi1005_flags & ~SHI1005_FLAGS.CSC_MASK | (SHI1005_FLAGS)value); } /// /// Gets or sets 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. /// /// /// Returns a value. If the caller does not have rights to get this information, this property returns . /// public string Path { 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. /// /// The access permissions for the share. If the caller does not have rights to get this information, this property returns . /// public RawSecurityDescriptor Permissions { get { try { return GetInfo().shi502_security_descriptor.ToManaged(); } catch { return null; } } set => SetInfo((ref SHARE_INFO_502 i) => i.shi502_security_descriptor = value.ToNative()); } /// /// Gets or sets the maximum number of concurrent connections that the shared resource can accommodate. The number of connections is /// unlimited if the value specified in this member is –1. /// /// The maximum number of concurrent connections. public int UserLimit { 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; private STYPE Type => (uint)type == uint.MaxValue ? type = GetInfo().shi1_type : type; /// Creates the disk volume share. /// /// 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. /// /// On success, a new instance of represented a newly created shared disk. public static SharedDevice CreateDiskVolumeShare(string target, string name, string comment, string path) => 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. /// /// 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. /// /// A combination of values that specify the type of the shared resource. The default is for disk drives. /// /// The identity of the user used to create the device. If this value is , the current user's credentials are used. /// /// On success, a new instance of represented a newly created shared resource. internal static SharedDevice Create([Optional] string target, string name, [Optional] string comment, string path, STYPE type = STYPE.STYPE_DISKTREE, WindowsIdentity identity = null) { 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 => Id.Run(() => NetShareGetInfo(Target, Name)); 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 WindowsIdentity identity; private readonly string target = null; /// 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. /// public SharedDevices(string serverName = null, WindowsIdentity accessIdentity = null) : base(false) { target = serverName; identity = accessIdentity ?? WindowsIdentity.GetCurrent(); } internal SharedDevices(Computer computer) : this(computer.Target, computer.Identity) { } /// Gets the number of elements contained in the . /// The number of elements contained in the . public override int Count { get { var h = 0U; var cnt = 0U; identity.Run(() => NetShareEnum(target, 0, out var _, MAX_PREFERRED_LENGTH, out cnt, out _, ref h).ThrowIfFailed()); return (int)cnt; } } /// Gets an containing the keys of the . /// An containing the keys of the object that implements . public override ICollection Keys => identity.Run(() => NetShareEnum(target).Select(i => i.shi0_netname).ToArray()); /// Creates the specified target. /// 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. /// /// A combination of values that specify the type of the shared resource. /// On success, a new instance of represented a newly created shared resource. public SharedDevice Add(string name, string comment, string path, STYPE type = STYPE.STYPE_DISKTREE) => SharedDevice.Create(target, name, comment, path, type, identity); /// Removes the element with the specified key from the . /// The key of the element to remove. /// /// if the element is successfully removed; otherwise, . This method also returns false /// if key was not found in the original . /// public override bool Remove(string key) => identity.Run(() => NetShareDel(target, key).Succeeded); /// Gets the value associated with the specified key. /// The key whose value to get. /// /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the /// type of the parameter. This parameter is passed uninitialized. /// /// /// if the contains an element with the key; /// otherwise, . /// public override bool TryGetValue(string key, out SharedDevice value) { value = ContainsKey(key) ? new SharedDevice(target, key, identity) : null; return !(value is null); } } }