NetworkShares.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. using System;
  2. using System.IO;
  3. using System.Collections;
  4. using System.Runtime.InteropServices;
  5. namespace MediaBrowser.Networking.Management
  6. {
  7. /// <summary>
  8. /// Type of share
  9. /// </summary>
  10. [Flags]
  11. public enum ShareType
  12. {
  13. /// <summary>Disk share</summary>
  14. Disk = 0,
  15. /// <summary>Printer share</summary>
  16. Printer = 1,
  17. /// <summary>Device share</summary>
  18. Device = 2,
  19. /// <summary>IPC share</summary>
  20. IPC = 3,
  21. /// <summary>Special share</summary>
  22. Special = -2147483648, // 0x80000000,
  23. }
  24. #region Share
  25. /// <summary>
  26. /// Information about a local share
  27. /// </summary>
  28. public class Share
  29. {
  30. #region Private data
  31. private string _server;
  32. private string _netName;
  33. private string _path;
  34. private ShareType _shareType;
  35. private string _remark;
  36. #endregion
  37. #region Constructor
  38. /// <summary>
  39. /// Constructor
  40. /// </summary>
  41. /// <param name="Server"></param>
  42. /// <param name="shi"></param>
  43. public Share(string server, string netName, string path, ShareType shareType, string remark)
  44. {
  45. if (ShareType.Special == shareType && "IPC$" == netName)
  46. {
  47. shareType |= ShareType.IPC;
  48. }
  49. _server = server;
  50. _netName = netName;
  51. _path = path;
  52. _shareType = shareType;
  53. _remark = remark;
  54. }
  55. #endregion
  56. #region Properties
  57. /// <summary>
  58. /// The name of the computer that this share belongs to
  59. /// </summary>
  60. public string Server
  61. {
  62. get { return _server; }
  63. }
  64. /// <summary>
  65. /// Share name
  66. /// </summary>
  67. public string NetName
  68. {
  69. get { return _netName; }
  70. }
  71. /// <summary>
  72. /// Local path
  73. /// </summary>
  74. public string Path
  75. {
  76. get { return _path; }
  77. }
  78. /// <summary>
  79. /// Share type
  80. /// </summary>
  81. public ShareType ShareType
  82. {
  83. get { return _shareType; }
  84. }
  85. /// <summary>
  86. /// Comment
  87. /// </summary>
  88. public string Remark
  89. {
  90. get { return _remark; }
  91. }
  92. /// <summary>
  93. /// Returns true if this is a file system share
  94. /// </summary>
  95. public bool IsFileSystem
  96. {
  97. get
  98. {
  99. // Shared device
  100. if (0 != (_shareType & ShareType.Device)) return false;
  101. // IPC share
  102. if (0 != (_shareType & ShareType.IPC)) return false;
  103. // Shared printer
  104. if (0 != (_shareType & ShareType.Printer)) return false;
  105. // Standard disk share
  106. if (0 == (_shareType & ShareType.Special)) return true;
  107. // Special disk share (e.g. C$)
  108. return ShareType.Special == _shareType && !string.IsNullOrEmpty(_netName);
  109. }
  110. }
  111. /// <summary>
  112. /// Get the root of a disk-based share
  113. /// </summary>
  114. public DirectoryInfo Root
  115. {
  116. get
  117. {
  118. if (IsFileSystem)
  119. {
  120. if (string.IsNullOrEmpty(_server))
  121. if (string.IsNullOrEmpty(_path))
  122. return new DirectoryInfo(ToString());
  123. else
  124. return new DirectoryInfo(_path);
  125. return new DirectoryInfo(ToString());
  126. }
  127. return null;
  128. }
  129. }
  130. #endregion
  131. /// <summary>
  132. /// Returns the path to this share
  133. /// </summary>
  134. /// <returns></returns>
  135. public override string ToString()
  136. {
  137. if (string.IsNullOrEmpty(_server))
  138. {
  139. return string.Format(@"\\{0}\{1}", Environment.MachineName, _netName);
  140. }
  141. return string.Format(@"\\{0}\{1}", _server, _netName);
  142. }
  143. /// <summary>
  144. /// Returns true if this share matches the local path
  145. /// </summary>
  146. /// <param name="path"></param>
  147. /// <returns></returns>
  148. public bool MatchesPath(string path)
  149. {
  150. if (!IsFileSystem) return false;
  151. if (string.IsNullOrEmpty(path)) return true;
  152. return path.ToLower().StartsWith(_path.ToLower());
  153. }
  154. }
  155. #endregion
  156. /// <summary>
  157. /// A collection of shares
  158. /// </summary>
  159. public class ShareCollection : ReadOnlyCollectionBase
  160. {
  161. #region Platform
  162. /// <summary>
  163. /// Is this an NT platform?
  164. /// </summary>
  165. protected static bool IsNT
  166. {
  167. get { return (PlatformID.Win32NT == Environment.OSVersion.Platform); }
  168. }
  169. /// <summary>
  170. /// Returns true if this is Windows 2000 or higher
  171. /// </summary>
  172. protected static bool IsW2KUp
  173. {
  174. get
  175. {
  176. OperatingSystem os = Environment.OSVersion;
  177. if (PlatformID.Win32NT == os.Platform && os.Version.Major >= 5)
  178. return true;
  179. else
  180. return false;
  181. }
  182. }
  183. #endregion
  184. #region Interop
  185. #region Constants
  186. /// <summary>Maximum path length</summary>
  187. protected const int MAX_PATH = 260;
  188. /// <summary>No error</summary>
  189. protected const int NO_ERROR = 0;
  190. /// <summary>Access denied</summary>
  191. protected const int ERROR_ACCESS_DENIED = 5;
  192. /// <summary>Access denied</summary>
  193. protected const int ERROR_WRONG_LEVEL = 124;
  194. /// <summary>More data available</summary>
  195. protected const int ERROR_MORE_DATA = 234;
  196. /// <summary>Not connected</summary>
  197. protected const int ERROR_NOT_CONNECTED = 2250;
  198. /// <summary>Level 1</summary>
  199. protected const int UNIVERSAL_NAME_INFO_LEVEL = 1;
  200. /// <summary>Max extries (9x)</summary>
  201. protected const int MAX_SI50_ENTRIES = 20;
  202. #endregion
  203. #region Structures
  204. /// <summary>Unc name</summary>
  205. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
  206. protected struct UNIVERSAL_NAME_INFO
  207. {
  208. [MarshalAs(UnmanagedType.LPTStr)]
  209. public string lpUniversalName;
  210. }
  211. /// <summary>Share information, NT, level 2</summary>
  212. /// <remarks>
  213. /// Requires admin rights to work.
  214. /// </remarks>
  215. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  216. protected struct SHARE_INFO_2
  217. {
  218. [MarshalAs(UnmanagedType.LPWStr)]
  219. public string NetName;
  220. public ShareType ShareType;
  221. [MarshalAs(UnmanagedType.LPWStr)]
  222. public string Remark;
  223. public int Permissions;
  224. public int MaxUsers;
  225. public int CurrentUsers;
  226. [MarshalAs(UnmanagedType.LPWStr)]
  227. public string Path;
  228. [MarshalAs(UnmanagedType.LPWStr)]
  229. public string Password;
  230. }
  231. /// <summary>Share information, NT, level 1</summary>
  232. /// <remarks>
  233. /// Fallback when no admin rights.
  234. /// </remarks>
  235. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  236. protected struct SHARE_INFO_1
  237. {
  238. [MarshalAs(UnmanagedType.LPWStr)]
  239. public string NetName;
  240. public ShareType ShareType;
  241. [MarshalAs(UnmanagedType.LPWStr)]
  242. public string Remark;
  243. }
  244. /// <summary>Share information, Win9x</summary>
  245. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
  246. protected struct SHARE_INFO_50
  247. {
  248. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
  249. public string NetName;
  250. public byte bShareType;
  251. public ushort Flags;
  252. [MarshalAs(UnmanagedType.LPTStr)]
  253. public string Remark;
  254. [MarshalAs(UnmanagedType.LPTStr)]
  255. public string Path;
  256. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
  257. public string PasswordRW;
  258. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
  259. public string PasswordRO;
  260. public ShareType ShareType
  261. {
  262. get { return (ShareType)((int)bShareType & 0x7F); }
  263. }
  264. }
  265. /// <summary>Share information level 1, Win9x</summary>
  266. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
  267. protected struct SHARE_INFO_1_9x
  268. {
  269. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
  270. public string NetName;
  271. public byte Padding;
  272. public ushort bShareType;
  273. [MarshalAs(UnmanagedType.LPTStr)]
  274. public string Remark;
  275. public ShareType ShareType
  276. {
  277. get { return (ShareType)((int)bShareType & 0x7FFF); }
  278. }
  279. }
  280. #endregion
  281. #region Functions
  282. /// <summary>Get a UNC name</summary>
  283. [DllImport("mpr", CharSet = CharSet.Auto)]
  284. protected static extern int WNetGetUniversalName(string lpLocalPath,
  285. int dwInfoLevel, ref UNIVERSAL_NAME_INFO lpBuffer, ref int lpBufferSize);
  286. /// <summary>Get a UNC name</summary>
  287. [DllImport("mpr", CharSet = CharSet.Auto)]
  288. protected static extern int WNetGetUniversalName(string lpLocalPath,
  289. int dwInfoLevel, IntPtr lpBuffer, ref int lpBufferSize);
  290. /// <summary>Enumerate shares (NT)</summary>
  291. [DllImport("netapi32", CharSet = CharSet.Unicode)]
  292. protected static extern int NetShareEnum(string lpServerName, int dwLevel,
  293. out IntPtr lpBuffer, int dwPrefMaxLen, out int entriesRead,
  294. out int totalEntries, ref int hResume);
  295. /// <summary>Enumerate shares (9x)</summary>
  296. [DllImport("svrapi", CharSet = CharSet.Ansi)]
  297. protected static extern int NetShareEnum(
  298. [MarshalAs(UnmanagedType.LPTStr)] string lpServerName, int dwLevel,
  299. IntPtr lpBuffer, ushort cbBuffer, out ushort entriesRead,
  300. out ushort totalEntries);
  301. /// <summary>Free the buffer (NT)</summary>
  302. [DllImport("netapi32")]
  303. protected static extern int NetApiBufferFree(IntPtr lpBuffer);
  304. #endregion
  305. #region Enumerate shares
  306. /// <summary>
  307. /// Enumerates the shares on Windows NT
  308. /// </summary>
  309. /// <param name="server">The server name</param>
  310. /// <param name="shares">The ShareCollection</param>
  311. protected static void EnumerateSharesNT(string server, ShareCollection shares)
  312. {
  313. int level = 2;
  314. int entriesRead, totalEntries, nRet, hResume = 0;
  315. IntPtr pBuffer = IntPtr.Zero;
  316. try
  317. {
  318. nRet = NetShareEnum(server, level, out pBuffer, -1,
  319. out entriesRead, out totalEntries, ref hResume);
  320. if (ERROR_ACCESS_DENIED == nRet)
  321. {
  322. //Need admin for level 2, drop to level 1
  323. level = 1;
  324. nRet = NetShareEnum(server, level, out pBuffer, -1,
  325. out entriesRead, out totalEntries, ref hResume);
  326. }
  327. if (NO_ERROR == nRet && entriesRead > 0)
  328. {
  329. Type t = (2 == level) ? typeof(SHARE_INFO_2) : typeof(SHARE_INFO_1);
  330. int offset = Marshal.SizeOf(t);
  331. for (int i = 0, lpItem = pBuffer.ToInt32(); i < entriesRead; i++, lpItem += offset)
  332. {
  333. IntPtr pItem = new IntPtr(lpItem);
  334. if (1 == level)
  335. {
  336. SHARE_INFO_1 si = (SHARE_INFO_1)Marshal.PtrToStructure(pItem, t);
  337. shares.Add(si.NetName, string.Empty, si.ShareType, si.Remark);
  338. }
  339. else
  340. {
  341. SHARE_INFO_2 si = (SHARE_INFO_2)Marshal.PtrToStructure(pItem, t);
  342. shares.Add(si.NetName, si.Path, si.ShareType, si.Remark);
  343. }
  344. }
  345. }
  346. }
  347. finally
  348. {
  349. // Clean up buffer allocated by system
  350. if (IntPtr.Zero != pBuffer)
  351. NetApiBufferFree(pBuffer);
  352. }
  353. }
  354. /// <summary>
  355. /// Enumerates the shares on Windows 9x
  356. /// </summary>
  357. /// <param name="server">The server name</param>
  358. /// <param name="shares">The ShareCollection</param>
  359. protected static void EnumerateShares9x(string server, ShareCollection shares)
  360. {
  361. int level = 50;
  362. int nRet = 0;
  363. ushort entriesRead, totalEntries;
  364. Type t = typeof(SHARE_INFO_50);
  365. int size = Marshal.SizeOf(t);
  366. ushort cbBuffer = (ushort)(MAX_SI50_ENTRIES * size);
  367. //On Win9x, must allocate buffer before calling API
  368. IntPtr pBuffer = Marshal.AllocHGlobal(cbBuffer);
  369. try
  370. {
  371. nRet = NetShareEnum(server, level, pBuffer, cbBuffer,
  372. out entriesRead, out totalEntries);
  373. if (ERROR_WRONG_LEVEL == nRet)
  374. {
  375. level = 1;
  376. t = typeof(SHARE_INFO_1_9x);
  377. size = Marshal.SizeOf(t);
  378. nRet = NetShareEnum(server, level, pBuffer, cbBuffer,
  379. out entriesRead, out totalEntries);
  380. }
  381. if (NO_ERROR == nRet || ERROR_MORE_DATA == nRet)
  382. {
  383. for (int i = 0, lpItem = pBuffer.ToInt32(); i < entriesRead; i++, lpItem += size)
  384. {
  385. IntPtr pItem = new IntPtr(lpItem);
  386. if (1 == level)
  387. {
  388. SHARE_INFO_1_9x si = (SHARE_INFO_1_9x)Marshal.PtrToStructure(pItem, t);
  389. shares.Add(si.NetName, string.Empty, si.ShareType, si.Remark);
  390. }
  391. else
  392. {
  393. SHARE_INFO_50 si = (SHARE_INFO_50)Marshal.PtrToStructure(pItem, t);
  394. shares.Add(si.NetName, si.Path, si.ShareType, si.Remark);
  395. }
  396. }
  397. }
  398. else
  399. Console.WriteLine(nRet);
  400. }
  401. finally
  402. {
  403. //Clean up buffer
  404. Marshal.FreeHGlobal(pBuffer);
  405. }
  406. }
  407. /// <summary>
  408. /// Enumerates the shares
  409. /// </summary>
  410. /// <param name="server">The server name</param>
  411. /// <param name="shares">The ShareCollection</param>
  412. protected static void EnumerateShares(string server, ShareCollection shares)
  413. {
  414. if (null != server && 0 != server.Length && !IsW2KUp)
  415. {
  416. server = server.ToUpper();
  417. // On NT4, 9x and Me, server has to start with "\\"
  418. if (!('\\' == server[0] && '\\' == server[1]))
  419. server = @"\\" + server;
  420. }
  421. if (IsNT)
  422. EnumerateSharesNT(server, shares);
  423. else
  424. EnumerateShares9x(server, shares);
  425. }
  426. #endregion
  427. #endregion
  428. #region Static methods
  429. /// <summary>
  430. /// Returns true if fileName is a valid local file-name of the form:
  431. /// X:\, where X is a drive letter from A-Z
  432. /// </summary>
  433. /// <param name="fileName">The filename to check</param>
  434. /// <returns></returns>
  435. public static bool IsValidFilePath(string fileName)
  436. {
  437. if (null == fileName || 0 == fileName.Length) return false;
  438. char drive = char.ToUpper(fileName[0]);
  439. if ('A' > drive || drive > 'Z')
  440. return false;
  441. else if (Path.VolumeSeparatorChar != fileName[1])
  442. return false;
  443. else if (Path.DirectorySeparatorChar != fileName[2])
  444. return false;
  445. else
  446. return true;
  447. }
  448. #endregion
  449. /// <summary>The name of the server this collection represents</summary>
  450. private string _server;
  451. #region Constructor
  452. /// <summary>
  453. /// Default constructor - local machine
  454. /// </summary>
  455. public ShareCollection()
  456. {
  457. _server = string.Empty;
  458. EnumerateShares(_server, this);
  459. }
  460. /// <summary>
  461. /// Constructor
  462. /// </summary>
  463. /// <param name="Server"></param>
  464. public ShareCollection(string server)
  465. {
  466. _server = server;
  467. EnumerateShares(_server, this);
  468. }
  469. #endregion
  470. #region Add
  471. protected void Add(Share share)
  472. {
  473. InnerList.Add(share);
  474. }
  475. protected void Add(string netName, string path, ShareType shareType, string remark)
  476. {
  477. InnerList.Add(new Share(_server, netName, path, shareType, remark));
  478. }
  479. #endregion
  480. #region Properties
  481. /// <summary>
  482. /// Returns the name of the server this collection represents
  483. /// </summary>
  484. public string Server
  485. {
  486. get { return _server; }
  487. }
  488. /// <summary>
  489. /// Returns the <see cref="Share"/> at the specified index.
  490. /// </summary>
  491. public Share this[int index]
  492. {
  493. get { return (Share)InnerList[index]; }
  494. }
  495. /// <summary>
  496. /// Returns the <see cref="Share"/> which matches a given local path
  497. /// </summary>
  498. /// <param name="path">The path to match</param>
  499. public Share this[string path]
  500. {
  501. get
  502. {
  503. if (null == path || 0 == path.Length) return null;
  504. path = Path.GetFullPath(path);
  505. if (!IsValidFilePath(path)) return null;
  506. Share match = null;
  507. for (int i = 0; i < InnerList.Count; i++)
  508. {
  509. Share s = (Share)InnerList[i];
  510. if (s.IsFileSystem && s.MatchesPath(path))
  511. {
  512. //Store first match
  513. if (null == match)
  514. match = s;
  515. // If this has a longer path,
  516. // and this is a disk share or match is a special share,
  517. // then this is a better match
  518. else if (match.Path.Length < s.Path.Length)
  519. {
  520. if (ShareType.Disk == s.ShareType || ShareType.Disk != match.ShareType)
  521. match = s;
  522. }
  523. }
  524. }
  525. return match;
  526. }
  527. }
  528. #endregion
  529. /// <summary>
  530. /// Copy this collection to an array
  531. /// </summary>
  532. /// <param name="array"></param>
  533. /// <param name="index"></param>
  534. public void CopyTo(Share[] array, int index)
  535. {
  536. InnerList.CopyTo(array, index);
  537. }
  538. }
  539. }