#if !BESTHTTP_DISABLE_COOKIES && (!UNITY_WEBGL || UNITY_EDITOR) using System; using System.Collections.Generic; using System.Linq; using System.Text; #if NETFX_CORE using FileStream = BestHTTP.PlatformSupport.IO.FileStream; using Directory = BestHTTP.PlatformSupport.IO.Directory; using File = BestHTTP.PlatformSupport.IO.File; using BestHTTP.PlatformSupport.IO; #else using FileStream = System.IO.FileStream; using Directory = System.IO.Directory; using System.IO; #endif namespace BestHTTP.Cookies { /// /// The Cookie Jar implementation based on RFC 6265(http://tools.ietf.org/html/rfc6265). /// public static class CookieJar { // Version of the cookie store. It may be used in a future version for maintaining compatibility. private const int Version = 1; /// /// Returns true if File apis are supported. /// public static bool IsSavingSupported { get { if (IsSupportCheckDone) return _isSavingSupported; try { File.Exists(HTTPManager.GetRootCacheFolder()); _isSavingSupported = true; } catch { _isSavingSupported = false; HTTPManager.Logger.Warning("CookieJar", "Cookie saving and loading disabled!"); } finally { IsSupportCheckDone = true; } return _isSavingSupported; } } /// /// The plugin will delete cookies that are accessed this threshold ago. Its default value is 7 days. /// public static TimeSpan AccessThreshold = TimeSpan.FromDays(7); #region Privates /// /// List of the Cookies /// private static List Cookies = new List(); private static string CookieFolder { get; set; } private static string LibraryPath { get; set; } /// /// Synchronization object for thread safety. /// private static object Locker = new object(); private static bool _isSavingSupported; private static bool IsSupportCheckDone; private static bool Loaded; #endregion #region Internal Functions internal static void SetupFolder() { if (!CookieJar.IsSavingSupported) return; try { if (string.IsNullOrEmpty(CookieFolder) || string.IsNullOrEmpty(LibraryPath)) { CookieFolder = System.IO.Path.Combine(HTTPManager.GetRootCacheFolder(), "Cookies"); LibraryPath = System.IO.Path.Combine(CookieFolder, "Library"); } } catch { } } /// /// Will set or update all cookies from the response object. /// internal static void Set(HTTPResponse response) { if (response == null) return; lock(Locker) { try { Maintain(); List newCookies = new List(); var setCookieHeaders = response.GetHeaderValues("set-cookie"); // No cookies. :'( if (setCookieHeaders == null) return; foreach (var cookieHeader in setCookieHeaders) { try { Cookie cookie = Cookie.Parse(cookieHeader, response.baseRequest.CurrentUri); if (cookie != null) { int idx; var old = Find(cookie, out idx); // if no value for the cookie or already expired then the server asked us to delete the cookie bool expired = string.IsNullOrEmpty(cookie.Value) || !cookie.WillExpireInTheFuture(); if (!expired) { // no old cookie, add it straight to the list if (old == null) { Cookies.Add(cookie); newCookies.Add(cookie); } else { // Update the creation-time of the newly created cookie to match the creation-time of the old-cookie. cookie.Date = old.Date; Cookies[idx] = cookie; newCookies.Add(cookie); } } else if (idx != -1) // delete the cookie Cookies.RemoveAt(idx); } } catch { // Ignore cookie on error } } response.Cookies = newCookies; } catch {} } } /// /// Deletes all expired or 'old' cookies, and will keep the sum size of cookies under the given size. /// internal static void Maintain() { // It's not the same as in the rfc: // http://tools.ietf.org/html/rfc6265#section-5.3 lock (Locker) { try { uint size = 0; for (int i = 0; i < Cookies.Count; ) { var cookie = Cookies[i]; // Remove expired or not used cookies if (!cookie.WillExpireInTheFuture() || (cookie.LastAccess + AccessThreshold) < DateTime.UtcNow) Cookies.RemoveAt(i); else { if (!cookie.IsSession) size += cookie.GuessSize(); i++; } } if (size > HTTPManager.CookieJarSize) { Cookies.Sort(); while (size > HTTPManager.CookieJarSize && Cookies.Count > 0) { var cookie = Cookies[0]; Cookies.RemoveAt(0); size -= cookie.GuessSize(); } } } catch { } } } /// /// Saves the Cookie Jar to a file. /// /// Not implemented under Unity WebPlayer internal static void Persist() { if (!IsSavingSupported) return; lock (Locker) { if (!Loaded) return; try { // Delete any expired cookie Maintain(); if (!Directory.Exists(CookieFolder)) Directory.CreateDirectory(CookieFolder); using (var fs = new FileStream(LibraryPath, FileMode.Create)) using (var bw = new System.IO.BinaryWriter(fs)) { bw.Write(Version); // Count how many non-session cookies we have int count = 0; foreach (var cookie in Cookies) if (!cookie.IsSession) count++; bw.Write(count); // Save only the persistable cookies foreach (var cookie in Cookies) if (!cookie.IsSession) cookie.SaveTo(bw); } } catch { } } } /// /// Load previously persisted cookie library from the file. /// internal static void Load() { if (!IsSavingSupported) return; lock (Locker) { if (Loaded) return; SetupFolder(); try { Cookies.Clear(); if (!Directory.Exists(CookieFolder)) Directory.CreateDirectory(CookieFolder); if (!File.Exists(LibraryPath)) return; using (var fs = new FileStream(LibraryPath, FileMode.Open)) using (var br = new System.IO.BinaryReader(fs)) { /*int version = */br.ReadInt32(); int cookieCount = br.ReadInt32(); for (int i = 0; i < cookieCount; ++i) { Cookie cookie = new Cookie(); cookie.LoadFrom(br); if (cookie.WillExpireInTheFuture()) Cookies.Add(cookie); } } } catch { Cookies.Clear(); } finally { Loaded = true; } } } #endregion #region Public Functions /// /// Returns all Cookies that corresponds to the given Uri. /// public static List Get(Uri uri) { lock (Locker) { Load(); List result = null; for (int i = 0; i < Cookies.Count; ++i) { Cookie cookie = Cookies[i]; if (cookie.WillExpireInTheFuture() && uri.Host.IndexOf(cookie.Domain) != -1 && uri.AbsolutePath.StartsWith(cookie.Path)) { if (result == null) result = new List(); result.Add(cookie); } } return result; } } /// /// Will add a new, or overwrite an old cookie if already exists. /// public static void Set(Uri uri, Cookie cookie) { Set(cookie); } /// /// Will add a new, or overwrite an old cookie if already exists. /// public static void Set(Cookie cookie) { lock (Locker) { Load(); int idx; Find(cookie, out idx); if (idx >= 0) Cookies[idx] = cookie; else Cookies.Add(cookie); } } public static List GetAll() { lock (Locker) { Load(); return Cookies; } } /// /// Deletes all cookies from the Jar. /// public static void Clear() { lock (Locker) { Load(); Cookies.Clear(); } } /// /// Removes cookies that older than the given parameter. /// public static void Clear(TimeSpan olderThan) { lock (Locker) { Load(); for (int i = 0; i < Cookies.Count; ) { var cookie = Cookies[i]; // Remove expired or not used cookies if (!cookie.WillExpireInTheFuture() || (cookie.Date + olderThan) < DateTime.UtcNow) Cookies.RemoveAt(i); else i++; } } } /// /// Removes cookies that matches to the given domain. /// public static void Clear(string domain) { lock (Locker) { Load(); for (int i = 0; i < Cookies.Count; ) { var cookie = Cookies[i]; // Remove expired or not used cookies if (!cookie.WillExpireInTheFuture() || cookie.Domain.IndexOf(domain) != -1) Cookies.RemoveAt(i); else i++; } } } public static void Remove(Uri uri, string name) { lock(Locker) { Load(); for (int i = 0; i < Cookies.Count; ) { var cookie = Cookies[i]; if (cookie.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && uri.Host.IndexOf(cookie.Domain) != -1) Cookies.RemoveAt(i); else i++; } } } #endregion #region Private Helper Functions /// /// Find and return a Cookie and his index in the list. /// private static Cookie Find(Cookie cookie, out int idx) { for (int i = 0; i < Cookies.Count; ++i) { Cookie c = Cookies[i]; if (c.Equals(cookie)) { idx = i; return c; } } idx = -1; return null; } #endregion } } #endif