aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Common/Net/NetworkShares.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Common/Net/NetworkShares.cs')
-rw-r--r--MediaBrowser.Common/Net/NetworkShares.cs644
1 files changed, 644 insertions, 0 deletions
diff --git a/MediaBrowser.Common/Net/NetworkShares.cs b/MediaBrowser.Common/Net/NetworkShares.cs
new file mode 100644
index 000000000..202865b4c
--- /dev/null
+++ b/MediaBrowser.Common/Net/NetworkShares.cs
@@ -0,0 +1,644 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Runtime.InteropServices;
+
+namespace MediaBrowser.Common.Net
+{
+ /// <summary>
+ /// Type of share
+ /// </summary>
+ [Flags]
+ public enum ShareType
+ {
+ /// <summary>Disk share</summary>
+ Disk = 0,
+ /// <summary>Printer share</summary>
+ Printer = 1,
+ /// <summary>Device share</summary>
+ Device = 2,
+ /// <summary>IPC share</summary>
+ IPC = 3,
+ /// <summary>Special share</summary>
+ Special = -2147483648, // 0x80000000,
+ }
+
+ #region Share
+
+ /// <summary>
+ /// Information about a local share
+ /// </summary>
+ public class Share
+ {
+ #region Private data
+
+ private string _server;
+ private string _netName;
+ private string _path;
+ private ShareType _shareType;
+ private string _remark;
+
+ #endregion
+
+ #region Constructor
+
+ /// <summary>
+ /// Constructor
+ /// </summary>
+ /// <param name="Server"></param>
+ /// <param name="shi"></param>
+ public Share(string server, string netName, string path, ShareType shareType, string remark)
+ {
+ if (ShareType.Special == shareType && "IPC$" == netName)
+ {
+ shareType |= ShareType.IPC;
+ }
+
+ _server = server;
+ _netName = netName;
+ _path = path;
+ _shareType = shareType;
+ _remark = remark;
+ }
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// The name of the computer that this share belongs to
+ /// </summary>
+ public string Server
+ {
+ get { return _server; }
+ }
+
+ /// <summary>
+ /// Share name
+ /// </summary>
+ public string NetName
+ {
+ get { return _netName; }
+ }
+
+ /// <summary>
+ /// Local path
+ /// </summary>
+ public string Path
+ {
+ get { return _path; }
+ }
+
+ /// <summary>
+ /// Share type
+ /// </summary>
+ public ShareType ShareType
+ {
+ get { return _shareType; }
+ }
+
+ /// <summary>
+ /// Comment
+ /// </summary>
+ public string Remark
+ {
+ get { return _remark; }
+ }
+
+ /// <summary>
+ /// Returns true if this is a file system share
+ /// </summary>
+ public bool IsFileSystem
+ {
+ get
+ {
+ // Shared device
+ if (0 != (_shareType & ShareType.Device)) return false;
+ // IPC share
+ if (0 != (_shareType & ShareType.IPC)) return false;
+ // Shared printer
+ if (0 != (_shareType & ShareType.Printer)) return false;
+
+ // Standard disk share
+ if (0 == (_shareType & ShareType.Special)) return true;
+
+ // Special disk share (e.g. C$)
+ if (ShareType.Special == _shareType && null != _netName && 0 != _netName.Length)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Get the root of a disk-based share
+ /// </summary>
+ public DirectoryInfo Root
+ {
+ get
+ {
+ if (IsFileSystem)
+ {
+ if (null == _server || 0 == _server.Length)
+ if (null == _path || 0 == _path.Length)
+ return new DirectoryInfo(ToString());
+ else
+ return new DirectoryInfo(_path);
+ else
+ return new DirectoryInfo(ToString());
+ }
+ else
+ return null;
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Returns the path to this share
+ /// </summary>
+ /// <returns></returns>
+ public override string ToString()
+ {
+ if (null == _server || 0 == _server.Length)
+ {
+ return string.Format(@"\\{0}\{1}", Environment.MachineName, _netName);
+ }
+ else
+ return string.Format(@"\\{0}\{1}", _server, _netName);
+ }
+
+ /// <summary>
+ /// Returns true if this share matches the local path
+ /// </summary>
+ /// <param name="path"></param>
+ /// <returns></returns>
+ public bool MatchesPath(string path)
+ {
+ if (!IsFileSystem) return false;
+ if (null == path || 0 == path.Length) return true;
+
+ return path.ToLower().StartsWith(_path.ToLower());
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// A collection of shares
+ /// </summary>
+ public class ShareCollection : ReadOnlyCollectionBase
+ {
+ #region Platform
+
+ /// <summary>
+ /// Is this an NT platform?
+ /// </summary>
+ protected static bool IsNT
+ {
+ get { return (PlatformID.Win32NT == Environment.OSVersion.Platform); }
+ }
+
+ /// <summary>
+ /// Returns true if this is Windows 2000 or higher
+ /// </summary>
+ protected static bool IsW2KUp
+ {
+ get
+ {
+ OperatingSystem os = Environment.OSVersion;
+ if (PlatformID.Win32NT == os.Platform && os.Version.Major >= 5)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ #endregion
+
+ #region Interop
+
+ #region Constants
+
+ /// <summary>Maximum path length</summary>
+ protected const int MAX_PATH = 260;
+ /// <summary>No error</summary>
+ protected const int NO_ERROR = 0;
+ /// <summary>Access denied</summary>
+ protected const int ERROR_ACCESS_DENIED = 5;
+ /// <summary>Access denied</summary>
+ protected const int ERROR_WRONG_LEVEL = 124;
+ /// <summary>More data available</summary>
+ protected const int ERROR_MORE_DATA = 234;
+ /// <summary>Not connected</summary>
+ protected const int ERROR_NOT_CONNECTED = 2250;
+ /// <summary>Level 1</summary>
+ protected const int UNIVERSAL_NAME_INFO_LEVEL = 1;
+ /// <summary>Max extries (9x)</summary>
+ protected const int MAX_SI50_ENTRIES = 20;
+
+ #endregion
+
+ #region Structures
+
+ /// <summary>Unc name</summary>
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ protected struct UNIVERSAL_NAME_INFO
+ {
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string lpUniversalName;
+ }
+
+ /// <summary>Share information, NT, level 2</summary>
+ /// <remarks>
+ /// Requires admin rights to work.
+ /// </remarks>
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ protected struct SHARE_INFO_2
+ {
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string NetName;
+ public ShareType ShareType;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string Remark;
+ public int Permissions;
+ public int MaxUsers;
+ public int CurrentUsers;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string Path;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string Password;
+ }
+
+ /// <summary>Share information, NT, level 1</summary>
+ /// <remarks>
+ /// Fallback when no admin rights.
+ /// </remarks>
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ protected struct SHARE_INFO_1
+ {
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string NetName;
+ public ShareType ShareType;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string Remark;
+ }
+
+ /// <summary>Share information, Win9x</summary>
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ protected struct SHARE_INFO_50
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
+ public string NetName;
+
+ public byte bShareType;
+ public ushort Flags;
+
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string Remark;
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string Path;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
+ public string PasswordRW;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
+ public string PasswordRO;
+
+ public ShareType ShareType
+ {
+ get { return (ShareType)((int)bShareType & 0x7F); }
+ }
+ }
+
+ /// <summary>Share information level 1, Win9x</summary>
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ protected struct SHARE_INFO_1_9x
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
+ public string NetName;
+ public byte Padding;
+
+ public ushort bShareType;
+
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string Remark;
+
+ public ShareType ShareType
+ {
+ get { return (ShareType)((int)bShareType & 0x7FFF); }
+ }
+ }
+
+ #endregion
+
+ #region Functions
+
+ /// <summary>Get a UNC name</summary>
+ [DllImport("mpr", CharSet = CharSet.Auto)]
+ protected static extern int WNetGetUniversalName(string lpLocalPath,
+ int dwInfoLevel, ref UNIVERSAL_NAME_INFO lpBuffer, ref int lpBufferSize);
+
+ /// <summary>Get a UNC name</summary>
+ [DllImport("mpr", CharSet = CharSet.Auto)]
+ protected static extern int WNetGetUniversalName(string lpLocalPath,
+ int dwInfoLevel, IntPtr lpBuffer, ref int lpBufferSize);
+
+ /// <summary>Enumerate shares (NT)</summary>
+ [DllImport("netapi32", CharSet = CharSet.Unicode)]
+ protected static extern int NetShareEnum(string lpServerName, int dwLevel,
+ out IntPtr lpBuffer, int dwPrefMaxLen, out int entriesRead,
+ out int totalEntries, ref int hResume);
+
+ /// <summary>Enumerate shares (9x)</summary>
+ [DllImport("svrapi", CharSet = CharSet.Ansi)]
+ protected static extern int NetShareEnum(
+ [MarshalAs(UnmanagedType.LPTStr)] string lpServerName, int dwLevel,
+ IntPtr lpBuffer, ushort cbBuffer, out ushort entriesRead,
+ out ushort totalEntries);
+
+ /// <summary>Free the buffer (NT)</summary>
+ [DllImport("netapi32")]
+ protected static extern int NetApiBufferFree(IntPtr lpBuffer);
+
+ #endregion
+
+ #region Enumerate shares
+
+ /// <summary>
+ /// Enumerates the shares on Windows NT
+ /// </summary>
+ /// <param name="server">The server name</param>
+ /// <param name="shares">The ShareCollection</param>
+ protected static void EnumerateSharesNT(string server, ShareCollection shares)
+ {
+ int level = 2;
+ int entriesRead, totalEntries, nRet, hResume = 0;
+ IntPtr pBuffer = IntPtr.Zero;
+
+ try
+ {
+ nRet = NetShareEnum(server, level, out pBuffer, -1,
+ out entriesRead, out totalEntries, ref hResume);
+
+ if (ERROR_ACCESS_DENIED == nRet)
+ {
+ //Need admin for level 2, drop to level 1
+ level = 1;
+ nRet = NetShareEnum(server, level, out pBuffer, -1,
+ out entriesRead, out totalEntries, ref hResume);
+ }
+
+ if (NO_ERROR == nRet && entriesRead > 0)
+ {
+ Type t = (2 == level) ? typeof(SHARE_INFO_2) : typeof(SHARE_INFO_1);
+ int offset = Marshal.SizeOf(t);
+
+ for (int i = 0, lpItem = pBuffer.ToInt32(); i < entriesRead; i++, lpItem += offset)
+ {
+ IntPtr pItem = new IntPtr(lpItem);
+ if (1 == level)
+ {
+ SHARE_INFO_1 si = (SHARE_INFO_1)Marshal.PtrToStructure(pItem, t);
+ shares.Add(si.NetName, string.Empty, si.ShareType, si.Remark);
+ }
+ else
+ {
+ SHARE_INFO_2 si = (SHARE_INFO_2)Marshal.PtrToStructure(pItem, t);
+ shares.Add(si.NetName, si.Path, si.ShareType, si.Remark);
+ }
+ }
+ }
+
+ }
+ finally
+ {
+ // Clean up buffer allocated by system
+ if (IntPtr.Zero != pBuffer)
+ NetApiBufferFree(pBuffer);
+ }
+ }
+
+ /// <summary>
+ /// Enumerates the shares on Windows 9x
+ /// </summary>
+ /// <param name="server">The server name</param>
+ /// <param name="shares">The ShareCollection</param>
+ protected static void EnumerateShares9x(string server, ShareCollection shares)
+ {
+ int level = 50;
+ int nRet = 0;
+ ushort entriesRead, totalEntries;
+
+ Type t = typeof(SHARE_INFO_50);
+ int size = Marshal.SizeOf(t);
+ ushort cbBuffer = (ushort)(MAX_SI50_ENTRIES * size);
+ //On Win9x, must allocate buffer before calling API
+ IntPtr pBuffer = Marshal.AllocHGlobal(cbBuffer);
+
+ try
+ {
+ nRet = NetShareEnum(server, level, pBuffer, cbBuffer,
+ out entriesRead, out totalEntries);
+
+ if (ERROR_WRONG_LEVEL == nRet)
+ {
+ level = 1;
+ t = typeof(SHARE_INFO_1_9x);
+ size = Marshal.SizeOf(t);
+
+ nRet = NetShareEnum(server, level, pBuffer, cbBuffer,
+ out entriesRead, out totalEntries);
+ }
+
+ if (NO_ERROR == nRet || ERROR_MORE_DATA == nRet)
+ {
+ for (int i = 0, lpItem = pBuffer.ToInt32(); i < entriesRead; i++, lpItem += size)
+ {
+ IntPtr pItem = new IntPtr(lpItem);
+
+ if (1 == level)
+ {
+ SHARE_INFO_1_9x si = (SHARE_INFO_1_9x)Marshal.PtrToStructure(pItem, t);
+ shares.Add(si.NetName, string.Empty, si.ShareType, si.Remark);
+ }
+ else
+ {
+ SHARE_INFO_50 si = (SHARE_INFO_50)Marshal.PtrToStructure(pItem, t);
+ shares.Add(si.NetName, si.Path, si.ShareType, si.Remark);
+ }
+ }
+ }
+ else
+ Console.WriteLine(nRet);
+
+ }
+ finally
+ {
+ //Clean up buffer
+ Marshal.FreeHGlobal(pBuffer);
+ }
+ }
+
+ /// <summary>
+ /// Enumerates the shares
+ /// </summary>
+ /// <param name="server">The server name</param>
+ /// <param name="shares">The ShareCollection</param>
+ protected static void EnumerateShares(string server, ShareCollection shares)
+ {
+ if (null != server && 0 != server.Length && !IsW2KUp)
+ {
+ server = server.ToUpper();
+
+ // On NT4, 9x and Me, server has to start with "\\"
+ if (!('\\' == server[0] && '\\' == server[1]))
+ server = @"\\" + server;
+ }
+
+ if (IsNT)
+ EnumerateSharesNT(server, shares);
+ else
+ EnumerateShares9x(server, shares);
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Static methods
+
+ /// <summary>
+ /// Returns true if fileName is a valid local file-name of the form:
+ /// X:\, where X is a drive letter from A-Z
+ /// </summary>
+ /// <param name="fileName">The filename to check</param>
+ /// <returns></returns>
+ public static bool IsValidFilePath(string fileName)
+ {
+ if (null == fileName || 0 == fileName.Length) return false;
+
+ char drive = char.ToUpper(fileName[0]);
+ if ('A' > drive || drive > 'Z')
+ return false;
+
+ else if (Path.VolumeSeparatorChar != fileName[1])
+ return false;
+ else if (Path.DirectorySeparatorChar != fileName[2])
+ return false;
+ else
+ return true;
+ }
+
+ #endregion
+
+ /// <summary>The name of the server this collection represents</summary>
+ private string _server;
+
+ #region Constructor
+
+ /// <summary>
+ /// Default constructor - local machine
+ /// </summary>
+ public ShareCollection()
+ {
+ _server = string.Empty;
+ EnumerateShares(_server, this);
+ }
+
+ /// <summary>
+ /// Constructor
+ /// </summary>
+ /// <param name="Server"></param>
+ public ShareCollection(string server)
+ {
+ _server = server;
+ EnumerateShares(_server, this);
+ }
+
+ #endregion
+
+ #region Add
+
+ protected void Add(Share share)
+ {
+ InnerList.Add(share);
+ }
+
+ protected void Add(string netName, string path, ShareType shareType, string remark)
+ {
+ InnerList.Add(new Share(_server, netName, path, shareType, remark));
+ }
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Returns the name of the server this collection represents
+ /// </summary>
+ public string Server
+ {
+ get { return _server; }
+ }
+
+ /// <summary>
+ /// Returns the <see cref="Share"/> at the specified index.
+ /// </summary>
+ public Share this[int index]
+ {
+ get { return (Share)InnerList[index]; }
+ }
+
+ /// <summary>
+ /// Returns the <see cref="Share"/> which matches a given local path
+ /// </summary>
+ /// <param name="path">The path to match</param>
+ public Share this[string path]
+ {
+ get
+ {
+ if (null == path || 0 == path.Length) return null;
+
+ path = Path.GetFullPath(path);
+ if (!IsValidFilePath(path)) return null;
+
+ Share match = null;
+
+ for (int i = 0; i < InnerList.Count; i++)
+ {
+ Share s = (Share)InnerList[i];
+
+ if (s.IsFileSystem && s.MatchesPath(path))
+ {
+ //Store first match
+ if (null == match)
+ match = s;
+
+ // If this has a longer path,
+ // and this is a disk share or match is a special share,
+ // then this is a better match
+ else if (match.Path.Length < s.Path.Length)
+ {
+ if (ShareType.Disk == s.ShareType || ShareType.Disk != match.ShareType)
+ match = s;
+ }
+ }
+ }
+
+ return match;
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Copy this collection to an array
+ /// </summary>
+ /// <param name="array"></param>
+ /// <param name="index"></param>
+ public void CopyTo(Share[] array, int index)
+ {
+ InnerList.CopyTo(array, index);
+ }
+ }
+} \ No newline at end of file