CookieContainer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. //
  2. // System.Net.CookieContainer
  3. //
  4. // Authors:
  5. // Lawrence Pit ([email protected])
  6. // Gonzalo Paniagua Javier ([email protected])
  7. //
  8. // (c) 2003 Ximian, Inc. (http://www.ximian.com)
  9. // (c) Copyright 2004 Ximian, Inc. (http://www.ximian.com)
  10. //
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. using System;
  32. using System.Collections;
  33. using System.Globalization;
  34. using System.Runtime.Serialization;
  35. using System.Text;
  36. namespace System.Net
  37. {
  38. [Serializable]
  39. [MonoTODO ("Need to remove older/unused cookies if it reaches the maximum capacity")]
  40. public class CookieContainer
  41. {
  42. public const int DefaultCookieLengthLimit = 4096;
  43. public const int DefaultCookieLimit = 300;
  44. public const int DefaultPerDomainCookieLimit = 20;
  45. int count;
  46. int capacity = DefaultCookieLimit;
  47. int perDomainCapacity = DefaultPerDomainCookieLimit;
  48. int maxCookieSize = DefaultCookieLengthLimit;
  49. CookieCollection cookies;
  50. // ctors
  51. public CookieContainer ()
  52. {
  53. }
  54. public CookieContainer (int capacity)
  55. {
  56. if (capacity <= 0)
  57. throw new ArgumentException ("Must be greater than zero", "capacity");
  58. this.capacity = capacity;
  59. }
  60. public CookieContainer (int capacity, int perDomainCapacity, int maxCookieSize)
  61. : this (capacity)
  62. {
  63. if (perDomainCapacity != Int32.MaxValue && (perDomainCapacity <= 0 || perDomainCapacity > capacity))
  64. throw new ArgumentException ("Invalid value", "perDomaniCapacity");
  65. if (maxCookieSize <= 0)
  66. throw new ArgumentException ("Must be greater than zero", "maxCookieSize");
  67. this.perDomainCapacity = perDomainCapacity;
  68. this.maxCookieSize = maxCookieSize;
  69. }
  70. // properties
  71. public int Count {
  72. get { return count; }
  73. }
  74. public int Capacity {
  75. get { return capacity; }
  76. set {
  77. if (value < 0 || (value < perDomainCapacity && perDomainCapacity != Int32.MaxValue))
  78. throw new ArgumentOutOfRangeException ("value");
  79. if (value < maxCookieSize)
  80. maxCookieSize = value;
  81. capacity = value;
  82. }
  83. }
  84. public int MaxCookieSize {
  85. get { return maxCookieSize; }
  86. set {
  87. if (value <= 0)
  88. throw new ArgumentOutOfRangeException ("value");
  89. maxCookieSize = value;
  90. }
  91. }
  92. public int PerDomainCapacity {
  93. get { return perDomainCapacity; }
  94. set {
  95. if (value != Int32.MaxValue && (value <= 0 || value > capacity))
  96. throw new ArgumentOutOfRangeException ("value");
  97. perDomainCapacity = value;
  98. }
  99. }
  100. public void Add (Cookie cookie)
  101. {
  102. if (cookie == null)
  103. throw new ArgumentNullException ("cookie");
  104. if (cookie.Domain == "")
  105. throw new ArgumentException ("Cookie domain not set.", "cookie");
  106. if (cookie.Value.Length > maxCookieSize)
  107. throw new CookieException ("value is larger than MaxCookieSize.");
  108. AddCookie (cookie);
  109. }
  110. void AddCookie (Cookie cookie)
  111. {
  112. if (cookies == null)
  113. cookies = new CookieCollection ();
  114. if (count + 1 > capacity)
  115. throw new CookieException ("Capacity exceeded");
  116. cookies.Add (cookie);
  117. count = cookies.Count;
  118. CheckExpiration ();
  119. }
  120. // Only needs to be called from AddCookie (Cookie) and GetCookies (Uri)
  121. void CheckExpiration ()
  122. {
  123. if (cookies == null)
  124. return;
  125. ArrayList removed = null;
  126. for (int i = cookies.Count - 1; i >= 0; i--) {
  127. Cookie cookie = cookies [i];
  128. if (cookie.Expired) {
  129. if (removed == null)
  130. removed = new ArrayList ();
  131. removed.Add (i);
  132. }
  133. }
  134. if (removed != null) {
  135. // We went backwards above, so this works.
  136. ArrayList list = cookies.List;
  137. foreach (int n in removed) {
  138. list.RemoveAt (n);
  139. }
  140. }
  141. }
  142. public void Add (CookieCollection cookies)
  143. {
  144. if (cookies == null)
  145. throw new ArgumentNullException ("cookies");
  146. foreach (Cookie cookie in cookies)
  147. Add (cookie);
  148. }
  149. void Cook (Uri uri, Cookie cookie)
  150. {
  151. if (cookie.Name == null || cookie.Name == "")
  152. throw new CookieException ("Invalid cookie: name");
  153. if (cookie.Value == null)
  154. throw new CookieException ("Invalid cookie: value");
  155. if (uri != null && cookie.Domain == "")
  156. cookie.Domain = uri.Host;
  157. if (cookie.Path == null || cookie.Path == "") {
  158. if (uri != null) {
  159. cookie.Path = uri.AbsolutePath;
  160. } else {
  161. cookie.Path = "/";
  162. }
  163. }
  164. if (cookie.Port == "" && uri != null && !uri.IsDefaultPort) {
  165. cookie.Port = "\"" + uri.Port.ToString () + "\"";
  166. }
  167. }
  168. public void Add (Uri uri, Cookie cookie)
  169. {
  170. if (uri == null)
  171. throw new ArgumentNullException ("uri");
  172. if (cookie == null)
  173. throw new ArgumentNullException ("cookie");
  174. Cook (uri, cookie);
  175. AddCookie (cookie);
  176. }
  177. public void Add (Uri uri, CookieCollection cookies)
  178. {
  179. if (uri == null)
  180. throw new ArgumentNullException ("uri");
  181. if (cookies == null)
  182. throw new ArgumentNullException ("cookies");
  183. foreach (Cookie c in cookies) {
  184. Cook (uri, c);
  185. AddCookie (c);
  186. }
  187. }
  188. public string GetCookieHeader (Uri uri)
  189. {
  190. if (uri == null)
  191. throw new ArgumentNullException ("uri");
  192. CookieCollection coll = GetCookies (uri);
  193. if (coll.Count == 0)
  194. return "";
  195. StringBuilder result = new StringBuilder ();
  196. foreach (Cookie cookie in cookies) {
  197. result.Append (cookie.ToString ());
  198. result.Append (';');
  199. }
  200. if (result.Length > 0)
  201. result.Length--; // remove trailing semicolon
  202. return result.ToString ();
  203. }
  204. static bool CheckDomain (string domain, string host)
  205. {
  206. if (domain != "" && domain [0] != '.')
  207. return (String.Compare (domain, host, true, CultureInfo.InvariantCulture) == 0);
  208. int dot = host.IndexOf ('.');
  209. if (dot == -1)
  210. return (String.Compare (host, domain, true, CultureInfo.InvariantCulture) == 0);
  211. if (host.Length < domain.Length)
  212. return false;
  213. string subdomain = host.Substring (host.Length - domain.Length);
  214. return (String.Compare (subdomain, domain, true, CultureInfo.InvariantCulture) == 0);
  215. }
  216. public CookieCollection GetCookies (Uri uri)
  217. {
  218. if (uri == null)
  219. throw new ArgumentNullException ("uri");
  220. CheckExpiration ();
  221. CookieCollection coll = new CookieCollection ();
  222. if (cookies == null)
  223. return coll;
  224. foreach (Cookie cookie in cookies) {
  225. string domain = cookie.Domain;
  226. string host = uri.Host;
  227. if (!CheckDomain (domain, host))
  228. continue;
  229. if (cookie.Port != "" && cookie.Ports != null && uri.Port != -1) {
  230. if (Array.IndexOf (cookie.Ports, uri.Port) == -1)
  231. continue;
  232. }
  233. string path = cookie.Path;
  234. string uripath = uri.AbsolutePath;
  235. if (path != "" && path != "/") {
  236. if (uripath != path) {
  237. if (!uripath.StartsWith (path))
  238. continue;
  239. if (path [path.Length - 1] != '/' && uripath.Length > path.Length &&
  240. uripath [path.Length] != '/')
  241. continue;
  242. }
  243. }
  244. if (cookie.Secure && uri.Scheme != "https")
  245. continue;
  246. coll.Add (cookie);
  247. }
  248. return coll;
  249. }
  250. public void SetCookies (Uri uri, string cookieHeader)
  251. {
  252. if (uri == null)
  253. throw new ArgumentNullException ("uri");
  254. if (cookieHeader == null)
  255. throw new ArgumentNullException ("cookieHeader");
  256. ParseAndAddCookies (uri, cookieHeader);
  257. }
  258. // GetCookieValue, GetCookieName and ParseAndAddCookies copied from HttpRequest.cs
  259. static string GetCookieValue (string str, int length, ref int i)
  260. {
  261. if (i >= length)
  262. return null;
  263. int k = i;
  264. while (k < length && Char.IsWhiteSpace (str [k]))
  265. k++;
  266. int begin = k;
  267. while (k < length && str [k] != ';')
  268. k++;
  269. i = k;
  270. return str.Substring (begin, i - begin).Trim ();
  271. }
  272. static string GetCookieName (string str, int length, ref int i)
  273. {
  274. if (i >= length)
  275. return null;
  276. int k = i;
  277. while (k < length && Char.IsWhiteSpace (str [k]))
  278. k++;
  279. int begin = k;
  280. while (k < length && str [k] != ';' && str [k] != '=')
  281. k++;
  282. i = k + 1;
  283. return str.Substring (begin, k - begin).Trim ();
  284. }
  285. static string GetDir (string path)
  286. {
  287. if (path == null || path == "")
  288. return "/";
  289. int last = path.LastIndexOf ('/');
  290. if (last == -1)
  291. return "/" + path;
  292. return path.Substring (0, last + 1);
  293. }
  294. void ParseAndAddCookies (Uri uri, string header)
  295. {
  296. if (header.Length == 0)
  297. return;
  298. string [] name_values = header.Trim ().Split (';');
  299. int length = name_values.Length;
  300. Cookie cookie = null;
  301. int pos;
  302. CultureInfo inv = CultureInfo.InvariantCulture;
  303. bool havePath = false;
  304. bool haveDomain = false;
  305. for (int i = 0; i < length; i++) {
  306. pos = 0;
  307. string name_value = name_values [i].Trim ();
  308. string name = GetCookieName (name_value, name_value.Length, ref pos);
  309. if (name == null || name == "")
  310. throw new CookieException ("Name is empty.");
  311. string value = GetCookieValue (name_value, name_value.Length, ref pos);
  312. if (cookie != null) {
  313. if (!havePath && String.Compare (name, "$Path", true, inv) == 0 ||
  314. String.Compare (name, "path", true, inv) == 0) {
  315. havePath = true;
  316. cookie.Path = value;
  317. continue;
  318. }
  319. if (!haveDomain && String.Compare (name, "$Domain", true, inv) == 0 ||
  320. String.Compare (name, "domain", true, inv) == 0) {
  321. cookie.Domain = value;
  322. haveDomain = true;
  323. continue;
  324. }
  325. if (!havePath)
  326. cookie.Path = GetDir (uri.AbsolutePath);
  327. if (!haveDomain)
  328. cookie.Domain = uri.Host;
  329. havePath = false;
  330. haveDomain = false;
  331. Add (cookie);
  332. cookie = null;
  333. }
  334. cookie = new Cookie (name, value);
  335. }
  336. if (cookie != null) {
  337. if (!havePath)
  338. cookie.Path = GetDir (uri.AbsolutePath);
  339. if (!haveDomain)
  340. cookie.Domain = uri.Host;
  341. Add (cookie);
  342. }
  343. }
  344. } // CookieContainer
  345. } // System.Net