NetworkShares.cs 20 KB

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