PageRenderTime 64ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/watin/source/src/Core/Native/InternetExplorer/WinInet.cs

https://github.com/easySaCoder/dingwalotsammino
C# | 472 lines | 363 code | 67 blank | 42 comment | 59 complexity | 536619f0477f42345c787cc47c17b444 MD5 | raw file
  1. #region WatiN Copyright (C) 2006-2011 Jeroen van Menen
  2. //Copyright 2006-2011 Jeroen van Menen
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #endregion Copyright
  16. using System;
  17. using System.Net;
  18. using System.Runtime.InteropServices;
  19. using System.Text;
  20. using WatiN.Core.Logging;
  21. using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
  22. namespace WatiN.Core.Native.InternetExplorer
  23. {
  24. /// <summary>
  25. /// Provides low-level support for manipulating cookies, clearing caches and
  26. /// setting internet options.
  27. /// </summary>
  28. /// <remarks author="jeff.brown">
  29. /// This cookie clearing code is based on the sample code from the following MS KB article:
  30. /// http://support.microsoft.com/default.aspx?scid=kb;EN-US;326201
  31. /// Beware, the code presented in that article is somewhat buggy so it has been
  32. /// completely rewritten here.
  33. /// </remarks>
  34. internal class WinInet
  35. {
  36. private delegate void CacheGroupAction(long groupId);
  37. private delegate void CacheEntryAction(INTERNET_CACHE_ENTRY_INFO cacheEntry);
  38. #region Constants
  39. private const int NORMAL_CACHE_ENTRY = 0x1;
  40. private const int EDITED_CACHE_ENTRY = 0x8;
  41. private const int TRACK_OFFLINE_CACHE_ENTRY = 0x10;
  42. private const int TRACK_ONLINE_CACHE_ENTRY = 0x20;
  43. private const int STICKY_CACHE_ENTRY = 0x40;
  44. private const int SPARSE_CACHE_ENTRY = 0x10000;
  45. private const int COOKIE_CACHE_ENTRY = 0x100000;
  46. private const int URLHISTORY_CACHE_ENTRY = 0x200000;
  47. // Indicates that all of the cache groups in the user's system should be enumerated
  48. private const int CACHEGROUP_SEARCH_ALL = 0x0;
  49. // Indicates that all the cache entries that are associated with the cache group
  50. // should be deleted, unless the entry belongs to another cache group.
  51. private const int CACHEGROUP_FLAG_FLUSHURL_ONDELETE = 0x2;
  52. private const int ERROR_FILE_NOT_FOUND = 0x2;
  53. private const int ERROR_NO_MORE_ITEMS = 259;
  54. private const int ERROR_INSUFFICIENT_BUFFER = 122;
  55. public enum InternetCookieState
  56. {
  57. COOKIE_STATE_UNKNOWN = 0x0,
  58. COOKIE_STATE_ACCEPT = 0x1,
  59. COOKIE_STATE_PROMPT = 0x2,
  60. COOKIE_STATE_LEASH = 0x3,
  61. COOKIE_STATE_DOWNGRADE = 0x4,
  62. COOKIE_STATE_REJECT = 0x5
  63. }
  64. #endregion
  65. private WinInet() {}
  66. private static string RetrieveIECookiesForUrl(string url)
  67. {
  68. var cookieHeader = new StringBuilder(new String(' ', 256), 256);
  69. int datasize = cookieHeader.Length;
  70. if (!InternetGetCookie(url, null, cookieHeader, ref datasize))
  71. {
  72. if (datasize < 0)
  73. return String.Empty;
  74. cookieHeader = new StringBuilder(datasize);
  75. InternetGetCookie(url, null, cookieHeader, ref datasize);
  76. }
  77. return cookieHeader.ToString();
  78. }
  79. public static CookieContainer GetCookieContainerForUrl(Uri url)
  80. {
  81. var container = new CookieContainer();
  82. string cookieHeaders = RetrieveIECookiesForUrl(url.AbsoluteUri);
  83. if (cookieHeaders.Length > 0)
  84. {
  85. try { container.SetCookies(url, cookieHeaders); }
  86. catch (CookieException) { }
  87. }
  88. return container;
  89. }
  90. public static CookieCollection GetCookiesForUrl(Uri url)
  91. {
  92. var container = GetCookieContainerForUrl(url);
  93. return container.GetCookies(url);
  94. }
  95. public static string GetCookie(string url, string cookieName)
  96. {
  97. int bufferLength = 0;
  98. IntPtr buffer = IntPtr.Zero;
  99. try
  100. {
  101. for (;;)
  102. {
  103. bool returnValue = InternetGetCookieEx(url, cookieName, buffer, ref bufferLength, 0, IntPtr.Zero);
  104. int err = Marshal.GetLastWin32Error();
  105. if (returnValue && buffer != IntPtr.Zero)
  106. break;
  107. if (err == ERROR_NO_MORE_ITEMS)
  108. return null;
  109. if (err != 0 && err != ERROR_INSUFFICIENT_BUFFER)
  110. ThrowExceptionForLastWin32Error();
  111. buffer = Marshal.ReAllocCoTaskMem(buffer, bufferLength);
  112. }
  113. return Marshal.PtrToStringUni(buffer);
  114. }
  115. finally
  116. {
  117. Marshal.FreeCoTaskMem(buffer);
  118. }
  119. }
  120. public static void SetCookie(string url, string cookieData)
  121. {
  122. InternetCookieState cookieState = (InternetCookieState)
  123. InternetSetCookieEx(url, null, cookieData, 0, IntPtr.Zero);
  124. if (cookieState == InternetCookieState.COOKIE_STATE_UNKNOWN)
  125. ThrowExceptionForLastWin32Error();
  126. if (cookieState != InternetCookieState.COOKIE_STATE_ACCEPT)
  127. throw new InvalidOperationException("The cookie could not be set. Its acceptance state was: " + cookieState);
  128. }
  129. public static void ClearCookies(string url)
  130. {
  131. new ClearCookiesCommand(url).Run();
  132. }
  133. public static void ClearCache()
  134. {
  135. ForEachCacheGroup(new CacheGroupAction(DeleteCacheGroup));
  136. }
  137. private static void DeleteCacheGroup(long groupId)
  138. {
  139. if (!DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero))
  140. {
  141. int err = Marshal.GetLastWin32Error();
  142. if (err != ERROR_FILE_NOT_FOUND)
  143. ThrowExceptionForLastWin32Error();
  144. }
  145. }
  146. private static void ForEachCacheGroup(CacheGroupAction action)
  147. {
  148. // Groups may not always exist on the system.
  149. // For more information, visit the following Microsoft Web site:
  150. // http://msdn.microsoft.com/library/?url=/workshop/networking/wininet/overview/cache.asp
  151. // By default, a URL does not belong to any group. Therefore, that cache may become
  152. // empty even when the CacheGroup APIs are not used because the existing URL does not belong to any group.
  153. long groupId = 0;
  154. IntPtr enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
  155. int err = Marshal.GetLastWin32Error();
  156. // If there are no items in the Cache, you are finished.
  157. if (enumHandle == IntPtr.Zero)
  158. {
  159. if (err != ERROR_NO_MORE_ITEMS && err != ERROR_FILE_NOT_FOUND)
  160. ThrowExceptionForLastWin32Error();
  161. }
  162. else
  163. {
  164. // Loop through Cache Group.
  165. for (;;)
  166. {
  167. action(groupId);
  168. // Get the next one.
  169. bool returnValue = FindNextUrlCacheGroup(enumHandle, ref groupId, IntPtr.Zero);
  170. err = Marshal.GetLastWin32Error();
  171. if (!returnValue)
  172. {
  173. if (err != ERROR_NO_MORE_ITEMS && err != ERROR_FILE_NOT_FOUND)
  174. ThrowExceptionForLastWin32Error();
  175. break;
  176. }
  177. }
  178. }
  179. // Process group 0.
  180. action(0);
  181. }
  182. private static void ForEachCacheEntry(long groupId, string searchPattern, int flags, CacheEntryAction action)
  183. {
  184. int cacheEntryInfoBufferSize = 0;
  185. IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
  186. IntPtr enumHandle = IntPtr.Zero;
  187. try
  188. {
  189. for (;;)
  190. {
  191. enumHandle = FindFirstUrlCacheEntryEx(searchPattern, 0, flags,
  192. groupId, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
  193. int err = Marshal.GetLastWin32Error();
  194. if (enumHandle != IntPtr.Zero)
  195. break;
  196. if (err == ERROR_NO_MORE_ITEMS || err == ERROR_FILE_NOT_FOUND)
  197. return;
  198. if (err != ERROR_INSUFFICIENT_BUFFER)
  199. ThrowExceptionForLastWin32Error();
  200. cacheEntryInfoBuffer = Marshal.ReAllocCoTaskMem(cacheEntryInfoBuffer, cacheEntryInfoBufferSize);
  201. }
  202. for (;;)
  203. {
  204. INTERNET_CACHE_ENTRY_INFO entry = (INTERNET_CACHE_ENTRY_INFO)
  205. Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof (INTERNET_CACHE_ENTRY_INFO));
  206. action(entry);
  207. // Get next entry.
  208. bool returnValue = FindNextUrlCacheEntryEx(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSize,
  209. IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
  210. int err = Marshal.GetLastWin32Error();
  211. if (!returnValue)
  212. {
  213. if (err == ERROR_NO_MORE_ITEMS || err == ERROR_FILE_NOT_FOUND)
  214. return;
  215. if (err != ERROR_INSUFFICIENT_BUFFER)
  216. ThrowExceptionForLastWin32Error();
  217. cacheEntryInfoBuffer = Marshal.ReAllocCoTaskMem(cacheEntryInfoBuffer, cacheEntryInfoBufferSize);
  218. }
  219. }
  220. }
  221. finally
  222. {
  223. Marshal.FreeCoTaskMem(cacheEntryInfoBuffer);
  224. if (enumHandle != IntPtr.Zero)
  225. FindCloseUrlCache(enumHandle);
  226. }
  227. }
  228. private static void ThrowExceptionForLastWin32Error()
  229. {
  230. Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
  231. }
  232. #region Structures
  233. [StructLayout(LayoutKind.Sequential)]
  234. private struct INTERNET_CACHE_ENTRY_INFO
  235. {
  236. public UInt32 dwStructSize;
  237. public IntPtr lpszSourceUrlName;
  238. public IntPtr lpszLocalFileName;
  239. public UInt32 CacheEntryType;
  240. public UInt32 dwUseCount;
  241. public UInt32 dwHitRate;
  242. public UInt32 dwSizeLow;
  243. public UInt32 dwSizeHigh;
  244. public FILETIME LastModifiedTime;
  245. public FILETIME ExpireTime;
  246. public FILETIME LastAccessTime;
  247. public FILETIME LastSyncTime;
  248. public IntPtr lpHeaderInfo;
  249. public UInt32 dwHeaderInfoSize;
  250. public IntPtr lpszFileExtension;
  251. public UInt32 dwExemptDelta;
  252. }
  253. #endregion
  254. #region PInvokes
  255. [DllImport(@"wininet",
  256. SetLastError = true,
  257. CharSet = CharSet.Auto,
  258. EntryPoint = "FindFirstUrlCacheGroup",
  259. CallingConvention = CallingConvention.StdCall)]
  260. private static extern IntPtr FindFirstUrlCacheGroup(
  261. int dwFlags,
  262. int dwFilter,
  263. IntPtr lpSearchCondition,
  264. int dwSearchCondition,
  265. ref long lpGroupId,
  266. IntPtr lpReserved);
  267. [DllImport(@"wininet",
  268. SetLastError = true,
  269. CharSet = CharSet.Auto,
  270. EntryPoint = "FindNextUrlCacheGroup",
  271. CallingConvention = CallingConvention.StdCall)]
  272. [return : MarshalAs(UnmanagedType.Bool)]
  273. private static extern bool FindNextUrlCacheGroup(
  274. IntPtr hFind,
  275. ref long lpGroupId,
  276. IntPtr lpReserved);
  277. [DllImport(@"wininet",
  278. SetLastError = true,
  279. CharSet = CharSet.Auto,
  280. EntryPoint = "DeleteUrlCacheGroup",
  281. CallingConvention = CallingConvention.StdCall)]
  282. [return : MarshalAs(UnmanagedType.Bool)]
  283. private static extern bool DeleteUrlCacheGroup(
  284. long GroupId,
  285. int dwFlags,
  286. IntPtr lpReserved);
  287. [DllImport(@"wininet",
  288. SetLastError = true,
  289. CharSet = CharSet.Unicode,
  290. EntryPoint = "FindFirstUrlCacheEntryExW",
  291. CallingConvention = CallingConvention.StdCall)]
  292. private static extern IntPtr FindFirstUrlCacheEntryEx(
  293. [MarshalAs(UnmanagedType.LPTStr)] string lpszUrlSearchPattern,
  294. int flags, int filter, long groupId,
  295. IntPtr lpFirstCacheEntryInfo,
  296. ref int lpdwFirstCacheEntryInfoBufferSize,
  297. IntPtr reserved, IntPtr reserved2, IntPtr reserved3);
  298. [DllImport(@"wininet",
  299. SetLastError = true,
  300. CharSet = CharSet.Unicode,
  301. EntryPoint = "FindNextUrlCacheEntryExW",
  302. CallingConvention = CallingConvention.StdCall)]
  303. [return : MarshalAs(UnmanagedType.Bool)]
  304. private static extern bool FindNextUrlCacheEntryEx(
  305. IntPtr hFind,
  306. IntPtr lpNextCacheEntryInfo,
  307. ref int lpdwNextCacheEntryInfoBufferSize,
  308. IntPtr reserved, IntPtr reserved2, IntPtr reserved3);
  309. [DllImport(@"wininet", SetLastError = true, CharSet = CharSet.Auto)]
  310. [return : MarshalAs(UnmanagedType.Bool)]
  311. private static extern bool FindCloseUrlCache(IntPtr handle);
  312. [DllImport(@"wininet", SetLastError = true,
  313. CharSet = CharSet.Unicode,
  314. EntryPoint = "DeleteUrlCacheEntryW",
  315. CallingConvention = CallingConvention.StdCall)]
  316. [return : MarshalAs(UnmanagedType.Bool)]
  317. private static extern bool DeleteUrlCacheEntry(
  318. IntPtr lpszUrlName);
  319. [DllImport("wininet.dll", SetLastError = true,
  320. CharSet = CharSet.Unicode,
  321. EntryPoint = "InternetQueryOptionW")]
  322. [return : MarshalAs(UnmanagedType.Bool)]
  323. private static extern bool InternetQueryOption(IntPtr hInternet, uint dwOption, IntPtr lpBuffer, ref int lpdwBufferLength);
  324. [DllImport("wininet.dll", SetLastError = true,
  325. CharSet = CharSet.Unicode,
  326. EntryPoint = "InternetSetOptionW")]
  327. [return : MarshalAs(UnmanagedType.Bool)]
  328. private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int lpdwBufferLength);
  329. [DllImport("wininet.dll", SetLastError = true,
  330. CharSet = CharSet.Unicode)]
  331. public static extern bool InternetGetCookie (string url, string name, StringBuilder data, ref int dataSize);
  332. [DllImport("wininet.dll", SetLastError = true,
  333. CharSet = CharSet.Unicode,
  334. EntryPoint = "InternetGetCookieExW")]
  335. [return : MarshalAs(UnmanagedType.Bool)]
  336. private static extern bool InternetGetCookieEx(
  337. [MarshalAs(UnmanagedType.LPTStr)] string pchURL,
  338. [MarshalAs(UnmanagedType.LPTStr)] string pchCookieName,
  339. IntPtr pchCookieData,
  340. ref int pcchCookieData,
  341. int dwFlags,
  342. IntPtr lpReserved);
  343. [DllImport("wininet.dll", SetLastError = true,
  344. CharSet = CharSet.Unicode,
  345. EntryPoint = "InternetSetCookieExW")]
  346. [return : MarshalAs(UnmanagedType.I4)]
  347. private static extern int InternetSetCookieEx(
  348. [MarshalAs(UnmanagedType.LPTStr)] string lpszURL,
  349. [MarshalAs(UnmanagedType.LPTStr)] string lpszCookieName,
  350. [MarshalAs(UnmanagedType.LPTStr)] string lpszCookieData,
  351. int dwFlags,
  352. IntPtr dwReserved);
  353. #endregion
  354. /// <summary>
  355. /// Holds state for the duration of the clear cookies operation because we
  356. /// don't have anonymous delegates in .Net 1.1.
  357. /// </summary>
  358. private class ClearCookiesCommand
  359. {
  360. private readonly string[] cacheEntrySuffixes;
  361. public ClearCookiesCommand(string url)
  362. {
  363. // The entry looks like "Cookie:user@my.domain.com/".
  364. // Generate a list of suffixes to delete.
  365. if (url == null)
  366. cacheEntrySuffixes = null;
  367. else
  368. {
  369. string remainder = new Uri(url).Host + "/";
  370. cacheEntrySuffixes = new string[] {"@" + remainder, "." + remainder};
  371. }
  372. }
  373. public void Run()
  374. {
  375. ForEachCacheGroup(new CacheGroupAction(ClearCookiesInCacheGroup));
  376. }
  377. private void ClearCookiesInCacheGroup(long groupId)
  378. {
  379. ForEachCacheEntry(groupId, "cookie:", COOKIE_CACHE_ENTRY | NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY,
  380. new CacheEntryAction(DeleteUrlCacheEntryIfUrlMatches));
  381. }
  382. private void DeleteUrlCacheEntryIfUrlMatches(INTERNET_CACHE_ENTRY_INFO entry)
  383. {
  384. string cacheEntryName = Marshal.PtrToStringUni(entry.lpszSourceUrlName);
  385. if (cacheEntrySuffixes != null)
  386. {
  387. foreach (string suffix in cacheEntrySuffixes)
  388. {
  389. if (cacheEntryName.EndsWith(suffix))
  390. goto Match;
  391. }
  392. return;
  393. }
  394. Match:
  395. Logger.LogAction((LogFunction log) => { log("Deleting '{0}'", cacheEntryName); });
  396. if (!DeleteUrlCacheEntry(entry.lpszSourceUrlName))
  397. {
  398. int err = Marshal.GetLastWin32Error();
  399. if (err != ERROR_FILE_NOT_FOUND)
  400. ThrowExceptionForLastWin32Error();
  401. }
  402. }
  403. }
  404. }
  405. }