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