Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3948 lines
110 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // COOKIE.CXX
  4. //
  5. // Cookie Jar
  6. //
  7. // This file implements cookies as defined by Navigator 4 behavior and the
  8. // specification at http://www.netscape.com/newsref/std/cookie_spec.html.
  9. // If Navigator 4 and the specification are not in agreement, we try to
  10. // match the Navigator 4 behavior.
  11. //
  12. // The following describes some interesting aspects of cookie behavior.
  13. //
  14. // SYNTAX
  15. //
  16. // Syntax for cookie is
  17. //
  18. // [[name]=] value [; options]
  19. //
  20. // The name is everything before "=" with leading and trailing whitespace
  21. // removed. The value is everything after "=" and before ";" with leading
  22. // and trailing whitespace removed. The name and value can contain spaces,
  23. // quotes or any other character except ";" and "=". The name and equal
  24. // sign are optional.
  25. //
  26. // Example: =foo -> name: <blank> value: foo
  27. // foo -> name: <blank> value: foo
  28. // foo= -> name: foo value: <blank>
  29. // ; -> name: <blank> value: <blank>
  30. //
  31. // ORDER
  32. //
  33. // Cookies with a more specific path are sent before cookies with
  34. // a less specific path mapping. The domain does not contibute
  35. // to the ordering of cookies.
  36. //
  37. // If the path length of two cookies are equal, then the cookies
  38. // are ordered by time of creation. Navigator maintains this
  39. // ordering across domain and path boundaries. IE maintains this
  40. // ordering for a specific domain and path. It is difficult to match
  41. // the Navigator behavior and there are no known bugs because of
  42. // this difference.
  43. //
  44. // MATCHING
  45. //
  46. // Path matches are done at the character level. Any
  47. // directory structure in the path is ignored.
  48. //
  49. // Navigator matches domains at the character level and ignores
  50. // the structure of the domain name.
  51. //
  52. // Previous versions of IE tossed the leading "." on a domain name.
  53. // With out this information, character by character compares are
  54. // can produce incorrect results. For backwards compatibilty with
  55. // old cookie we continue to match on a component by component basis.
  56. //
  57. // Some examples of the difference are:
  58. //
  59. // Cookie domain Document domain Navigator match IE match
  60. // .foo.com foo.com no yes
  61. // bar.x.com foobar.x.com yes no
  62. //
  63. // ACCEPTING COOKIES
  64. //
  65. // A cookie is rejected if the path specified in the set cookie
  66. // header is not a prefix of document's path.
  67. //
  68. // Navigator rejects a cookie if the domain specified in the
  69. // set cookie header does not contain at least two periods
  70. // or the domain is not a suffix of the document's domain.
  71. // The suffix match is done on a character by character basis.
  72. //
  73. // Navigator ignores all the stuff in the specification about
  74. // three period matching and the seven special top level domains.
  75. //
  76. // IE rejects a cookie if the domain specified by the cookie
  77. // header does not contain at least one embedded period or the
  78. // domain is not a suffix of the documents domain.
  79. //
  80. // Cookies are accepted if the path specified in the set cookie
  81. // header is a prefix of the document's path and the domain
  82. // specified in the set cookie header.
  83. //
  84. // The difference in behavior is a result of the matching rules
  85. // described in the previous section.
  86. //
  87. //---------------------------------------------------------------------------
  88. #include <wininetp.h>
  89. #include "httpp.h"
  90. #include "cookiepolicy.h"
  91. #include "cookieprompt.h"
  92. extern DWORD ConfirmCookie(HWND hwnd, HTTP_REQUEST_HANDLE_OBJECT *lpRequest, DWORD dwFlags, LPVOID *lppvData, LPDWORD pdwStopWarning);
  93. #define CCH_COOKIE_MAX (5 * 1024)
  94. CRITICAL_SECTION s_csCookieJar;
  95. static class CCookieJar *s_pJar;
  96. static char s_achEmpty[] = "";
  97. static char s_cCacheModify;
  98. static const char s_achCookieScheme[] = "Cookie:";
  99. static DWORD s_dwCacheVersion;
  100. static BOOL s_fFirstTime = TRUE;
  101. // Registry path for prompt-history
  102. static const char regpathPromptHistory[] = INTERNET_SETTINGS_KEY "\\P3P\\History";
  103. // Prompt history-- persists user decisions about cookies
  104. CCookiePromptHistory cookieUIhistory(regpathPromptHistory);
  105. // Hard-coded list of special domains. If any of these are present between the
  106. // second-to-last and last dot we will require 2 embedded dots.
  107. // The domain strings are reversed to make the compares easier
  108. static const char *s_pachSpecialRestrictedDomains[] =
  109. {"MOC", "UDE", "TEN", "GRO", "VOG", "LIM", "TNI" };
  110. static const char s_chSpecialAllowedDomains[] = "vt."; // domains ending with ".tv" always only need one dot
  111. /* Non-scriptable cookies */
  112. #define COOKIE_NONSCRIPTABLE 0x00002000
  113. const char gcszNoScriptField[] = "httponly";
  114. #if INET_DEBUG
  115. DWORD s_dwThreadID;
  116. #endif
  117. // values returned from cookie UI
  118. #define COOKIE_DONT_ALLOW 1
  119. #define COOKIE_ALLOW 2
  120. #define COOKIE_ALLOW_ALL 4
  121. #define COOKIE_DONT_ALLOW_ALL 8
  122. // Function declaration
  123. BOOL EvaluateCookiePolicy(const char *pszURL, BOOL f3rdParty, BOOL fRestricted,
  124. P3PCookieState *pState,
  125. const char *pszHostName=NULL);
  126. DWORD getImpliedCookieFlags(P3PCookieState *pState);
  127. DWORD GetCookieMainSwitch(DWORD dwZone);
  128. DWORD GetCookieMainSwitch(LPCSTR pszURL);
  129. #define IsLegacyCookie(pc) ((pc->_dwFlags & INTERNET_COOKIE_IE6) == 0)
  130. //---------------------------------------------------------------------------
  131. //
  132. // CACHE_ENTRY_INFO_BUFFER
  133. //
  134. //---------------------------------------------------------------------------
  135. class CACHE_ENTRY_INFO_BUFFER : public INTERNET_CACHE_ENTRY_INFO
  136. {
  137. BYTE _ab[5 * 1024];
  138. };
  139. //---------------------------------------------------------------------------
  140. //
  141. // CCookieCriticalSection
  142. //
  143. // Enter / Exit critical section.
  144. //
  145. //---------------------------------------------------------------------------
  146. class CCookieCriticalSection
  147. {
  148. private:
  149. int Dummy; // Variable needed to force compiler to generate code for const/dest.
  150. public:
  151. CCookieCriticalSection()
  152. {
  153. EnterCriticalSection(&s_csCookieJar);
  154. #if INET_DEBUG
  155. s_dwThreadID = GetCurrentThreadId();
  156. #endif
  157. }
  158. ~CCookieCriticalSection()
  159. {
  160. #if INET_DEBUG
  161. s_dwThreadID = 0;
  162. #endif
  163. LeaveCriticalSection(&s_csCookieJar);
  164. }
  165. };
  166. #define ASSERT_CRITSEC() INET_ASSERT(GetCurrentThreadId() == s_dwThreadID)
  167. //---------------------------------------------------------------------------
  168. //
  169. // CCookieBase
  170. //
  171. // Provides operator new which allocates extra memory
  172. // after object and initializes the memory to zero.
  173. //
  174. //---------------------------------------------------------------------------
  175. class CCookieBase
  176. {
  177. public:
  178. void * operator new(size_t cb, size_t cbExtra);
  179. void operator delete(void *pv);
  180. };
  181. //---------------------------------------------------------------------------
  182. //
  183. // CCookie
  184. //
  185. // Holds a single cookie value.
  186. //
  187. //---------------------------------------------------------------------------
  188. class CCookie : public CCookieBase
  189. {
  190. public:
  191. ~CCookie();
  192. static CCookie *Construct(const char *pchName);
  193. BOOL SetValue(const char *pchValue);
  194. BOOL WriteCacheFile(HANDLE hFile, char *pchRDomain, char *pchPath);
  195. BOOL CanSend(FILETIME *pftCurrent, BOOL fSecure);
  196. BOOL IsPersistent() { return (_dwFlags & COOKIE_SESSION) == 0; }
  197. BOOL IsRestricted() { return (_dwFlags & COOKIE_RESTRICT)!= 0; }
  198. BOOL IsLegacy() { return (_dwFlags & INTERNET_COOKIE_IS_LEGACY) != 0; }
  199. BOOL PurgePersistent(void *);
  200. BOOL PurgeSession(void *);
  201. BOOL PurgeAll(void *);
  202. BOOL PurgeByName(void *);
  203. BOOL PurgeThis(void *);
  204. BOOL PurgeExpired(void *);
  205. FILETIME _ftExpire;
  206. FILETIME _ftLastModified;
  207. DWORD _dwFlags;
  208. CCookie * _pCookieNext;
  209. char * _pchName;
  210. char * _pchValue;
  211. DWORD _dwPromptMask; // never persisted, only used in session
  212. };
  213. //---------------------------------------------------------------------------
  214. //
  215. // CCookieLocation
  216. //
  217. // Holds all cookies for a given domain and path.
  218. //
  219. //---------------------------------------------------------------------------
  220. class CCookieLocation : public CCookieBase
  221. {
  222. public:
  223. ~CCookieLocation();
  224. static CCookieLocation *Construct(const char *pchRDomain, const char *pchPath);
  225. CCookie * GetCookie(const char *pchName, BOOL fCreate);
  226. BOOL WriteCacheFile();
  227. BOOL ReadCacheFile();
  228. BOOL ReadCacheFileIfNeeded();
  229. BOOL Purge(BOOL (CCookie::*)(void *), void *);
  230. BOOL Purge(FILETIME *pftCurrent, BOOL fSession);
  231. BOOL IsMatch(char *pchRDomain, char *pchPath);
  232. char * GetCacheURL();
  233. FILETIME _ftCacheFileLastModified;
  234. CCookie * _pCookieKids;
  235. CCookieLocation*_pLocationNext;
  236. char * _pchRDomain;
  237. char * _pchPath;
  238. int _cchPath;
  239. BYTE _fCacheFileExists;
  240. BYTE _fReadFromCacheFileNeeded;
  241. };
  242. //---------------------------------------------------------------------------
  243. //
  244. // CCookieJar
  245. //
  246. // Maintains fixed size hash table of cookie location objects.
  247. //
  248. //---------------------------------------------------------------------------
  249. enum COOKIE_RESULT
  250. {
  251. COOKIE_FAIL = 0,
  252. COOKIE_SUCCESS = 1,
  253. COOKIE_DISALLOW = 2,
  254. COOKIE_PENDING = 3
  255. };
  256. class CCookieJar : public CCookieBase
  257. {
  258. public:
  259. static CCookieJar * Construct();
  260. ~CCookieJar();
  261. struct CookieInfo {
  262. const char *pchURL;
  263. char *pchRDomain;
  264. char *pchPath;
  265. char *pchName;
  266. char *pchValue;
  267. DWORD dwFlags;
  268. FILETIME ftExpire;
  269. P3PCookieState *pP3PState;
  270. };
  271. DWORD
  272. CheckCookiePolicy(
  273. HTTP_REQUEST_HANDLE_OBJECT *pRequest,
  274. CookieInfo *pInfo,
  275. DWORD dwOperation
  276. );
  277. void EnforceCookieLimits(CCookieLocation *pLocation, char *pchName, BOOL *pfWriteCacheFileNeeded);
  278. DWORD SetCookie(HTTP_REQUEST_HANDLE_OBJECT *pRequest, const char *pchURL, char *pchHeader,
  279. DWORD &dwFlags, P3PCookieState *pState, LPDWORD pdwAction);
  280. void Purge(FILETIME *pftCurrent, BOOL fSession);
  281. BOOL SyncWithCache();
  282. BOOL SyncWithCacheIfNeeded();
  283. void CacheFilesModified();
  284. CCookieLocation** GetBucket(const char *pchRDomain);
  285. CCookieLocation * GetLocation(const char *pchRDomain, const char *pchPath, BOOL fCreate);
  286. CCookieLocation * _apLocation[128];
  287. };
  288. //---------------------------------------------------------------------------
  289. //
  290. // Track cache modificaitons.
  291. //
  292. //---------------------------------------------------------------------------
  293. inline void
  294. MarkCacheModified()
  295. {
  296. IncrementUrlCacheHeaderData(CACHE_HEADER_DATA_COOKIE_CHANGE_COUNT, &s_dwCacheVersion);
  297. }
  298. inline BOOL
  299. IsCacheModified()
  300. {
  301. DWORD dwCacheVersion;
  302. if (s_fFirstTime)
  303. {
  304. s_fFirstTime = FALSE;
  305. GetUrlCacheHeaderData(CACHE_HEADER_DATA_COOKIE_CHANGE_COUNT, &s_dwCacheVersion);
  306. return TRUE;
  307. }
  308. else
  309. {
  310. dwCacheVersion = s_dwCacheVersion;
  311. GetUrlCacheHeaderData(CACHE_HEADER_DATA_COOKIE_CHANGE_COUNT, &s_dwCacheVersion);
  312. return dwCacheVersion != s_dwCacheVersion;
  313. }
  314. }
  315. //---------------------------------------------------------------------------
  316. //
  317. // String utilities
  318. //
  319. //---------------------------------------------------------------------------
  320. static BOOL
  321. IsZero(FILETIME *pft)
  322. {
  323. return pft->dwLowDateTime == 0 && pft->dwHighDateTime == 0;
  324. }
  325. static char *
  326. StrnDup(const char *pch, int cch)
  327. {
  328. char *pchAlloc = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cch + 1);
  329. if (!pchAlloc)
  330. {
  331. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  332. return NULL;
  333. }
  334. memcpy(pchAlloc, pch, cch);
  335. pchAlloc[cch] = 0;
  336. return pchAlloc;
  337. }
  338. static BOOL
  339. IsPathMatch(const char *pchPrefix, const char *pchStr)
  340. {
  341. while (*pchPrefix == *pchStr && *pchStr)
  342. {
  343. pchPrefix += 1;
  344. pchStr += 1;
  345. }
  346. return *pchPrefix == 0;
  347. }
  348. static BOOL
  349. IsDomainMatch(const char *pchPrefix, const char *pchStr)
  350. {
  351. while (*pchPrefix == *pchStr && *pchStr)
  352. {
  353. pchPrefix += 1;
  354. pchStr += 1;
  355. }
  356. return *pchPrefix == 0 && (*pchStr == 0 || *pchStr == '.');
  357. }
  358. static BOOL
  359. IsPathLegal(const char *pchHeader, const char *pchDocument)
  360. {
  361. return TRUE;
  362. /*
  363. We attempted to implement the specification here.
  364. It looks like Navigator does not reject cookies
  365. based on the path attribute. We now consider
  366. all path attributes to be legal.
  367. while (*pchHeader == *pchDocument && *pchDocument)
  368. {
  369. pchHeader += 1;
  370. pchDocument += 1;
  371. }
  372. if (*pchDocument == 0)
  373. {
  374. while (*pchHeader && *pchHeader != '/' && *pchHeader != '\\')
  375. {
  376. pchHeader += 1;
  377. }
  378. }
  379. return *pchHeader == 0;
  380. */
  381. }
  382. //
  383. // DarrenMi: No longer need IsVerySpecialDomain
  384. extern PTSTR GlobalSpecialDomains;
  385. extern PTSTR *GlobalSDOffsets;
  386. static BOOL
  387. IsVerySpecialDomain(const char *pch, int nTopLevel, int nSecond)
  388. {
  389. if (!GlobalSpecialDomains)
  390. {
  391. HKEY hk;
  392. if (ERROR_SUCCESS==RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  393. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\5.0"),
  394. 0,
  395. KEY_READ,
  396. &hk))
  397. {
  398. DWORD dwType, dwSize;
  399. if ((ERROR_SUCCESS==RegQueryValueEx(hk, "SpecialDomains", NULL, &dwType, NULL, &dwSize))
  400. && (REG_SZ==dwType))
  401. {
  402. GlobalSpecialDomains = new TCHAR[dwSize];
  403. if (GlobalSpecialDomains
  404. && (ERROR_SUCCESS==RegQueryValueEx(hk, "SpecialDomains", NULL, &dwType, (LPBYTE)GlobalSpecialDomains, &dwSize)))
  405. {
  406. // We're going to scan a string stored in the registry to gather the domains we should
  407. // allow. Format:
  408. // [domain] [domain] [domain]
  409. // The delimiter is a space character.
  410. PTSTR psz = GlobalSpecialDomains;
  411. DWORD dwDomains = 0;
  412. BOOL fWord = FALSE;
  413. while (*psz)
  414. {
  415. if (*psz==TEXT(' '))
  416. {
  417. if (fWord)
  418. {
  419. fWord = FALSE;
  420. dwDomains++;
  421. }
  422. }
  423. else
  424. {
  425. fWord = TRUE;
  426. }
  427. psz++;
  428. }
  429. if (fWord)
  430. {
  431. dwDomains++;
  432. }
  433. GlobalSDOffsets = (PTSTR*)new PTSTR[dwDomains+1];
  434. if (GlobalSDOffsets)
  435. {
  436. psz = GlobalSpecialDomains;
  437. for (DWORD dw = 0; dw < dwDomains; dw++)
  438. {
  439. INET_ASSERT(*psz);
  440. while (*psz==TEXT(' '))
  441. psz++;
  442. INET_ASSERT(*psz);
  443. GlobalSDOffsets[dw] = psz;
  444. while (*psz && *psz!=TEXT(' '))
  445. {
  446. psz++;
  447. }
  448. if (*psz)
  449. {
  450. *psz = TEXT('\0');
  451. psz++;
  452. }
  453. }
  454. GlobalSDOffsets[dwDomains] = NULL;
  455. }
  456. }
  457. }
  458. RegCloseKey(hk);
  459. }
  460. }
  461. // WARNING: The following lines of code make it possible for cookies to be set for *.uk,
  462. // (for example) if "ku." is in the registry
  463. BOOL fRet = FALSE;
  464. if (GlobalSDOffsets)
  465. {
  466. for (DWORD i = 0; GlobalSDOffsets[i]; i++)
  467. {
  468. if (!StrCmpNI(pch, GlobalSDOffsets[i], nTopLevel)
  469. || !StrCmpNI(pch, GlobalSDOffsets[i], nTopLevel+nSecond+1))
  470. {
  471. fRet = TRUE;
  472. break;
  473. }
  474. }
  475. }
  476. return fRet;
  477. }
  478. static BOOL
  479. IsSpecialDomain(const char *pch, int nCount)
  480. {
  481. for (int i = 0 ; i < ARRAY_ELEMENTS(s_pachSpecialRestrictedDomains) ; i++ )
  482. {
  483. if (StrCmpNIC(pch, s_pachSpecialRestrictedDomains[i], nCount) == 0)
  484. return TRUE;
  485. }
  486. return FALSE;
  487. }
  488. static BOOL
  489. IsDomainLegal(const char *pchHeader, const char *pchDocument)
  490. {
  491. const char *pchCurrent = pchHeader;
  492. int nSegment = 0;
  493. int dwCharCount = 0;
  494. int rgcch[2] = { 0, 0 }; // How many characters between dots
  495. // Must have at least one period in name.
  496. // and contains nothing but '.' is illegal
  497. int dwSegmentLength = 0;
  498. const char * pchSecondPart = NULL; // for a domain string such as
  499. BOOL fIPAddress = TRUE;
  500. for (; *pchCurrent; pchCurrent++)
  501. {
  502. if (isalpha(*pchCurrent))
  503. {
  504. fIPAddress = FALSE;
  505. }
  506. if (*pchCurrent == '.')
  507. {
  508. if (nSegment < 2)
  509. {
  510. // Remember how many characters we have between the last two dots
  511. // For example if domain header is .microsoft.com
  512. // rgcch[0] should be 3 for "com"
  513. // rgcch[1] should be 9 for "microsoft"
  514. rgcch[nSegment] = dwSegmentLength;
  515. if (nSegment == 1)
  516. {
  517. pchSecondPart = pchCurrent - dwSegmentLength;
  518. }
  519. }
  520. dwSegmentLength = 0;
  521. nSegment += 1;
  522. }
  523. else
  524. {
  525. dwSegmentLength++;
  526. }
  527. dwCharCount++;
  528. }
  529. // The code below depends on the leading dot being removed from the domain header.
  530. // The parse code does that, but an assert here just in case something changes in the
  531. // parse code.
  532. INET_ASSERT(*(pchCurrent - 1) != '.');
  533. if (fIPAddress)
  534. {
  535. // If we're given an IP address, we must match the entire IP address, not just a part
  536. while (*pchHeader == *pchDocument && *pchDocument)
  537. {
  538. pchHeader++;
  539. pchDocument++;
  540. }
  541. return !(*pchHeader || *pchDocument);
  542. }
  543. // Remember the count of the characters between the begining of the header and
  544. // the first dot. So for domain=abc.com this will set rgch[1] = 3.
  545. // Note that this assumes that if domain=.abc.com the leading dot has been stripped
  546. // out in the parse code. See assert above.
  547. if (nSegment < 2 )
  548. {
  549. rgcch[nSegment] = dwSegmentLength;
  550. if (nSegment==1)
  551. {
  552. pchSecondPart = pchCurrent - dwSegmentLength;
  553. }
  554. }
  555. // If the domain name is of the form abc.xx.yy where the number of characters between the last two dots is less than
  556. // 2 we require a minimum of two embedded dots. This is so you are not allowed to set cookies readable by all of .co.nz for
  557. // example. However this rule is not sufficient and we special case things like .edu.nz as well.
  558. // darrenmi: new semantics: if segment 0 is less than or equal to 2, you need 2 embedded dots if segment 1 is a
  559. // "well known" string including edu, com, etc. and co.
  560. // fschwiet: An exception is now being made so that domains of the form "??.tv" are allowed.
  561. int cEmbeddedDotsNeeded = 1;
  562. BOOL fIsVerySpecialDomain = FALSE;
  563. if (rgcch[0] <= 2 && rgcch[1] <= 2)
  564. {
  565. fIsVerySpecialDomain = IsVerySpecialDomain(pchHeader, rgcch[0], rgcch[1]);
  566. }
  567. if(!fIsVerySpecialDomain
  568. && rgcch[0] <= 2
  569. && 0 != StrCmpNIC( pchHeader, s_chSpecialAllowedDomains, sizeof( s_chSpecialAllowedDomains) - 1)
  570. && (rgcch[1] <= 2
  571. || IsSpecialDomain(pchSecondPart, rgcch[1])))
  572. {
  573. cEmbeddedDotsNeeded = 2;
  574. }
  575. if (nSegment < cEmbeddedDotsNeeded || dwCharCount == nSegment)
  576. return FALSE;
  577. // Mismatch between header and document not allowed.
  578. // Must match full components of domain name.
  579. while (*pchHeader == *pchDocument && *pchDocument)
  580. {
  581. pchHeader += 1;
  582. pchDocument += 1;
  583. }
  584. return *pchHeader == 0 && (*pchDocument == 0 || *pchDocument == '.' );
  585. }
  586. void
  587. LowerCaseString(char *pch)
  588. {
  589. for (; *pch; pch++)
  590. {
  591. if (*pch >= 'A' && *pch <= 'Z')
  592. *pch += 'a' - 'A';
  593. }
  594. }
  595. static void
  596. ReverseString(char *pchFront)
  597. {
  598. char *pchBack;
  599. char ch;
  600. int cch;
  601. cch = strlen(pchFront);
  602. pchBack = pchFront + cch - 1;
  603. cch = cch / 2;
  604. while (--cch >= 0)
  605. {
  606. ch = tolower(*pchFront);
  607. *pchFront = tolower(*pchBack);
  608. *pchBack = ch;
  609. pchFront += 1;
  610. pchBack -= 1;
  611. }
  612. }
  613. static BOOL
  614. PathAndRDomainFromURL(const char *pchURL, char **ppchRDomain, char **ppchPath, BOOL *pfSecure, BOOL bStrip = TRUE)
  615. {
  616. char *pchDomainBuf;
  617. char *pchRDomain = NULL;
  618. char *pchPathBuf;
  619. char *pchPath = NULL;
  620. char *pchExtra;
  621. DWORD cchDomain;
  622. DWORD cchPath;
  623. DWORD cchExtra;
  624. BOOL fSuccess = FALSE;
  625. DWORD dwError;
  626. INTERNET_SCHEME ustScheme;
  627. char *pchURLCopy = NULL;
  628. pchURLCopy = NewString(pchURL);
  629. if (!pchURLCopy)
  630. {
  631. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  632. goto Cleanup;
  633. }
  634. dwError = CrackUrl((char *)pchURLCopy,
  635. 0,
  636. FALSE,
  637. &ustScheme,
  638. NULL, // Scheme Name
  639. NULL, // Scheme Lenth
  640. &pchDomainBuf,
  641. &cchDomain,
  642. NULL, // Internet Port
  643. NULL, // UserName
  644. NULL, // UserName Length
  645. NULL, // Password
  646. NULL, // Password Lenth
  647. &pchPathBuf,
  648. &cchPath,
  649. &pchExtra, // Extra
  650. &cchExtra, // Extra Length
  651. NULL);
  652. if (dwError != ERROR_SUCCESS)
  653. {
  654. SetLastError(dwError);
  655. goto Cleanup;
  656. }
  657. if ( ustScheme != INTERNET_SCHEME_HTTP &&
  658. ustScheme != INTERNET_SCHEME_HTTPS &&
  659. ustScheme != INTERNET_SCHEME_FILE)
  660. {
  661. //
  662. // known scheme should be supported
  663. // e.g. 3rd party pluggable protocol should be able to
  664. // call cookie api to setup cookies...
  665. //
  666. //a-thkesa. Allowing all of the pluggable protocols to setcookie creates security concerns.
  667. //So we only allow this "hcp" to qualify to setup cookie apart from 'file', Http, Https.
  668. //WinSE BUG: 24011 . In The windows help system they are setting cookie on a HCP protocol!.
  669. //we don't expect any protocol other then HTTP, HTTPS ,and File setting cookie here. But
  670. //we also have to allow the pluggable protocols to set cookie. Since allowing that causes security
  671. //problem, we only allow HCP which is a pluggable protocol used in windows Help and Support.
  672. //In future, we have to make sure to allow all of the pluggable protocol to set cookie, or
  673. //document only http, https and file can set cookies!
  674. //pluggable protocols returns INTERNET_SCHEME_UNKNOWN.
  675. //If so check if it returns INTERNET_SCHEME_UNKNOWN and check the protocols is a HCP protocol. If it is HCP
  676. // then do not set error.
  677. if( INTERNET_SCHEME_UNKNOWN == ustScheme ) // HCP returns INTERNET_SCHEME_UNKNOWN
  678. {
  679. char szProtocolU[]= "HCP:";
  680. char szProtocolL[]= "hcp:";
  681. short ilen = 0;
  682. while(4 > ilen) // check only the first four char.
  683. {
  684. if(*(pchURLCopy+ilen) != *(szProtocolU+ilen) &&
  685. *(pchURLCopy+ilen) != *(szProtocolL+ilen) )
  686. {
  687. SetLastError(ERROR_INVALID_PARAMETER);
  688. goto Cleanup;
  689. }
  690. ilen++;
  691. }
  692. }
  693. else
  694. {
  695. SetLastError(ERROR_INVALID_PARAMETER);
  696. goto Cleanup;
  697. }
  698. }
  699. *pfSecure = ustScheme == INTERNET_SCHEME_HTTPS;
  700. if (ustScheme == INTERNET_SCHEME_FILE)
  701. {
  702. pchDomainBuf = "~~local~~";
  703. cchDomain = sizeof("~~local~~") - 1;
  704. }
  705. else
  706. {
  707. // SECURITY: It's possible for us to navigate to a carefully
  708. // constructed URL such as http://server%3F.microsoft.com.
  709. // This results in a cracked hostname of server?.microsoft.com.
  710. // Given the current architecture, it would probably be best to
  711. // make CrackUrl smarter. However, the minimal fix below prevents
  712. // the x-domain security violation without breaking escaped cases
  713. // that work today that customers may expect to be allowed.
  714. DWORD n;
  715. for (n = 0; n < cchDomain; n++)
  716. {
  717. // RFC 952 as amended by RFC 1123: the only valid chars are
  718. // a-z, A-Z, 0-9, '-', and '.'. The last two are delimiters
  719. // which cannot start or end the name, but this detail doesn't
  720. // matter for the security fix.
  721. if (!((pchDomainBuf[n] >= 'a' && pchDomainBuf[n] <= 'z') ||
  722. (pchDomainBuf[n] >= 'A' && pchDomainBuf[n] <= 'Z') ||
  723. (pchDomainBuf[n] >= '0' && pchDomainBuf[n] <= '9') ||
  724. (pchDomainBuf[n] == '-') ||
  725. (pchDomainBuf[n] == '.')))
  726. {
  727. // Since we're incorrectly cracking the URL, don't worry
  728. // about fixing up the path.
  729. fSuccess = FALSE;
  730. goto Cleanup;
  731. }
  732. }
  733. }
  734. if(bStrip)
  735. {
  736. while (cchPath > 0)
  737. {
  738. if (pchPathBuf[cchPath - 1] == '/' || pchPathBuf[cchPath - 1] == '\\')
  739. {
  740. break;
  741. }
  742. cchPath -= 1;
  743. }
  744. }
  745. pchRDomain = StrnDup(pchDomainBuf, cchDomain);
  746. if (!pchRDomain)
  747. goto Cleanup;
  748. LowerCaseString(pchRDomain);
  749. ReverseString(pchRDomain);
  750. pchPath = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cchPath + 2);
  751. if (!pchPath)
  752. goto Cleanup;
  753. if (*pchPathBuf != '/')
  754. {
  755. *pchPath = '/';
  756. memcpy(pchPath + 1, pchPathBuf, cchPath);
  757. pchPath[cchPath + 1] = TEXT('\0');
  758. }
  759. else
  760. {
  761. memcpy(pchPath, pchPathBuf, cchPath);
  762. pchPath[cchPath] = TEXT('\0');
  763. }
  764. fSuccess = TRUE;
  765. Cleanup:
  766. if (!fSuccess)
  767. {
  768. if (pchRDomain)
  769. FREE_MEMORY(pchRDomain);
  770. if (pchPath)
  771. FREE_MEMORY(pchPath);
  772. }
  773. else
  774. {
  775. *ppchRDomain = pchRDomain;
  776. *ppchPath = pchPath;
  777. }
  778. if (pchURLCopy)
  779. FREE_MEMORY(pchURLCopy);
  780. return fSuccess;
  781. }
  782. //---------------------------------------------------------------------------
  783. //
  784. // CCookieBase implementation
  785. //
  786. //---------------------------------------------------------------------------
  787. void *
  788. CCookieBase::operator new(size_t cb, size_t cbExtra)
  789. {
  790. void *pv = ALLOCATE_MEMORY(LMEM_FIXED, cb + cbExtra);
  791. if (!pv)
  792. {
  793. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  794. return NULL;
  795. }
  796. memset(pv, 0, cb);
  797. return pv;
  798. }
  799. inline void
  800. CCookieBase::operator delete(void *pv)
  801. {
  802. FREE_MEMORY(pv);
  803. }
  804. //---------------------------------------------------------------------------
  805. //
  806. // CCookie implementation
  807. //
  808. //---------------------------------------------------------------------------
  809. CCookie *
  810. CCookie::Construct(const char *pchName)
  811. {
  812. CCookie *pCookie = new(strlen(pchName) + 1) CCookie();
  813. if (!pCookie)
  814. return NULL;
  815. pCookie->_pchName = (char *)(pCookie + 1);
  816. pCookie->_pchValue = s_achEmpty;
  817. strcpy(pCookie->_pchName, pchName);
  818. pCookie->_dwFlags = COOKIE_SESSION;
  819. pCookie->_dwPromptMask = 0;
  820. return pCookie;
  821. }
  822. CCookie::~CCookie()
  823. {
  824. if (_pchValue != s_achEmpty)
  825. FREE_MEMORY(_pchValue);
  826. }
  827. BOOL
  828. CCookie::SetValue(const char *pchValue)
  829. {
  830. int cchValue;
  831. if (_pchValue != s_achEmpty)
  832. FREE_MEMORY(_pchValue);
  833. if (!pchValue || !*pchValue)
  834. {
  835. _pchValue = s_achEmpty;
  836. }
  837. else
  838. {
  839. cchValue = strlen(pchValue) + 1;
  840. _pchValue = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cchValue);
  841. if (!_pchValue)
  842. {
  843. _pchValue = s_achEmpty;
  844. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  845. return FALSE;
  846. }
  847. memcpy(_pchValue, pchValue, cchValue);
  848. }
  849. return TRUE;
  850. }
  851. BOOL
  852. CCookie::CanSend(FILETIME *pftCurrent, BOOL fSecure)
  853. {
  854. return (fSecure || !(_dwFlags & COOKIE_SECURE)) &&
  855. (CompareFileTime(_ftExpire, *pftCurrent) >= 0);
  856. }
  857. BOOL CCookie::PurgePersistent(void *)
  858. {
  859. return IsPersistent();
  860. }
  861. BOOL CCookie::PurgeAll(void *)
  862. {
  863. return TRUE;
  864. }
  865. BOOL CCookie::PurgeByName(void *pvName)
  866. {
  867. return strcmp((char *)pvName, _pchName) == 0;
  868. }
  869. BOOL CCookie::PurgeThis(void *pvThis)
  870. {
  871. return this == (CCookie *)pvThis;
  872. }
  873. BOOL CCookie::PurgeExpired(void *pvCurrent)
  874. {
  875. return CompareFileTime(_ftExpire, *(FILETIME *)pvCurrent) < 0;
  876. }
  877. static BOOL
  878. WriteString(HANDLE hFile, const char *pch)
  879. {
  880. DWORD cb;
  881. return pch && *pch ? WriteFile(hFile, pch, strlen(pch), &cb, NULL) : TRUE;
  882. }
  883. static BOOL
  884. WriteStringLF(HANDLE hFile, const char *pch)
  885. {
  886. DWORD cb;
  887. if (!WriteString(hFile, pch)) return FALSE;
  888. return WriteFile(hFile, "\n", 1, &cb, NULL);
  889. }
  890. BOOL
  891. CCookie::WriteCacheFile(HANDLE hFile, char *pchRDomain, char *pchPath)
  892. {
  893. BOOL fSuccess = FALSE;
  894. char achBuf[128];
  895. ReverseString(pchRDomain);
  896. if (!WriteStringLF(hFile, _pchName)) goto Cleanup;
  897. if (!WriteStringLF(hFile, _pchValue)) goto Cleanup;
  898. if (!WriteString(hFile, pchRDomain)) goto Cleanup;
  899. if (!WriteStringLF(hFile, pchPath)) goto Cleanup;
  900. wsprintf(achBuf, "%u\n%u\n%u\n%u\n%u\n*\n",
  901. _dwFlags,
  902. _ftExpire.dwLowDateTime,
  903. _ftExpire.dwHighDateTime,
  904. _ftLastModified.dwLowDateTime,
  905. _ftLastModified.dwHighDateTime);
  906. if (!WriteString(hFile, achBuf)) goto Cleanup;
  907. fSuccess = TRUE;
  908. Cleanup:
  909. ReverseString(pchRDomain);
  910. return fSuccess;
  911. }
  912. //---------------------------------------------------------------------------
  913. //
  914. // CCookieLocation implementation
  915. //
  916. //---------------------------------------------------------------------------
  917. CCookieLocation *
  918. CCookieLocation::Construct(const char *pchRDomain, const char *pchPath)
  919. {
  920. int cchPath = strlen(pchPath);
  921. CCookieLocation *pLocation = new(strlen(pchRDomain) + cchPath + 2) CCookieLocation();
  922. if (!pLocation)
  923. return NULL;
  924. pLocation->_cchPath = cchPath;
  925. pLocation->_pchPath = (char *)(pLocation + 1);
  926. pLocation->_pchRDomain = pLocation->_pchPath + cchPath + 1;
  927. strcpy(pLocation->_pchRDomain, pchRDomain);
  928. strcpy(pLocation->_pchPath, pchPath);
  929. return pLocation;
  930. }
  931. CCookieLocation::~CCookieLocation()
  932. {
  933. Purge(CCookie::PurgeAll, NULL);
  934. }
  935. CCookie *
  936. CCookieLocation::GetCookie(const char *pchName, BOOL fCreate)
  937. {
  938. CCookie *pCookie;
  939. CCookie **ppCookie = &_pCookieKids;
  940. for (pCookie = _pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  941. {
  942. if (strcmp(pchName, pCookie->_pchName) == 0)
  943. return pCookie;
  944. ppCookie = &pCookie->_pCookieNext;
  945. }
  946. if (!fCreate)
  947. return NULL;
  948. pCookie = CCookie::Construct(pchName);
  949. if (!pCookie)
  950. return NULL;
  951. //
  952. // Insert cookie at end of list to match Navigator's behavior.
  953. //
  954. pCookie->_pCookieNext = NULL;
  955. *ppCookie = pCookie;
  956. return pCookie;
  957. }
  958. BOOL
  959. CCookieLocation::Purge(BOOL (CCookie::*pfnPurge)(void *), void *pv)
  960. {
  961. CCookie **ppCookie = &_pCookieKids;
  962. CCookie *pCookie;
  963. BOOL fPersistentDeleted = FALSE;
  964. while ((pCookie = *ppCookie) != NULL)
  965. {
  966. if ((pCookie->*pfnPurge)(pv))
  967. {
  968. *ppCookie = pCookie->_pCookieNext;
  969. fPersistentDeleted |= pCookie->IsPersistent();
  970. delete pCookie;
  971. }
  972. else
  973. {
  974. ppCookie = &pCookie->_pCookieNext;
  975. }
  976. }
  977. return fPersistentDeleted;
  978. }
  979. BOOL
  980. CCookieLocation::Purge(FILETIME *pftCurrent, BOOL fSession)
  981. {
  982. if (!_fCacheFileExists)
  983. {
  984. // If cache file is gone, then delete all persistent
  985. // cookies. If there's no cache file, then it's certainly
  986. // the case that we do not need to read the cache file.
  987. Purge(CCookie::PurgePersistent, NULL);
  988. _fReadFromCacheFileNeeded = FALSE;
  989. }
  990. // This is a good time to check for expired persistent cookies.
  991. if (!_fReadFromCacheFileNeeded && Purge(CCookie::PurgeExpired, pftCurrent))
  992. {
  993. WriteCacheFile();
  994. }
  995. if (fSession)
  996. {
  997. // If we are purging because a session ended, nuke
  998. // everything in sight. If we deleted a persistent
  999. // cookie, note that we need to read the cache file
  1000. // on next access.
  1001. _fReadFromCacheFileNeeded |= Purge(CCookie::PurgeAll, NULL);
  1002. }
  1003. return !_fReadFromCacheFileNeeded && _pCookieKids == NULL;
  1004. }
  1005. char *
  1006. CCookieLocation::GetCacheURL()
  1007. {
  1008. char *pchURL;
  1009. char *pch;
  1010. int cchScheme = sizeof(s_achCookieScheme) - 1;
  1011. int cchUser = vdwCurrentUserLen;
  1012. int cchAt = 1;
  1013. int cchDomain = strlen(_pchRDomain);
  1014. int cchPath = strlen(_pchPath);
  1015. pchURL = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cchScheme + cchUser + cchAt + cchDomain + cchPath + 1);
  1016. if (!pchURL)
  1017. return NULL;
  1018. pch = pchURL;
  1019. memcpy(pch, s_achCookieScheme, cchScheme);
  1020. pch += cchScheme;
  1021. memcpy(pch, vszCurrentUser, cchUser);
  1022. pch += cchUser;
  1023. memcpy(pch, "@", cchAt);
  1024. pch += cchAt;
  1025. ReverseString(_pchRDomain);
  1026. memcpy(pch, _pchRDomain, cchDomain);
  1027. ReverseString(_pchRDomain);
  1028. pch += cchDomain;
  1029. strcpy(pch, _pchPath);
  1030. return pchURL;
  1031. }
  1032. BOOL
  1033. CCookieLocation::WriteCacheFile()
  1034. {
  1035. HANDLE hFile = INVALID_HANDLE_VALUE;
  1036. char achFile[MAX_PATH];
  1037. char * pchURL = NULL;
  1038. BOOL fSuccess = FALSE;
  1039. CCookie * pCookie;
  1040. FILETIME ftLastExpire = { 0, 0 };
  1041. achFile[0] = 0;
  1042. GetCurrentGmtTime(&_ftCacheFileLastModified);
  1043. //
  1044. // Determine the latest expiry time and if we have something to write.
  1045. //
  1046. for (pCookie = _pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  1047. {
  1048. if (pCookie->IsPersistent() && CompareFileTime(pCookie->_ftExpire, ftLastExpire) > 0)
  1049. {
  1050. ftLastExpire = pCookie->_ftExpire;
  1051. }
  1052. }
  1053. pchURL = GetCacheURL();
  1054. if (!pchURL)
  1055. goto Cleanup;
  1056. if (CompareFileTime(ftLastExpire, _ftCacheFileLastModified) < 0)
  1057. {
  1058. fSuccess = TRUE;
  1059. DeleteUrlCacheEntry(pchURL);
  1060. _fCacheFileExists = FALSE;
  1061. goto Cleanup;
  1062. }
  1063. _fCacheFileExists = TRUE;
  1064. if (!CreateUrlCacheEntry(pchURL,
  1065. 0, // Estimated size
  1066. "txt", // File extension
  1067. achFile,
  1068. 0))
  1069. goto Cleanup;
  1070. hFile = CreateFile(
  1071. achFile,
  1072. GENERIC_WRITE,
  1073. 0, // no sharing.
  1074. NULL,
  1075. TRUNCATE_EXISTING,
  1076. FILE_ATTRIBUTE_NORMAL,
  1077. NULL );
  1078. if (hFile == INVALID_HANDLE_VALUE)
  1079. goto Cleanup;
  1080. for (pCookie = _pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  1081. {
  1082. if (pCookie->IsPersistent() && CompareFileTime(pCookie->_ftExpire, _ftCacheFileLastModified) >= 0)
  1083. {
  1084. if (!pCookie->WriteCacheFile(hFile, _pchRDomain, _pchPath))
  1085. goto Cleanup;
  1086. }
  1087. }
  1088. CloseHandle(hFile);
  1089. hFile = INVALID_HANDLE_VALUE;
  1090. if (!CommitUrlCacheEntry(pchURL,
  1091. achFile,
  1092. ftLastExpire,
  1093. _ftCacheFileLastModified,
  1094. NORMAL_CACHE_ENTRY | COOKIE_CACHE_ENTRY,
  1095. NULL,
  1096. 0,
  1097. NULL,
  1098. 0 ))
  1099. goto Cleanup;
  1100. MarkCacheModified();
  1101. fSuccess = TRUE;
  1102. Cleanup:
  1103. if (hFile != INVALID_HANDLE_VALUE)
  1104. CloseHandle(hFile);
  1105. if (!fSuccess)
  1106. {
  1107. if (achFile[0])
  1108. DeleteFile(achFile);
  1109. if (pchURL)
  1110. DeleteUrlCacheEntry(pchURL);
  1111. }
  1112. if (pchURL)
  1113. FREE_MEMORY(pchURL);
  1114. return fSuccess;
  1115. }
  1116. static char *
  1117. ScanString(char *pch, char **pchStr)
  1118. {
  1119. *pchStr = pch;
  1120. for (; *pch; *pch++)
  1121. {
  1122. if (*pch == '\n')
  1123. {
  1124. *pch = 0;
  1125. pch += 1;
  1126. break;
  1127. }
  1128. }
  1129. return pch;
  1130. }
  1131. static char *
  1132. ScanNumber(char *pch, DWORD *pdw)
  1133. {
  1134. DWORD dw = 0;
  1135. char *pchJunk;
  1136. for (; *pch >= '0' && *pch <= '9'; *pch++)
  1137. {
  1138. dw = (dw * 10) + *pch - '0';
  1139. }
  1140. *pdw = dw;
  1141. return ScanString(pch, &pchJunk);
  1142. }
  1143. BOOL
  1144. CCookieLocation::ReadCacheFile()
  1145. {
  1146. char * pchURL = NULL;
  1147. char * pch;
  1148. DWORD cbCEI;
  1149. HANDLE hCacheStream = NULL;
  1150. char * pchBuffer = NULL;
  1151. CCookie * pCookie;
  1152. CACHE_ENTRY_INFO_BUFFER *pcei = new CACHE_ENTRY_INFO_BUFFER;
  1153. if (pcei == NULL)
  1154. goto Cleanup;
  1155. _fReadFromCacheFileNeeded = FALSE;
  1156. pchURL = GetCacheURL();
  1157. if (!pchURL)
  1158. goto Cleanup;
  1159. cbCEI = sizeof(*pcei);
  1160. hCacheStream = RetrieveUrlCacheEntryStream(
  1161. pchURL,
  1162. pcei,
  1163. &cbCEI,
  1164. FALSE, // sequential access
  1165. 0);
  1166. if (!hCacheStream)
  1167. {
  1168. // If we failed to get the entry, try to nuke it so it does not
  1169. // bother us in the future.
  1170. DeleteUrlCacheEntry(pchURL);
  1171. goto Cleanup;
  1172. }
  1173. // Old cache files to not have last modified time set.
  1174. // Bump the time up so that we can use file times to determine
  1175. // if we need to resync a file.
  1176. if (IsZero(&(pcei->LastModifiedTime)))
  1177. {
  1178. pcei->LastModifiedTime.dwLowDateTime = 1;
  1179. }
  1180. _ftCacheFileLastModified = pcei->LastModifiedTime;
  1181. // Read cache file into a null terminated buffer.
  1182. pchBuffer = (char *)ALLOCATE_MEMORY(LMEM_FIXED, pcei->dwSizeLow + 1 * sizeof(char));
  1183. if (!pchBuffer)
  1184. {
  1185. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1186. goto Cleanup;
  1187. }
  1188. if (!ReadUrlCacheEntryStream(hCacheStream, 0, pchBuffer, &pcei->dwSizeLow, 0))
  1189. goto Cleanup;
  1190. pchBuffer[pcei->dwSizeLow] = 0;
  1191. // Blow away all existing persistent cookies.
  1192. Purge(CCookie::PurgePersistent, NULL);
  1193. // Parse cookies from the buffer;
  1194. for (pch = pchBuffer; *pch; )
  1195. {
  1196. char *pchName;
  1197. char *pchValue;
  1198. char *pchLocation;
  1199. char *pchStar;
  1200. DWORD dwFlags;
  1201. FILETIME ftExpire;
  1202. FILETIME ftLast;
  1203. pch = ScanString(pch, &pchName);
  1204. pch = ScanString(pch, &pchValue);
  1205. pch = ScanString(pch, &pchLocation);
  1206. pch = ScanNumber(pch, &dwFlags);
  1207. pch = ScanNumber(pch, &ftExpire.dwLowDateTime);
  1208. pch = ScanNumber(pch, &ftExpire.dwHighDateTime);
  1209. pch = ScanNumber(pch, &ftLast.dwLowDateTime);
  1210. pch = ScanNumber(pch, &ftLast.dwHighDateTime);
  1211. pch = ScanString(pch, &pchStar);
  1212. if (strcmp(pchStar, "*"))
  1213. {
  1214. goto Cleanup;
  1215. }
  1216. pCookie = GetCookie(pchName, TRUE);
  1217. if (!pCookie)
  1218. goto Cleanup;
  1219. // Initialize the cookie.
  1220. pCookie->SetValue(pchValue);
  1221. pCookie->_ftExpire = ftExpire;
  1222. pCookie->_ftLastModified = ftLast;
  1223. pCookie->_dwFlags = dwFlags;
  1224. }
  1225. Cleanup:
  1226. if (pcei)
  1227. delete pcei;
  1228. if (hCacheStream)
  1229. UnlockUrlCacheEntryStream(hCacheStream, 0);
  1230. if (pchURL)
  1231. FREE_MEMORY(pchURL);
  1232. if (pchBuffer)
  1233. FREE_MEMORY(pchBuffer);
  1234. return TRUE;
  1235. }
  1236. BOOL
  1237. CCookieLocation::IsMatch(char *pchRDomain, char *pchPath)
  1238. {
  1239. return IsDomainMatch(_pchRDomain, pchRDomain) &&
  1240. IsPathMatch(_pchPath, pchPath);
  1241. }
  1242. BOOL
  1243. CCookieLocation::ReadCacheFileIfNeeded()
  1244. {
  1245. return _fReadFromCacheFileNeeded ? ReadCacheFile() : TRUE;
  1246. }
  1247. //---------------------------------------------------------------------------
  1248. //
  1249. // CCookieJar implementation
  1250. //
  1251. //---------------------------------------------------------------------------
  1252. CCookieJar *
  1253. CCookieJar::Construct()
  1254. {
  1255. CCookieJar *s_pJar = new(0) CCookieJar();
  1256. if (!s_pJar)
  1257. return NULL;
  1258. return s_pJar;
  1259. }
  1260. CCookieJar::~CCookieJar()
  1261. {
  1262. for (int i = ARRAY_ELEMENTS(_apLocation); --i >= 0; )
  1263. {
  1264. CCookieLocation *pLocation = _apLocation[i];
  1265. while (pLocation)
  1266. {
  1267. CCookieLocation *pLocationT = pLocation->_pLocationNext;
  1268. delete pLocation;
  1269. pLocation = pLocationT;
  1270. }
  1271. }
  1272. }
  1273. CCookieLocation **
  1274. CCookieJar::GetBucket(const char *pchRDomain)
  1275. {
  1276. int ch;
  1277. int cPeriod = 0;
  1278. unsigned int hash = 0;
  1279. ASSERT_CRITSEC();
  1280. for (; (ch = *pchRDomain) != 0; pchRDomain++)
  1281. {
  1282. if (ch == '.')
  1283. {
  1284. cPeriod += 1;
  1285. if (cPeriod >= 2)
  1286. break;
  1287. }
  1288. hash = (hash * 29) + ch;
  1289. }
  1290. hash = hash % ARRAY_ELEMENTS(_apLocation);
  1291. return &_apLocation[hash];
  1292. }
  1293. CCookieLocation *
  1294. CCookieJar::GetLocation(const char *pchRDomain, const char *pchPath, BOOL fCreate)
  1295. {
  1296. ASSERT_CRITSEC();
  1297. int cchPath = strlen(pchPath);
  1298. CCookieLocation *pLocation = NULL;
  1299. CCookieLocation **ppLocation = GetBucket(pchRDomain);
  1300. // To support sending more specific cookies before less specific,
  1301. // we keep list sorted by path length.
  1302. while ((pLocation = *ppLocation) != NULL)
  1303. {
  1304. if (pLocation->_cchPath < cchPath)
  1305. break;
  1306. if (strcmp(pLocation->_pchPath, pchPath) == 0 &&
  1307. strcmp(pLocation->_pchRDomain, pchRDomain) == 0)
  1308. return pLocation;
  1309. ppLocation = &pLocation->_pLocationNext;
  1310. }
  1311. if (!fCreate)
  1312. goto Cleanup;
  1313. pLocation = CCookieLocation::Construct(pchRDomain, pchPath);
  1314. if (!pLocation)
  1315. goto Cleanup;
  1316. pLocation->_pLocationNext = *ppLocation;
  1317. *ppLocation = pLocation;
  1318. Cleanup:
  1319. return pLocation;
  1320. }
  1321. void
  1322. CCookieJar::Purge(FILETIME *pftCurrent, BOOL fSession)
  1323. {
  1324. ASSERT_CRITSEC();
  1325. for (int i = ARRAY_ELEMENTS(_apLocation); --i >= 0; )
  1326. {
  1327. CCookieLocation **ppLocation = &_apLocation[i];
  1328. CCookieLocation *pLocation;
  1329. while ((pLocation = *ppLocation) != NULL)
  1330. {
  1331. if (pLocation->Purge(pftCurrent, fSession))
  1332. {
  1333. *ppLocation = pLocation->_pLocationNext;
  1334. delete pLocation;
  1335. }
  1336. else
  1337. {
  1338. ppLocation = &pLocation->_pLocationNext;
  1339. }
  1340. }
  1341. }
  1342. }
  1343. BOOL
  1344. CCookieJar::SyncWithCache()
  1345. {
  1346. DWORD dwBufferSize;
  1347. HANDLE hEnum = NULL;
  1348. int cchUserNameAt;
  1349. char achUserNameAt[MAX_PATH + 2];
  1350. FILETIME ftCurrent;
  1351. char * pchRDomain;
  1352. char * pchPath;
  1353. char * pch;
  1354. CACHE_ENTRY_INFO_BUFFER *pcei;
  1355. CCookieLocation *pLocation;
  1356. ASSERT_CRITSEC();
  1357. pcei = new CACHE_ENTRY_INFO_BUFFER;
  1358. if (pcei == NULL)
  1359. goto Cleanup;
  1360. if (!vdwCurrentUserLen)
  1361. GetWininetUserName();
  1362. strcpy(achUserNameAt, vszCurrentUser);
  1363. strcat(achUserNameAt, "@");
  1364. cchUserNameAt = vdwCurrentUserLen+1;
  1365. dwBufferSize = sizeof(*pcei);
  1366. hEnum = FindFirstUrlCacheEntry(s_achCookieScheme, pcei, &dwBufferSize);
  1367. for (int i = ARRAY_ELEMENTS(_apLocation); --i >= 0; )
  1368. {
  1369. for (pLocation = _apLocation[i];
  1370. pLocation;
  1371. pLocation = pLocation->_pLocationNext)
  1372. {
  1373. pLocation->_fCacheFileExists = FALSE;
  1374. }
  1375. }
  1376. if (hEnum)
  1377. {
  1378. do
  1379. {
  1380. if ( pcei->lpszSourceUrlName &&
  1381. (strnicmp(pcei->lpszSourceUrlName, s_achCookieScheme, sizeof(s_achCookieScheme) - 1 ) == 0) &&
  1382. (strnicmp(pcei->lpszSourceUrlName+sizeof(s_achCookieScheme) - 1,achUserNameAt, cchUserNameAt) == 0))
  1383. {
  1384. // Split domain name from path in buffer.
  1385. // Slide domain name down to make space for null terminator
  1386. // between domain and path.
  1387. pchRDomain = pcei->lpszSourceUrlName+sizeof(s_achCookieScheme) - 1 + cchUserNameAt - 1;
  1388. for (pch = pchRDomain + 1; *pch && *pch != '/'; pch++)
  1389. {
  1390. pch[-1] = pch[0];
  1391. }
  1392. pch[-1] = 0;
  1393. pchPath = pch;
  1394. ReverseString(pchRDomain);
  1395. pLocation = GetLocation(pchRDomain, pchPath, TRUE);
  1396. if (!pLocation)
  1397. {
  1398. continue;
  1399. }
  1400. // Old cache files to not have last modified time set.
  1401. // Bump the time up so that we can use file times to determine
  1402. // if we need to resync a file.
  1403. if (IsZero(&pcei->LastModifiedTime))
  1404. {
  1405. pcei->LastModifiedTime.dwLowDateTime = 1;
  1406. }
  1407. if (CompareFileTime(pLocation->_ftCacheFileLastModified, pcei->LastModifiedTime) < 0)
  1408. {
  1409. pLocation->_fReadFromCacheFileNeeded = TRUE;
  1410. }
  1411. pLocation->_fCacheFileExists = TRUE;
  1412. }
  1413. dwBufferSize = sizeof(*pcei);
  1414. } while (FindNextUrlCacheEntryA(hEnum, pcei, &dwBufferSize));
  1415. FindCloseUrlCache(hEnum);
  1416. }
  1417. // Now purge everthing we didn't get .
  1418. GetCurrentGmtTime(&ftCurrent);
  1419. Purge(&ftCurrent, FALSE);
  1420. Cleanup:
  1421. if (pcei)
  1422. delete pcei;
  1423. return TRUE;
  1424. }
  1425. BOOL
  1426. CCookieJar::SyncWithCacheIfNeeded()
  1427. {
  1428. return IsCacheModified() ? SyncWithCache() : TRUE;
  1429. }
  1430. struct PARSE
  1431. {
  1432. char *pchBuffer;
  1433. char *pchToken;
  1434. BOOL fEqualFound;
  1435. };
  1436. static char *
  1437. SkipWS(char *pch)
  1438. {
  1439. while (*pch == ' ' || *pch == '\t')
  1440. pch += 1;
  1441. return pch;
  1442. }
  1443. static BOOL
  1444. ParseToken(PARSE *pParse, BOOL fBreakOnSpecialTokens, BOOL fBreakOnEqual)
  1445. {
  1446. char ch;
  1447. char *pch;
  1448. char *pchEndToken;
  1449. pParse->fEqualFound = FALSE;
  1450. pch = SkipWS(pParse->pchBuffer);
  1451. if (*pch == 0)
  1452. {
  1453. pParse->pchToken = pch;
  1454. return FALSE;
  1455. }
  1456. pParse->pchToken = pch;
  1457. pchEndToken = pch;
  1458. while ((ch = *pch) != 0)
  1459. {
  1460. pch += 1;
  1461. if (ch == ';')
  1462. {
  1463. break;
  1464. }
  1465. else if (fBreakOnEqual && ch == '=')
  1466. {
  1467. pParse->fEqualFound = TRUE;
  1468. break;
  1469. }
  1470. else if (ch == ' ' || ch == '\t')
  1471. {
  1472. if (fBreakOnSpecialTokens)
  1473. {
  1474. if ((strnicmp(pch, "expires", sizeof("expires") - 1) == 0) ||
  1475. (strnicmp(pch, "path", sizeof("path") - 1) == 0) ||
  1476. (strnicmp(pch, "domain", sizeof("domain") - 1) == 0) ||
  1477. (strnicmp(pch, "secure", sizeof("secure") - 1) == 0) ||
  1478. (strnicmp(pch, gcszNoScriptField, sizeof(gcszNoScriptField) - 1) == 0))
  1479. {
  1480. break;
  1481. }
  1482. }
  1483. }
  1484. else
  1485. {
  1486. pchEndToken = pch;
  1487. }
  1488. }
  1489. *pchEndToken = 0;
  1490. pParse->pchBuffer = pch;
  1491. return TRUE;
  1492. }
  1493. static void
  1494. ParseHeader(
  1495. char *pchHeader,
  1496. char **ppchName,
  1497. char **ppchValue,
  1498. char **ppchPath,
  1499. char **ppchRDomain,
  1500. DWORD *pdwFlags,
  1501. FILETIME *pftExpire)
  1502. {
  1503. PARSE parse;
  1504. parse.pchBuffer = pchHeader;
  1505. *ppchName = NULL;
  1506. *ppchValue = NULL;
  1507. *ppchPath = NULL;
  1508. *ppchRDomain = NULL;
  1509. *pdwFlags = COOKIE_SESSION;
  1510. // If only one of name or value is specified, Navigator
  1511. // uses name=<blank> and value as what ever was specified.
  1512. // Example: =foo -> name: <blank> value: foo
  1513. // foo -> name: <blank> value: foo
  1514. // foo= -> name: foo value: <blank>
  1515. if (ParseToken(&parse, FALSE, TRUE))
  1516. {
  1517. *ppchName = parse.pchToken;
  1518. if (parse.fEqualFound)
  1519. {
  1520. if (ParseToken(&parse, FALSE, FALSE))
  1521. {
  1522. *ppchValue = parse.pchToken;
  1523. }
  1524. else
  1525. {
  1526. *ppchValue = s_achEmpty;
  1527. }
  1528. }
  1529. else
  1530. {
  1531. *ppchValue = *ppchName;
  1532. *ppchName = s_achEmpty;
  1533. }
  1534. }
  1535. while (ParseToken(&parse, FALSE, TRUE))
  1536. {
  1537. if (stricmp(parse.pchToken, "expires") == 0)
  1538. {
  1539. if (parse.fEqualFound && ParseToken(&parse, TRUE, FALSE))
  1540. {
  1541. if (FParseHttpDate(pftExpire, parse.pchToken))
  1542. {
  1543. // Don't make the cookie persistent if the parsing fails
  1544. *pdwFlags &= ~COOKIE_SESSION;
  1545. }
  1546. }
  1547. }
  1548. else if (stricmp(parse.pchToken, "domain") == 0)
  1549. {
  1550. if (parse.fEqualFound )
  1551. {
  1552. if( ParseToken(&parse, TRUE, FALSE))
  1553. {
  1554. // Previous versions of IE tossed the leading
  1555. // "." on domain names. We continue this behavior
  1556. // to maintain compatiblity with old cookie files.
  1557. // See comments at the top of this file for more
  1558. // information.
  1559. if (*parse.pchToken == '.') parse.pchToken += 1;
  1560. ReverseString(parse.pchToken);
  1561. *ppchRDomain = parse.pchToken;
  1562. }
  1563. else
  1564. {
  1565. *ppchRDomain = parse.pchToken;
  1566. }
  1567. }
  1568. }
  1569. else if (stricmp(parse.pchToken, "path") == 0)
  1570. {
  1571. if (parse.fEqualFound && ParseToken(&parse, TRUE, FALSE))
  1572. {
  1573. *ppchPath = parse.pchToken;
  1574. }
  1575. else
  1576. {
  1577. *ppchPath = s_achEmpty;
  1578. }
  1579. }
  1580. else if (stricmp(parse.pchToken, "secure") == 0)
  1581. {
  1582. *pdwFlags |= COOKIE_SECURE;
  1583. if (parse.fEqualFound)
  1584. {
  1585. ParseToken(&parse, TRUE, FALSE);
  1586. }
  1587. }
  1588. else if (stricmp(parse.pchToken, gcszNoScriptField) == 0)
  1589. {
  1590. *pdwFlags |= COOKIE_NONSCRIPTABLE;
  1591. if (parse.fEqualFound)
  1592. {
  1593. ParseToken(&parse, TRUE, FALSE);
  1594. }
  1595. }
  1596. else
  1597. {
  1598. if (parse.fEqualFound)
  1599. {
  1600. ParseToken(&parse, TRUE, FALSE);
  1601. }
  1602. }
  1603. }
  1604. if (!*ppchName)
  1605. {
  1606. *ppchName = *ppchValue = s_achEmpty;
  1607. }
  1608. if (*pdwFlags & COOKIE_SESSION)
  1609. {
  1610. pftExpire->dwLowDateTime = 0xFFFFFFFF;
  1611. pftExpire->dwHighDateTime = 0x7FFFFFFF;
  1612. }
  1613. }
  1614. /* Replace non-printable characters in a string with given char
  1615. This is used to enforce the RFC requirement that cookie header
  1616. tokens can only contain chars in the range 0x20-0x7F.
  1617. DCR: For compatability reasons we only replace control characters
  1618. in the range 0x00 - 0x1F inclusive.
  1619. There are international websites which depend on being able to
  1620. set cookies with DBCS characters in name and value.
  1621. (That assumption is wishful thinking and a violation of RFC2965.)
  1622. */
  1623. void replaceControlChars(char *pszstr, char chReplace='_') {
  1624. if (!pszstr)
  1625. return;
  1626. while (*pszstr) {
  1627. if (*pszstr>=0x00 && *pszstr<=0x1F)
  1628. *pszstr = chReplace;
  1629. pszstr++;
  1630. }
  1631. }
  1632. // free's an INTERNET_COOKIE structure
  1633. static VOID
  1634. DestroyInternetCookie(INTERNET_COOKIE *pic)
  1635. {
  1636. if ( pic != NULL )
  1637. {
  1638. if ( pic->pszDomain ) {
  1639. FREE_MEMORY(pic->pszDomain);
  1640. }
  1641. if ( pic->pszPath ) {
  1642. FREE_MEMORY(pic->pszPath);
  1643. }
  1644. if ( pic->pszName ) {
  1645. FREE_MEMORY(pic->pszName);
  1646. }
  1647. if ( pic->pszData ) {
  1648. FREE_MEMORY(pic->pszData);
  1649. }
  1650. if ( pic->pszUrl ) {
  1651. FREE_MEMORY(pic->pszUrl);
  1652. }
  1653. if( pic->pftExpires ) {
  1654. delete pic->pftExpires;
  1655. pic->pftExpires = NULL;
  1656. }
  1657. if (pic->pszP3PPolicy)
  1658. FREE_MEMORY(pic->pszP3PPolicy);
  1659. FREE_MEMORY(pic);
  1660. }
  1661. }
  1662. // allocate's an INTERNET_COOKIE structure
  1663. static INTERNET_COOKIE *
  1664. MakeInternetCookie(
  1665. const char *pchURL,
  1666. char *pchRDomain,
  1667. char *pchPath,
  1668. char *pchName,
  1669. char *pchValue,
  1670. DWORD dwFlags,
  1671. FILETIME ftExpire,
  1672. const char *pchPolicy
  1673. )
  1674. {
  1675. INTERNET_COOKIE *pic = NULL;
  1676. pic = (INTERNET_COOKIE *) ALLOCATE_MEMORY(LMEM_ZEROINIT, sizeof(INTERNET_COOKIE));
  1677. if ( pic == NULL ) {
  1678. return NULL;
  1679. }
  1680. pic->cbSize = sizeof(INTERNET_COOKIE);
  1681. pic->pszDomain = pchRDomain ? NewString(pchRDomain) : NULL;
  1682. if (pic->pszDomain) {
  1683. ReverseString(pic->pszDomain);
  1684. }
  1685. pic->pszPath = pchPath ? NewString(pchPath) : NULL;
  1686. pic->pszName = pchName ? NewString(pchName) : NULL;
  1687. pic->pszData = pchValue ? NewString(pchValue) : NULL;
  1688. pic->pszUrl = pchURL ? NewString(pchURL) : NULL;
  1689. pic->pszP3PPolicy = pchPolicy? NewString(pchPolicy) : NULL;
  1690. #if COOKIE_SECURE != INTERNET_COOKIE_IS_SECURE
  1691. #error MakeInternetCookie depends on cookie flags to remain the same
  1692. #endif
  1693. pic->dwFlags = dwFlags;
  1694. if( dwFlags & COOKIE_SESSION )
  1695. {
  1696. pic->pftExpires = NULL;
  1697. }
  1698. else
  1699. {
  1700. pic->pftExpires = new FILETIME;
  1701. if( pic->pftExpires )
  1702. {
  1703. memcpy(pic->pftExpires, &ftExpire, sizeof(FILETIME));
  1704. }
  1705. }
  1706. return pic;
  1707. }
  1708. DWORD
  1709. GetPromptMask(BOOL fIsSessionCookie, BOOL fIs3rdPartyCookie)
  1710. {
  1711. DWORD dwMask = 0x01; // prompted bit
  1712. if(fIsSessionCookie)
  1713. dwMask |= 0x02;
  1714. if(fIs3rdPartyCookie)
  1715. dwMask |= 0x04;
  1716. return dwMask;
  1717. }
  1718. void
  1719. SetCookiePromptMask(
  1720. LPSTR pchRDomain,
  1721. LPSTR pchPath,
  1722. BOOL fSecure,
  1723. BOOL f3rdParty
  1724. )
  1725. {
  1726. CCookieLocation *pLocation;
  1727. CCookie *pCookie;
  1728. FILETIME ftCurrent;
  1729. GetCurrentGmtTime(&ftCurrent);
  1730. CCookieCriticalSection cs;
  1731. if(s_pJar->SyncWithCacheIfNeeded())
  1732. {
  1733. for (pLocation = *s_pJar->GetBucket(pchRDomain); pLocation; pLocation = pLocation->_pLocationNext)
  1734. {
  1735. if (pLocation->IsMatch(pchRDomain, pchPath))
  1736. {
  1737. pLocation->ReadCacheFileIfNeeded();
  1738. for (pCookie = pLocation->_pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  1739. {
  1740. if (pCookie->CanSend(&ftCurrent, fSecure))
  1741. {
  1742. DWORD dwMask = GetPromptMask(!pCookie->IsPersistent(), f3rdParty);
  1743. DEBUG_PRINT(HTTP, INFO, ("[MASK] SetCookiePromptMask: Domain=%s, pCookie=%#x, dwMask=%#x\n", pchRDomain, pCookie, dwMask));
  1744. pCookie->_dwPromptMask = dwMask;
  1745. }
  1746. }
  1747. }
  1748. }
  1749. }
  1750. }
  1751. BOOL IsCookieIdentical(CCookie *pCookie, char *pchValue, DWORD dwFlags, FILETIME ftExpire)
  1752. {
  1753. //
  1754. // Decide if we need to prompt for a cookie when one already exists. Basic idea is
  1755. // if the cookie is identical, nothing is happening, so we don't need to prompt.
  1756. // Change of value or expiry time (inc. session <-> persistent) means we need to
  1757. // prompt again based on the new cookie type.
  1758. //
  1759. // no existing cookie ==> different
  1760. if(NULL == pCookie)
  1761. {
  1762. return FALSE;
  1763. }
  1764. // if existing cookie has non-empty value and new value is NULL ==> different
  1765. if(NULL == pchValue && pCookie->_pchValue != s_achEmpty)
  1766. {
  1767. return FALSE;
  1768. }
  1769. // different values ==> different (catches new non-empty value and existing empty value)
  1770. if(pchValue && lstrcmp(pCookie->_pchValue, pchValue))
  1771. {
  1772. return FALSE;
  1773. }
  1774. // different flags ==> different
  1775. if(dwFlags != pCookie->_dwFlags)
  1776. {
  1777. return FALSE;
  1778. }
  1779. // if persistant, different expires ==> different
  1780. if(memcmp(&ftExpire, &pCookie->_ftExpire, sizeof(FILETIME)))
  1781. {
  1782. return FALSE;
  1783. }
  1784. return TRUE;
  1785. }
  1786. DWORD
  1787. CCookieJar::CheckCookiePolicy(
  1788. HTTP_REQUEST_HANDLE_OBJECT *pRequest,
  1789. CookieInfo *pInfo,
  1790. DWORD dwOperation
  1791. )
  1792. {
  1793. /* Assumption: policy is checked at time of accepting cookies.
  1794. Existing cookies are sent without prompt after that point */
  1795. if (dwOperation & COOKIE_OP_GET)
  1796. return COOKIE_SUCCESS;
  1797. DWORD dwError;
  1798. DWORD dwCookiesPolicy;
  1799. BOOL fCleanupPcdi = FALSE;
  1800. COOKIE_DLG_INFO *pcdi = NULL, *pcdi_result = NULL;
  1801. SetLastError(ERROR_SUCCESS);
  1802. //
  1803. // Deal first with the basic quick cases to determine if we need UI here.
  1804. // they are:
  1805. /// - Do we allow UI for this given request?
  1806. //
  1807. if (pInfo->pchURL == NULL)
  1808. {
  1809. return COOKIE_FAIL;
  1810. }
  1811. if (pRequest && (pRequest->GetOpenFlags() & INTERNET_FLAG_NO_UI))
  1812. {
  1813. return COOKIE_FAIL;
  1814. }
  1815. //
  1816. // Now look up the cookie and confirm that it hasn't just been added,
  1817. // if its already added to the Cookie list, then we don't show UI,
  1818. // since once the user has chosen to add a given Cookie, we don't repeatly re-prompt
  1819. //
  1820. if(dwOperation & COOKIE_OP_SET)
  1821. {
  1822. CCookieCriticalSection cs;
  1823. CCookieLocation *pLocation;
  1824. if (!SyncWithCacheIfNeeded())
  1825. return COOKIE_FAIL;
  1826. pLocation = GetLocation(pInfo->pchRDomain, pInfo->pchPath, FALSE /* no creation*/);
  1827. if (pLocation)
  1828. {
  1829. CCookie *pCookie;
  1830. pLocation->ReadCacheFileIfNeeded();
  1831. pCookie = pLocation->GetCookie(pInfo->pchName, FALSE /* no creation */);
  1832. if(IsCookieIdentical(pCookie, pInfo->pchValue, pInfo->dwFlags, pInfo->ftExpire))
  1833. {
  1834. return COOKIE_SUCCESS;
  1835. }
  1836. }
  1837. }
  1838. //
  1839. // Now make the async request, to see if we can put up UI
  1840. //
  1841. {
  1842. DWORD dwAction;
  1843. DWORD dwResult;
  1844. DWORD dwDialogToShow;
  1845. LPVOID *ppParams;
  1846. pcdi = new COOKIE_DLG_INFO;
  1847. if(NULL == pcdi)
  1848. {
  1849. return COOKIE_FAIL;
  1850. }
  1851. memset(pcdi, 0, sizeof(*pcdi));
  1852. pcdi->dwOperation = dwOperation;
  1853. fCleanupPcdi = TRUE;
  1854. if(dwOperation & COOKIE_OP_SESSION)
  1855. {
  1856. // make sure flags have session so it shows up right in the UI
  1857. pInfo->dwFlags |= COOKIE_SESSION;
  1858. }
  1859. // create data to pass to dialog
  1860. pcdi->pic = MakeInternetCookie(pInfo->pchURL,
  1861. pInfo->pchRDomain,
  1862. pInfo->pchPath,
  1863. pInfo->pchName,
  1864. pInfo->pchValue,
  1865. pInfo->dwFlags,
  1866. pInfo->ftExpire,
  1867. pInfo->pP3PState ? pInfo->pP3PState->pszP3PHeader : NULL
  1868. );
  1869. if(pcdi->pic == NULL)
  1870. {
  1871. delete pcdi;
  1872. return COOKIE_FAIL;
  1873. }
  1874. pcdi_result = pcdi;
  1875. dwError = ChangeUIBlockingState(
  1876. (HINTERNET) pRequest,
  1877. ERROR_HTTP_COOKIE_NEEDS_CONFIRMATION_EX,
  1878. &dwAction,
  1879. &dwResult,
  1880. (LPVOID *)&pcdi_result
  1881. );
  1882. if(dwError != ERROR_IO_PENDING && dwError != ERROR_SUCCESS)
  1883. {
  1884. goto quit;
  1885. }
  1886. switch (dwAction)
  1887. {
  1888. case UI_ACTION_CODE_NONE_TAKEN:
  1889. {
  1890. // fallback to old behavior
  1891. const int MaxConcurrentDialogs = 10;
  1892. static HANDLE hUIsemaphore = CreateSemaphore(NULL, MaxConcurrentDialogs, MaxConcurrentDialogs, NULL);
  1893. // restrict number of concurrent dialogs
  1894. // NOTE: this is a *temporary* solution for #13393
  1895. // revisit the problem of serializing dialogs when prompting behavior
  1896. // for script is finalized.
  1897. if (WAIT_TIMEOUT==WaitForSingleObject(hUIsemaphore, 0))
  1898. {
  1899. dwError = ERROR_INTERNET_NEED_UI;
  1900. break;
  1901. }
  1902. dwError = ConfirmCookie(NULL, pRequest, pcdi);
  1903. ReleaseSemaphore(hUIsemaphore, 1, NULL);
  1904. // If user requested decision to persist, save prompt result in history
  1905. // "dwStopWarning" may contain 0 (no policy change) or COOKIE_ALLOW_ALL
  1906. // or COOKIE_DONT_ALLOW_ALL
  1907. if(pcdi->dwStopWarning)
  1908. {
  1909. int decision = (pcdi->dwStopWarning == COOKIE_ALLOW_ALL) ?
  1910. COOKIE_STATE_ACCEPT :
  1911. COOKIE_STATE_REJECT;
  1912. ReverseString(pInfo->pchRDomain);
  1913. cookieUIhistory.saveDecision(pInfo->pchRDomain, NULL, decision);
  1914. ReverseString(pInfo->pchRDomain);
  1915. }
  1916. break;
  1917. }
  1918. case UI_ACTION_CODE_USER_ACTION_COMPLETED:
  1919. // If user requested decision to persist, save prompt result in history
  1920. // "dwStopWarning" may contain 0 (no policy change) or COOKIE_ALLOW_ALL
  1921. // or COOKIE_DONT_ALLOW_ALL
  1922. if(pcdi_result->dwStopWarning)
  1923. {
  1924. int decision = (pcdi_result->dwStopWarning == COOKIE_ALLOW_ALL) ?
  1925. COOKIE_STATE_ACCEPT :
  1926. COOKIE_STATE_REJECT;
  1927. ReverseString(pInfo->pchRDomain);
  1928. cookieUIhistory.saveDecision(pInfo->pchRDomain, NULL, decision);
  1929. ReverseString(pInfo->pchRDomain);
  1930. }
  1931. if (pcdi != pcdi_result)
  1932. {
  1933. // got an old pcdi back, clean it
  1934. if(pcdi_result->pic)
  1935. {
  1936. DestroyInternetCookie(pcdi_result->pic);
  1937. }
  1938. delete pcdi_result;
  1939. }
  1940. // make sure we clean current, too
  1941. INET_ASSERT(fCleanupPcdi);
  1942. dwError = dwResult;
  1943. break;
  1944. case UI_ACTION_CODE_BLOCKED_FOR_USER_INPUT:
  1945. //
  1946. // Go pending while we wait for the UI to be shown
  1947. //
  1948. INET_ASSERT(pcdi == pcdi_result);
  1949. fCleanupPcdi = FALSE; // the UI needs this info, don't delete
  1950. // fall through ...
  1951. case UI_ACTION_CODE_BLOCKED_FOR_INTERNET_HANDLE:
  1952. INET_ASSERT(dwError == ERROR_IO_PENDING);
  1953. break;
  1954. }
  1955. }
  1956. quit:
  1957. if ( fCleanupPcdi )
  1958. {
  1959. if(pcdi->pic)
  1960. {
  1961. DestroyInternetCookie(pcdi->pic);
  1962. }
  1963. delete pcdi;
  1964. }
  1965. SetLastError(dwError);
  1966. if (dwError != ERROR_SUCCESS)
  1967. {
  1968. if ( dwError == ERROR_IO_PENDING ) {
  1969. return COOKIE_PENDING;
  1970. } else {
  1971. return COOKIE_FAIL;
  1972. }
  1973. }
  1974. else
  1975. {
  1976. return COOKIE_SUCCESS;
  1977. }
  1978. }
  1979. DWORD
  1980. CCookieJar::SetCookie(HTTP_REQUEST_HANDLE_OBJECT *pRequest, const char *pchURL, char *pchHeader,
  1981. DWORD &dwFlags, P3PCookieState *pState = NULL, LPDWORD pdwAction = NULL)
  1982. {
  1983. FILETIME ftExpire;
  1984. FILETIME ftCurrent;
  1985. char *pchName;
  1986. char *pchValue;
  1987. char *pchHeaderPath;
  1988. char *pchHeaderRDomain;
  1989. char *pchDocumentRDomain = NULL;
  1990. char *pchDocumentPath = NULL;
  1991. DWORD dwFlagsFromParse;
  1992. BOOL fDocumentSecure;
  1993. BOOL fDelete;
  1994. DWORD dwRet = COOKIE_FAIL;
  1995. BOOL fWriteToCacheFileNeeded;
  1996. CCookieLocation *pLocation;
  1997. DWORD dwOperation = COOKIE_OP_SET;
  1998. DWORD dwReqAction = COOKIE_STATE_UNKNOWN;
  1999. ParseHeader(pchHeader, &pchName, &pchValue, &pchHeaderPath, &pchHeaderRDomain, &dwFlagsFromParse, &ftExpire);
  2000. // merge flags given with those found by the parser.
  2001. dwFlags |= dwFlagsFromParse;
  2002. if (!PathAndRDomainFromURL(pchURL, &pchDocumentRDomain, &pchDocumentPath, &fDocumentSecure))
  2003. goto Cleanup;
  2004. //
  2005. // Verify domain and path
  2006. //
  2007. if ((pchHeaderRDomain && !IsDomainLegal(pchHeaderRDomain, pchDocumentRDomain)) ||
  2008. (pchHeaderPath && !IsPathLegal(pchHeaderPath, pchDocumentPath)))
  2009. {
  2010. SetLastError(ERROR_INVALID_PARAMETER);
  2011. goto Cleanup;
  2012. }
  2013. // Remove control-characters and other non-ASCII symbols
  2014. replaceControlChars(pchName);
  2015. replaceControlChars(pchValue);
  2016. replaceControlChars(pchHeaderPath);
  2017. replaceControlChars(pchHeaderRDomain);
  2018. if (!pchHeaderRDomain)
  2019. pchHeaderRDomain = pchDocumentRDomain;
  2020. if (!pchHeaderPath)
  2021. pchHeaderPath = pchDocumentPath;
  2022. // We need to discard any extra info (i.e. query strings and fragments)
  2023. // from the url.
  2024. if (pchHeaderPath)
  2025. {
  2026. PTSTR psz = pchHeaderPath;
  2027. while (*psz)
  2028. {
  2029. if (*psz==TEXT('?') || *psz==TEXT('#'))
  2030. {
  2031. *psz = TEXT('\0');
  2032. break;
  2033. }
  2034. psz++;
  2035. }
  2036. }
  2037. //
  2038. // Delete the cookie?
  2039. //
  2040. GetCurrentGmtTime(&ftCurrent);
  2041. fDelete = CompareFileTime(ftCurrent, ftExpire) > 0;
  2042. // get 3rd part flag
  2043. BOOL f3rdParty = (pRequest && pRequest->Is3rdPartyCookies()) ||
  2044. (dwFlags & INTERNET_COOKIE_THIRD_PARTY);
  2045. if (f3rdParty)
  2046. {
  2047. dwOperation |= COOKIE_OP_3RD_PARTY;
  2048. }
  2049. // check session vs. persistent
  2050. if(dwFlagsFromParse & COOKIE_SESSION)
  2051. {
  2052. dwOperation |= COOKIE_OP_SESSION;
  2053. }
  2054. else
  2055. {
  2056. dwOperation |= COOKIE_OP_PERSISTENT;
  2057. }
  2058. BOOL fSessionCookie = dwFlags & COOKIE_SESSION;
  2059. /* DELETE operations are not subject to P3P, except for leashed cookies */
  2060. BOOL fP3PApplies = !fDelete && pState && (!fSessionCookie || pState->fIncSession);
  2061. /* Check for the "anything-goes" mode */
  2062. BOOL fAllowAll = pState && (pState->dwEvalMode==URLPOLICY_ALLOW);
  2063. /* if cookie operations are disabled, fail the operation */
  2064. if (pState && pState->dwEvalMode==URLPOLICY_DISALLOW)
  2065. dwReqAction = COOKIE_STATE_REJECT;
  2066. else if (fP3PApplies && !fAllowAll) {
  2067. /* Since downgrading a session cookie is a NOOP,
  2068. report the action as ACCEPT in that case */
  2069. if (fSessionCookie && pState->dwPolicyState==COOKIE_STATE_DOWNGRADE)
  2070. dwReqAction = COOKIE_STATE_ACCEPT;
  2071. dwFlags |= getImpliedCookieFlags(pState);
  2072. dwReqAction = pState->dwPolicyState;
  2073. }
  2074. else
  2075. dwReqAction = COOKIE_STATE_ACCEPT;
  2076. // If prompt is required, show UI
  2077. if((dwFlags & INTERNET_COOKIE_PROMPT_REQUIRED) ||
  2078. dwReqAction==COOKIE_STATE_PROMPT)
  2079. {
  2080. CookieInfo ckInfo =
  2081. {
  2082. pchURL,
  2083. pchHeaderRDomain, pchHeaderPath,
  2084. pchName, pchValue,
  2085. dwFlags
  2086. };
  2087. ckInfo.ftExpire = ftExpire;
  2088. ckInfo.pP3PState = pState;
  2089. dwRet = CheckCookiePolicy(pRequest, &ckInfo, dwOperation);
  2090. if (dwRet != COOKIE_SUCCESS) {
  2091. dwReqAction = COOKIE_STATE_REJECT;
  2092. goto Cleanup;
  2093. }
  2094. else
  2095. dwReqAction = COOKIE_STATE_ACCEPT;
  2096. }
  2097. if (dwReqAction==COOKIE_STATE_REJECT) {
  2098. dwRet = COOKIE_FAIL;
  2099. goto Cleanup;
  2100. }
  2101. //
  2102. // Finally, we can add the cookie!
  2103. //
  2104. {
  2105. CCookieCriticalSection cs;
  2106. if (!SyncWithCacheIfNeeded())
  2107. goto Cleanup;
  2108. pLocation = GetLocation(pchHeaderRDomain, pchHeaderPath, !fDelete);
  2109. if (pLocation)
  2110. {
  2111. pLocation->ReadCacheFileIfNeeded();
  2112. fWriteToCacheFileNeeded = FALSE;
  2113. if (fDelete)
  2114. {
  2115. CCookie *pCookie = pLocation->GetCookie(pchName, FALSE);
  2116. // If the cookie we are attempting to delete does not exist,
  2117. // return success code
  2118. if (!pCookie) {
  2119. dwRet = COOKIE_SUCCESS;
  2120. goto Cleanup;
  2121. }
  2122. /* Leashed cookies cannot be deleted from 3rd party context
  2123. unless P3P is disabled completely eg when "fAllowAll" is true.
  2124. EXCEPTION: allow *legacy* leashed cookies to be deleted from 3rd party */
  2125. if (pCookie->IsRestricted()
  2126. && f3rdParty
  2127. && !(fAllowAll || pCookie->IsLegacy()))
  2128. {
  2129. dwReqAction = COOKIE_STATE_REJECT;
  2130. goto Cleanup;
  2131. }
  2132. fWriteToCacheFileNeeded |= pLocation->Purge(CCookie::PurgeByName, pchName);
  2133. }
  2134. else
  2135. {
  2136. CCookie *pCookie;
  2137. EnforceCookieLimits(pLocation, pchName, &fWriteToCacheFileNeeded);
  2138. pCookie = pLocation->GetCookie(pchName, TRUE);
  2139. if (!pCookie)
  2140. goto Cleanup;
  2141. pCookie->_ftLastModified = ftCurrent;
  2142. if (memcmp(&ftExpire, &pCookie->_ftExpire, sizeof(FILETIME)) ||
  2143. strcmp(pchValue, pCookie->_pchValue) ||
  2144. dwFlags != pCookie->_dwFlags)
  2145. {
  2146. fWriteToCacheFileNeeded |= pCookie->IsPersistent();
  2147. pCookie->_ftExpire = ftExpire;
  2148. pCookie->_dwFlags = dwFlags;
  2149. pCookie->SetValue(pchValue);
  2150. pCookie->_dwPromptMask = GetPromptMask(dwOperation & COOKIE_OP_SESSION, dwOperation & COOKIE_OP_3RD_PARTY);
  2151. DEBUG_PRINT(HTTP, INFO, ("[MASK] SetCookie: Domain=%s, Updating cookie mask, pCookie=%#x, new mask=%#x\n", pchHeaderRDomain, pCookie, pCookie->_dwPromptMask));
  2152. fWriteToCacheFileNeeded |= pCookie->IsPersistent();
  2153. }
  2154. }
  2155. if (fWriteToCacheFileNeeded)
  2156. {
  2157. if (!pLocation->WriteCacheFile())
  2158. goto Cleanup;
  2159. }
  2160. }
  2161. }
  2162. dwRet = COOKIE_SUCCESS;
  2163. Cleanup:
  2164. if (pchDocumentRDomain)
  2165. FREE_MEMORY(pchDocumentRDomain);
  2166. if (pchDocumentPath)
  2167. FREE_MEMORY(pchDocumentPath);
  2168. if (pdwAction)
  2169. *pdwAction = dwReqAction;
  2170. return dwRet;
  2171. }
  2172. void
  2173. CCookieJar::EnforceCookieLimits(CCookieLocation *pLocationNew, char *pchNameNew, BOOL *fWriteToCacheFileNeeded)
  2174. {
  2175. CCookieLocation *pLocation;
  2176. CCookieLocation *pLocationVictim;
  2177. CCookie *pCookie;
  2178. CCookie *pCookieVictim = NULL;
  2179. int nCookie = 0;
  2180. for (pLocation = *GetBucket(pLocationNew->_pchRDomain); pLocation; pLocation = pLocation->_pLocationNext)
  2181. {
  2182. // Same domain?
  2183. if (stricmp(pLocationNew->_pchRDomain, pLocation->_pchRDomain) == 0)
  2184. {
  2185. pLocation->ReadCacheFileIfNeeded();
  2186. for (pCookie = pLocation->_pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  2187. {
  2188. nCookie += 1;
  2189. if (pLocation == pLocationNew && strcmp(pCookie->_pchName, pchNameNew) == 0)
  2190. {
  2191. // No need to enforce limits when resetting existing cookie value.
  2192. return;
  2193. }
  2194. if (!pCookieVictim ||
  2195. CompareFileTime(pCookie->_ftLastModified, pCookieVictim->_ftLastModified) < 0)
  2196. {
  2197. pCookieVictim = pCookie;
  2198. pLocationVictim = pLocation;
  2199. }
  2200. }
  2201. }
  2202. }
  2203. if (nCookie >= 20)
  2204. {
  2205. INET_ASSERT(pCookieVictim != NULL && pLocationVictim != NULL);
  2206. if (pLocationVictim->Purge(CCookie::PurgeThis, pCookieVictim))
  2207. {
  2208. pLocationVictim->WriteCacheFile();
  2209. }
  2210. }
  2211. }
  2212. //---------------------------------------------------------------------------
  2213. //
  2214. // External APIs
  2215. //
  2216. //---------------------------------------------------------------------------
  2217. BOOL
  2218. OpenTheCookieJar()
  2219. {
  2220. if (s_pJar)
  2221. return TRUE;
  2222. s_pJar = CCookieJar::Construct();
  2223. if (!s_pJar)
  2224. return FALSE;
  2225. InitializeCriticalSection(&s_csCookieJar);
  2226. return TRUE;
  2227. }
  2228. void
  2229. CloseTheCookieJar()
  2230. {
  2231. if (s_pJar)
  2232. {
  2233. DeleteCriticalSection(&s_csCookieJar);
  2234. delete s_pJar;
  2235. }
  2236. s_fFirstTime = TRUE;
  2237. s_pJar = NULL;
  2238. }
  2239. void
  2240. PurgeCookieJarOfStaleCookies()
  2241. {
  2242. FILETIME ftCurrent;
  2243. if (s_pJar)
  2244. {
  2245. CCookieCriticalSection cs;
  2246. GetCurrentGmtTime(&ftCurrent);
  2247. s_pJar->Purge(&ftCurrent, TRUE);
  2248. }
  2249. }
  2250. INTERNETAPI_(BOOL) InternetGetCookieW(
  2251. LPCWSTR lpszUrl,
  2252. LPCWSTR lpszCookieName,
  2253. LPWSTR lpszCookieData,
  2254. LPDWORD lpdwSize
  2255. )
  2256. {
  2257. DEBUG_ENTER_API((DBG_INET,
  2258. Bool,
  2259. "InternetGetCookieW",
  2260. "%wq, %#x, %#x, %#x",
  2261. lpszUrl,
  2262. lpszCookieName,
  2263. lpszCookieData,
  2264. lpdwSize
  2265. ));
  2266. DWORD dwErr = ERROR_SUCCESS;
  2267. BOOL fResult = FALSE;
  2268. MEMORYPACKET mpUrl, mpCookieName, mpCookieData;
  2269. ALLOC_MB(lpszUrl,0,mpUrl);
  2270. if (!mpUrl.psStr)
  2271. {
  2272. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2273. goto cleanup;
  2274. }
  2275. UNICODE_TO_ANSI(lpszUrl,mpUrl);
  2276. if (lpszCookieName)
  2277. {
  2278. ALLOC_MB(lpszCookieName,0,mpCookieName);
  2279. if (!mpCookieName.psStr)
  2280. {
  2281. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2282. goto cleanup;
  2283. }
  2284. UNICODE_TO_ANSI(lpszCookieName,mpCookieName);
  2285. }
  2286. if (lpszCookieData)
  2287. {
  2288. mpCookieData.dwAlloc = mpCookieData.dwSize = *lpdwSize;
  2289. mpCookieData.psStr = (LPSTR)ALLOC_BYTES(*lpdwSize);
  2290. if (!mpCookieData.psStr)
  2291. {
  2292. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2293. goto cleanup;
  2294. }
  2295. }
  2296. fResult = InternetGetCookieA(mpUrl.psStr, mpCookieName.psStr, mpCookieData.psStr, &mpCookieData.dwSize);
  2297. *lpdwSize = mpCookieData.dwSize*sizeof(WCHAR);
  2298. if (lpszCookieData)
  2299. {
  2300. if (mpCookieData.dwSize <= mpCookieData.dwAlloc)
  2301. {
  2302. //Bug 2110: InternetGetCookieA already considered '\0' at the end of URL. MAYBE_COPY_ANSI does it again.
  2303. //We don't want to change MAYBE_COPY_ANSI, so we mpCookieData.dwSize -= 1 here. Otherwise we will overflow the heap
  2304. mpCookieData.dwSize -= 1;
  2305. MAYBE_COPY_ANSI(mpCookieData,lpszCookieData,*lpdwSize);
  2306. }
  2307. else
  2308. {
  2309. dwErr = ERROR_INSUFFICIENT_BUFFER;
  2310. fResult = FALSE;
  2311. }
  2312. }
  2313. cleanup:
  2314. if (dwErr!=ERROR_SUCCESS)
  2315. {
  2316. SetLastError(dwErr);
  2317. DEBUG_ERROR(INET, dwErr);
  2318. }
  2319. DEBUG_LEAVE_API(fResult);
  2320. return fResult;
  2321. }
  2322. void convertLegacyCookie(CCookie *pCookie, CCookieLocation *pLocation) {
  2323. const char* gasz_OptOutName[] = {"ID", "AA002", "id", "CyberGlobalAnonymous"};
  2324. const char* gasz_OptOutValue[] = {"OPT_OUT", "optout", "OPT_OUT", "optout"};
  2325. if (GlobalLeashLegacyCookies)
  2326. pCookie->_dwFlags |= INTERNET_COOKIE_IS_RESTRICTED;
  2327. /* special-case opt-out cookies-- these will never get leashed */
  2328. for( int i = 0;
  2329. i < sizeof( gasz_OptOutName)/sizeof(gasz_OptOutName[0]);
  2330. i++)
  2331. {
  2332. if (!strcmp(pCookie->_pchName, gasz_OptOutName[i])
  2333. && !strcmp(pCookie->_pchValue, gasz_OptOutValue[i]))
  2334. {
  2335. pCookie->_dwFlags &= ~INTERNET_COOKIE_IS_RESTRICTED;
  2336. break;
  2337. }
  2338. }
  2339. // Legacy cookies are special-cased for one time only
  2340. // After that they are subject to P3P.
  2341. pCookie->_dwFlags |= INTERNET_COOKIE_IE6;
  2342. /* we need to remember which cookies are genuine IE6 vs. upgraded legacy... */
  2343. pCookie->_dwFlags |= INTERNET_COOKIE_IS_LEGACY;
  2344. pLocation->WriteCacheFile();
  2345. }
  2346. //
  2347. // InternetGetCookieEx only returns those cookies within domain pchURL
  2348. //with a name that maches pchCookieName
  2349. //
  2350. INTERNETAPI_(BOOL) InternetGetCookieEx(
  2351. IN LPCSTR pchURL,
  2352. IN LPCSTR pchCookieName OPTIONAL,
  2353. IN LPSTR pchCookieData OPTIONAL,
  2354. IN OUT LPDWORD pcchCookieData,
  2355. IN DWORD dwFlags,
  2356. IN LPVOID lpReserved)
  2357. {
  2358. DEBUG_ENTER_API((DBG_INET,
  2359. Bool,
  2360. "InternetGetCookieA",
  2361. "%q, %#x, %#x, %#x",
  2362. pchURL,
  2363. pchCookieName,
  2364. pchCookieData,
  2365. pcchCookieData
  2366. ));
  2367. // force everyone to not give anything in lpReserved
  2368. INET_ASSERT( lpReserved == NULL);
  2369. if( lpReserved != NULL)
  2370. {
  2371. DEBUG_LEAVE_API(FALSE);
  2372. SetLastError(ERROR_INVALID_PARAMETER);
  2373. return FALSE;
  2374. }
  2375. /*
  2376. // force everyone to not give anything in dwFlags
  2377. INET_ASSERT( dwFlags == 0);
  2378. if( dwFlags != 0)
  2379. {
  2380. DEBUG_LEAVE_API(FALSE);
  2381. SetLastError(ERROR_INVALID_PARAMETER);
  2382. return FALSE;
  2383. }
  2384. */
  2385. BOOL fSuccess = FALSE;
  2386. char * pchRDomain = NULL;
  2387. char * pchPath = NULL;
  2388. BOOL fSecure;
  2389. DWORD cch = 0;
  2390. BOOL fFirst;
  2391. int cchName;
  2392. int cchValue;
  2393. FILETIME ftCurrent;
  2394. CCookieLocation *pLocation;
  2395. CCookie *pCookie;
  2396. DWORD dwErr = ERROR_SUCCESS;
  2397. if (!pcchCookieData || !pchURL)
  2398. {
  2399. dwErr = ERROR_INVALID_PARAMETER;
  2400. goto done;
  2401. }
  2402. if (!GlobalDataInitialized) {
  2403. dwErr = GlobalDataInitialize();
  2404. if (dwErr!= ERROR_SUCCESS) {
  2405. goto done;
  2406. }
  2407. }
  2408. // NOTE THIS SEEMS TO BE A BUG BUG BUG
  2409. if (!PathAndRDomainFromURL(pchURL, &pchRDomain, &pchPath, &fSecure))
  2410. goto Cleanup;
  2411. DWORD dwMainSwitch = (dwFlags & INTERNET_FLAG_RESTRICTED_ZONE) ?
  2412. GetCookieMainSwitch(URLZONE_UNTRUSTED) :
  2413. GetCookieMainSwitch(pchURL);
  2414. fFirst = TRUE;
  2415. GetCurrentGmtTime(&ftCurrent);
  2416. {
  2417. CCookieCriticalSection cs;
  2418. if (!s_pJar->SyncWithCacheIfNeeded())
  2419. goto Cleanup;
  2420. for (pLocation = *s_pJar->GetBucket(pchRDomain); pLocation; pLocation = pLocation->_pLocationNext)
  2421. {
  2422. if (pLocation->IsMatch(pchRDomain, pchPath))
  2423. {
  2424. pLocation->ReadCacheFileIfNeeded();
  2425. for (pCookie = pLocation->_pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  2426. {
  2427. if (IsLegacyCookie(pCookie))
  2428. convertLegacyCookie(pCookie, pLocation);
  2429. BOOL fAllow;
  2430. if (dwMainSwitch==URLPOLICY_ALLOW) /* replay all cookies-- even leashed ones */
  2431. fAllow = TRUE;
  2432. else if (dwMainSwitch==URLPOLICY_DISALLOW) /* suppress everything */
  2433. fAllow = FALSE;
  2434. else
  2435. {
  2436. /* default behavior: replay the cookie, provided its not leashed
  2437. or we are in 1st party context */
  2438. fAllow = !pCookie->IsRestricted() ||
  2439. (dwFlags & INTERNET_COOKIE_THIRD_PARTY) == 0;
  2440. }
  2441. BOOL fNonScriptable = (pCookie->_dwFlags & COOKIE_NONSCRIPTABLE);
  2442. if (fAllow
  2443. && !fNonScriptable // Check for non-scriptable cookies
  2444. && pCookie->CanSend(&ftCurrent, fSecure)
  2445. && (pchCookieName == NULL
  2446. || StrCmp( pCookie->_pchName, pchCookieName) == 0))
  2447. {
  2448. if (!fFirst) cch += 2; // for ; <space>
  2449. cch += cchName = strlen(pCookie->_pchName);
  2450. cch += cchValue = strlen(pCookie->_pchValue);
  2451. if (cchName && cchValue) cch += 1; // for equal sign
  2452. if (pchCookieData && cch < *pcchCookieData)
  2453. {
  2454. if (!fFirst)
  2455. {
  2456. *pchCookieData++ = ';';
  2457. *pchCookieData++ = ' ';
  2458. }
  2459. if (cchName > 0)
  2460. {
  2461. memcpy(pchCookieData, pCookie->_pchName, cchName);
  2462. pchCookieData += cchName;
  2463. if (cchValue > 0)
  2464. {
  2465. *pchCookieData++ = '=';
  2466. }
  2467. }
  2468. if (cchValue > 0)
  2469. {
  2470. memcpy(pchCookieData, pCookie->_pchValue, cchValue);
  2471. pchCookieData += cchValue;
  2472. }
  2473. }
  2474. fFirst = FALSE;
  2475. }
  2476. }
  2477. }
  2478. }
  2479. }
  2480. //TerminateBuffer:
  2481. cch += 1;
  2482. if (pchCookieData)
  2483. {
  2484. if (cch > *pcchCookieData)
  2485. {
  2486. dwErr = ERROR_INSUFFICIENT_BUFFER;
  2487. }
  2488. else
  2489. {
  2490. *pchCookieData = 0;
  2491. fSuccess = TRUE;
  2492. }
  2493. }
  2494. else
  2495. {
  2496. fSuccess = TRUE;
  2497. }
  2498. if (cch == 1)
  2499. {
  2500. dwErr = ERROR_NO_MORE_ITEMS;
  2501. fSuccess = FALSE;
  2502. cch = 0;
  2503. }
  2504. *pcchCookieData = cch;
  2505. Cleanup:
  2506. if (pchRDomain)
  2507. FREE_MEMORY(pchRDomain);
  2508. if (pchPath)
  2509. FREE_MEMORY(pchPath);
  2510. done:
  2511. if (dwErr!=ERROR_SUCCESS)
  2512. {
  2513. SetLastError(dwErr);
  2514. DEBUG_ERROR(INET, dwErr);
  2515. }
  2516. DEBUG_LEAVE_API(fSuccess);
  2517. return fSuccess;
  2518. }
  2519. /*
  2520. UNICODE version for InternetGetCookieEx
  2521. Difference from the standard InternetGetCookie* function is
  2522. addition of two parameters.
  2523. Supported flags: third-party, prompt-required.
  2524. */
  2525. INTERNETAPI_(BOOL) InternetGetCookieExW(
  2526. IN LPCWSTR lpszUrl,
  2527. IN LPCWSTR lpszCookieName OPTIONAL,
  2528. IN LPWSTR lpszCookieData OPTIONAL,
  2529. IN OUT LPDWORD lpdwSize,
  2530. IN DWORD dwFlags,
  2531. IN LPVOID lpReserved)
  2532. {
  2533. DEBUG_ENTER_API((DBG_INET,
  2534. Bool,
  2535. "InternetGetCookieExW",
  2536. "%wq, %#x, %#x, %#x",
  2537. lpszUrl,
  2538. lpszCookieName,
  2539. lpszCookieData,
  2540. lpdwSize
  2541. ));
  2542. DWORD dwErr = ERROR_SUCCESS;
  2543. BOOL fResult = FALSE;
  2544. MEMORYPACKET mpUrl, mpCookieName, mpCookieData;
  2545. ALLOC_MB(lpszUrl,0,mpUrl);
  2546. if (!mpUrl.psStr)
  2547. {
  2548. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2549. goto cleanup;
  2550. }
  2551. UNICODE_TO_ANSI(lpszUrl,mpUrl);
  2552. if (lpszCookieName)
  2553. {
  2554. ALLOC_MB(lpszCookieName,0,mpCookieName);
  2555. if (!mpCookieName.psStr)
  2556. {
  2557. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2558. goto cleanup;
  2559. }
  2560. UNICODE_TO_ANSI(lpszCookieName,mpCookieName);
  2561. }
  2562. if (lpszCookieData)
  2563. {
  2564. mpCookieData.dwAlloc = mpCookieData.dwSize = *lpdwSize;
  2565. mpCookieData.psStr = (LPSTR)ALLOC_BYTES(*lpdwSize);
  2566. if (!mpCookieData.psStr)
  2567. {
  2568. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2569. goto cleanup;
  2570. }
  2571. }
  2572. fResult = InternetGetCookieExA(mpUrl.psStr, mpCookieName.psStr, mpCookieData.psStr, &mpCookieData.dwSize, dwFlags, lpReserved);
  2573. *lpdwSize = mpCookieData.dwSize*sizeof(WCHAR);
  2574. if (lpszCookieData)
  2575. {
  2576. if (mpCookieData.dwSize <= mpCookieData.dwAlloc)
  2577. {
  2578. MAYBE_COPY_ANSI(mpCookieData,lpszCookieData,*lpdwSize);
  2579. }
  2580. else
  2581. {
  2582. dwErr = ERROR_INSUFFICIENT_BUFFER;
  2583. fResult = FALSE;
  2584. }
  2585. }
  2586. cleanup:
  2587. if (dwErr!=ERROR_SUCCESS)
  2588. {
  2589. SetLastError(dwErr);
  2590. DEBUG_ERROR(INET, dwErr);
  2591. }
  2592. DEBUG_LEAVE_API(fResult);
  2593. return fResult;
  2594. return FALSE;
  2595. }
  2596. INTERNETAPI_(BOOL) InternetGetCookieA(
  2597. IN LPCSTR pchURL,
  2598. IN LPCSTR pchCookieName OPTIONAL,
  2599. IN LPSTR pchCookieData OPTIONAL,
  2600. IN OUT LPDWORD pcchCookieData
  2601. )
  2602. {
  2603. // Because the value in pchCookieName had no effect on
  2604. //the previously exported API, Ex gets NULL to ensure
  2605. //the behavior doesn't change.
  2606. return InternetGetCookieEx( pchURL, NULL, pchCookieData,
  2607. pcchCookieData, 0, NULL);
  2608. }
  2609. INTERNETAPI_(BOOL) InternetSetCookieW(
  2610. LPCWSTR lpszUrl,
  2611. LPCWSTR lpszCookieName,
  2612. LPCWSTR lpszCookieData)
  2613. {
  2614. DEBUG_ENTER_API((DBG_INET,
  2615. Bool,
  2616. "InternetSetCookieW",
  2617. "%wq, %#x, %#x",
  2618. lpszUrl,
  2619. lpszCookieName,
  2620. lpszCookieData
  2621. ));
  2622. DWORD dwErr = ERROR_SUCCESS;
  2623. BOOL fResult = FALSE;
  2624. MEMORYPACKET mpUrl, mpCookieName, mpCookieData;
  2625. if (lpszUrl)
  2626. {
  2627. ALLOC_MB(lpszUrl,0,mpUrl);
  2628. if (!mpUrl.psStr)
  2629. {
  2630. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2631. goto cleanup;
  2632. }
  2633. UNICODE_TO_ANSI(lpszUrl,mpUrl);
  2634. }
  2635. if (lpszCookieName)
  2636. {
  2637. ALLOC_MB(lpszCookieName,0,mpCookieName);
  2638. if (!mpCookieName.psStr)
  2639. {
  2640. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2641. goto cleanup;
  2642. }
  2643. UNICODE_TO_ANSI(lpszCookieName,mpCookieName);
  2644. }
  2645. if (lpszCookieData)
  2646. {
  2647. ALLOC_MB(lpszCookieData,0,mpCookieData);
  2648. if (!mpCookieData.psStr)
  2649. {
  2650. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2651. goto cleanup;
  2652. }
  2653. UNICODE_TO_ANSI(lpszCookieData,mpCookieData);
  2654. }
  2655. fResult = InternetSetCookieA(mpUrl.psStr, mpCookieName.psStr, mpCookieData.psStr);
  2656. cleanup:
  2657. if (dwErr!=ERROR_SUCCESS)
  2658. {
  2659. SetLastError(dwErr);
  2660. DEBUG_ERROR(INET, dwErr);
  2661. }
  2662. DEBUG_LEAVE_API(fResult);
  2663. return fResult;
  2664. }
  2665. BOOL InternalInternetSetCookie(
  2666. LPCSTR pchURL,
  2667. LPCSTR pchCookieName,
  2668. LPCSTR pchCookieData,
  2669. DWORD dwFlags,
  2670. LPVOID lpReserved
  2671. )
  2672. {
  2673. DEBUG_ENTER_API((DBG_INET,
  2674. Bool,
  2675. "InternetSetCookieA",
  2676. "%q, %#x, %#x",
  2677. pchURL,
  2678. pchCookieName,
  2679. pchCookieData
  2680. ));
  2681. char * pch = NULL;
  2682. char * pchStart = NULL;
  2683. int cch;
  2684. int cchT;
  2685. DWORD dwErr = ERROR_SUCCESS;
  2686. BOOL fResult = FALSE;
  2687. P3PCookieState CS;
  2688. DWORD FlagsWithParam = INTERNET_COOKIE_EVALUATE_P3P |
  2689. INTERNET_COOKIE_APPLY_P3P;
  2690. BOOL fPolicy = (dwFlags & INTERNET_COOKIE_EVALUATE_P3P);
  2691. BOOL fDecision = (dwFlags & INTERNET_COOKIE_APPLY_P3P);
  2692. if (!pchURL || !pchCookieData || (fPolicy && fDecision))
  2693. {
  2694. fResult = FALSE;
  2695. dwErr = ERROR_INVALID_PARAMETER;
  2696. goto done;
  2697. }
  2698. if (!GlobalDataInitialized) {
  2699. dwErr = GlobalDataInitialize();
  2700. if (dwErr!= ERROR_SUCCESS) {
  2701. fResult = FALSE;
  2702. goto done;
  2703. }
  2704. }
  2705. pch = (char *) ALLOCATE_FIXED_MEMORY(CCH_COOKIE_MAX);
  2706. if (pch == NULL)
  2707. {
  2708. fResult = FALSE;
  2709. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  2710. goto done;
  2711. }
  2712. pchStart = pch;
  2713. /* The reserved parameter is used for passing in P3P header or decision */
  2714. if (fPolicy) {
  2715. CS.pszP3PHeader = (char*) lpReserved;
  2716. EvaluateCookiePolicy(pchURL,
  2717. dwFlags & INTERNET_COOKIE_THIRD_PARTY,
  2718. dwFlags & INTERNET_FLAG_RESTRICTED_ZONE,
  2719. &CS);
  2720. }
  2721. else if (fDecision) {
  2722. CookieDecision *pDecision = (CookieDecision*) lpReserved;
  2723. CS.fEvaluated = TRUE;
  2724. CS.dwPolicyState = pDecision->dwCookieState;
  2725. CS.fIncSession = ! pDecision->fAllowSession;
  2726. CS.dwEvalMode = GetCookieMainSwitch(pchURL);
  2727. }
  2728. cch = CCH_COOKIE_MAX - 2; // one for null terminator, one for "="
  2729. if (pchCookieName)
  2730. {
  2731. cchT = strlen(pchCookieName);
  2732. if (cchT > cch)
  2733. cchT = cch;
  2734. memcpy(pch, pchCookieName, cchT);
  2735. pch += cchT;
  2736. cch -= cchT;
  2737. memcpy(pch, "=", 1);
  2738. pch += 1;
  2739. cch -= 1;
  2740. }
  2741. // Ensure null termination upon overflow.
  2742. if (cch <= 0)
  2743. cch = 1;
  2744. // Append the cookie data.
  2745. lstrcpyn (pch, pchCookieData, cch);
  2746. // All IE6 cookies are marked with this flag to distinguish
  2747. // from legacy cookies inherited from past versions.
  2748. if (fPolicy || fDecision)
  2749. dwFlags |= INTERNET_COOKIE_IE6;
  2750. DWORD dwAction = 0;
  2751. if(s_pJar->SetCookie(NULL, pchURL, pchStart, dwFlags,
  2752. (fPolicy||fDecision) ? &CS : NULL,
  2753. &dwAction) == COOKIE_FAIL)
  2754. {
  2755. if( dwAction == COOKIE_STATE_REJECT)
  2756. fResult = COOKIE_STATE_REJECT;
  2757. else
  2758. fResult = FALSE;
  2759. }
  2760. else
  2761. {
  2762. /* Return the action taken (accept, downgrade, etc.) */
  2763. fResult = dwAction;
  2764. }
  2765. done:
  2766. if (dwErr!=ERROR_SUCCESS)
  2767. {
  2768. SetLastError(dwErr);
  2769. DEBUG_ERROR(INET, dwErr);
  2770. }
  2771. if (pchStart)
  2772. FREE_MEMORY(pchStart);
  2773. DEBUG_LEAVE_API(fResult);
  2774. return fResult;
  2775. }
  2776. INTERNETAPI_(BOOL) InternetSetCookieA(
  2777. LPCSTR pchURL,
  2778. LPCSTR pchCookieName,
  2779. LPCSTR pchCookieData
  2780. )
  2781. {
  2782. DWORD dwResult = InternalInternetSetCookie( pchURL, pchCookieName, pchCookieData, 0, NULL);
  2783. // For IE6 InternalInternetSetCookie returns the action taken.
  2784. // When the API fails or cookie is rejected, that would be REJECT which is a positive value.
  2785. // Convert this to FALSE to retain semantics compatible with IE5.5
  2786. return (dwResult==COOKIE_STATE_REJECT) ? FALSE : dwResult;
  2787. }
  2788. BOOL seekPolicyRef(const char *pszP3PHeader, char **pszPolicyRef, LPDWORD pdwLength) {
  2789. static const char gszPolicyRefField[] = "policyref";
  2790. *pszPolicyRef = FindNamedValue((char*)pszP3PHeader, gszPolicyRefField, pdwLength);
  2791. return (*pszPolicyRef != NULL);
  2792. }
  2793. DWORD extractP3PHeader(HTTP_REQUEST_HANDLE_OBJECT *pRequest, char *pszHeader, DWORD *pdwHeaderSize)
  2794. {
  2795. const char gszPolicyHeaderName[] = "P3P";
  2796. const int gszHeaderSize = sizeof(gszPolicyHeaderName)-1;
  2797. DWORD dwIndex = 0;
  2798. return pRequest->QueryResponseHeader((LPSTR) gszPolicyHeaderName, gszHeaderSize,
  2799. pszHeader, pdwHeaderSize, 0, &dwIndex);
  2800. }
  2801. DWORD getImpliedCookieFlags(P3PCookieState *pState) {
  2802. if (!pState)
  2803. return 0;
  2804. DWORD dwImpliedFlags = 0;
  2805. // "leash" means that the cookie will only be used in 1st party context
  2806. if (pState->dwPolicyState==COOKIE_STATE_LEASH)
  2807. dwImpliedFlags |= INTERNET_COOKIE_IS_RESTRICTED;
  2808. // "downgrade" option forces cookies to session
  2809. if (pState->dwPolicyState==COOKIE_STATE_DOWNGRADE)
  2810. dwImpliedFlags |= INTERNET_COOKIE_IS_SESSION;
  2811. return dwImpliedFlags;
  2812. }
  2813. BOOL EvaluateCookiePolicy(const char *pszURL, BOOL f3rdParty, BOOL fRestricted,
  2814. P3PCookieState *pState,
  2815. const char *pszHostName) {
  2816. char achHostName[INTERNET_MAX_HOST_NAME_LENGTH];
  2817. // If hostname is not given, it will be derived from the URL
  2818. if (!pszHostName) {
  2819. URL_COMPONENTS uc;
  2820. memset(&uc, 0, sizeof(uc));
  2821. uc.dwStructSize = sizeof(URL_COMPONENTS);
  2822. uc.lpszHostName = achHostName;
  2823. uc.dwHostNameLength = sizeof(achHostName);
  2824. InternetCrackUrl(pszURL, 0, 0, &uc);
  2825. pszHostName = achHostName;
  2826. }
  2827. /* For compatibility purposes--
  2828. If registry settings are not available default behavior is:
  2829. ACCEPT all cookies without restrictions */
  2830. pState->dwPolicyState = COOKIE_STATE_ACCEPT;
  2831. pState->fValidPolicy = FALSE;
  2832. pState->fEvaluated = FALSE;
  2833. pState->fIncSession = TRUE;
  2834. pState->dwEvalMode = URLPOLICY_QUERY;
  2835. DWORD dwMainSwitch = fRestricted ?
  2836. GetCookieMainSwitch(URLZONE_UNTRUSTED) :
  2837. GetCookieMainSwitch(pszURL);
  2838. if (dwMainSwitch!=URLPOLICY_QUERY)
  2839. {
  2840. pState->dwEvalMode = dwMainSwitch;
  2841. pState->dwPolicyState = (dwMainSwitch==URLPOLICY_ALLOW) ?
  2842. COOKIE_STATE_ACCEPT :
  2843. COOKIE_STATE_REJECT;
  2844. return TRUE;
  2845. }
  2846. /* Check prompt history for past decisions made by the user about this website. */
  2847. if (cookieUIhistory.lookupDecision(pszHostName, NULL, & pState->dwPolicyState))
  2848. {
  2849. pState->fValidPolicy = FALSE;
  2850. }
  2851. else
  2852. {
  2853. CCookieSettings *pSettings = NULL;
  2854. CCookieSettings::GetSettings(&pSettings, pszURL, f3rdParty, fRestricted);
  2855. if (pSettings)
  2856. {
  2857. pSettings->EvaluatePolicy(pState);
  2858. pSettings->Release();
  2859. }
  2860. }
  2861. return TRUE;
  2862. }
  2863. DWORD cacheFlagFromAction(DWORD dwAction) {
  2864. switch (dwAction) {
  2865. case COOKIE_STATE_ACCEPT: return COOKIE_ACCEPTED_CACHE_ENTRY;
  2866. case COOKIE_STATE_LEASH: return COOKIE_LEASHED_CACHE_ENTRY;
  2867. case COOKIE_STATE_DOWNGRADE: return COOKIE_DOWNGRADED_CACHE_ENTRY;
  2868. case COOKIE_STATE_REJECT: return COOKIE_REJECTED_CACHE_ENTRY;
  2869. }
  2870. return 0;
  2871. }
  2872. DWORD
  2873. HTTP_REQUEST_HANDLE_OBJECT::ExtractSetCookieHeaders(LPDWORD lpdwHeaderIndex)
  2874. {
  2875. DWORD error = ERROR_HTTP_COOKIE_DECLINED;
  2876. P3PCookieState CS;
  2877. char *pchP3PHeader = (char *) ALLOCATE_ZERO_MEMORY(CCH_COOKIE_MAX);
  2878. char *pchHeader = (char *) ALLOCATE_ZERO_MEMORY(CCH_COOKIE_MAX);
  2879. if (pchP3PHeader == NULL || pchHeader == NULL)
  2880. {
  2881. error = ERROR_NOT_ENOUGH_MEMORY;
  2882. goto Cleanup;
  2883. }
  2884. DWORD cbPolicy = CCH_COOKIE_MAX;
  2885. if (ERROR_SUCCESS == extractP3PHeader(this, pchP3PHeader, &cbPolicy))
  2886. {
  2887. CS.pszP3PHeader = pchP3PHeader;
  2888. InternetIndicateStatus(INTERNET_STATUS_P3P_HEADER,
  2889. (LPBYTE) CS.pszP3PHeader,
  2890. cbPolicy+1);
  2891. }
  2892. else
  2893. CS.pszP3PHeader = NULL;
  2894. if (!IsResponseHeaderPresent(HTTP_QUERY_SET_COOKIE))
  2895. {
  2896. error = ERROR_SUCCESS;
  2897. goto CheckForPolicyRef;
  2898. }
  2899. BOOL fRestricted = GetOpenFlags() & INTERNET_FLAG_RESTRICTED_ZONE;
  2900. EvaluateCookiePolicy(GetURL(),
  2901. Is3rdPartyCookies(),
  2902. fRestricted,
  2903. &CS,
  2904. GetHostName());
  2905. /* NULL index pointer indicates that only P3P policy is evaluated,
  2906. cookies are not processed */
  2907. if (!lpdwHeaderIndex)
  2908. goto SendNotification;
  2909. DWORD iQuery = *lpdwHeaderIndex;
  2910. DWORD cbHeader = CCH_COOKIE_MAX - 1;
  2911. int cPersistent = 0; /* # of persistent cookies */
  2912. int cSession = 0; /* # of session cookies */
  2913. /* Array for storing # of cookies subject to each action */
  2914. int cCount[COOKIE_STATE_MAX+1] = { 0 };
  2915. _ResponseHeaders.LockHeaders();
  2916. while ( QueryResponseHeader(
  2917. HTTP_QUERY_SET_COOKIE,
  2918. pchHeader,
  2919. &cbHeader,
  2920. 0,
  2921. &iQuery) == ERROR_SUCCESS)
  2922. {
  2923. // All IE6 cookies are marked with this flag to distinguish
  2924. // from legacy cookies inherited from past versions.
  2925. DWORD dwCookieFlags = INTERNET_COOKIE_IE6;
  2926. if (_fBlockedOnPrompt)
  2927. dwCookieFlags |= INTERNET_COOKIE_PROMPT_REQUIRED;
  2928. pchHeader[cbHeader] = 0;
  2929. DWORD dwAction;
  2930. DWORD dwRet = s_pJar->SetCookie(this, GetURL(), pchHeader, dwCookieFlags, &CS, &dwAction);
  2931. /* The cookie flags are passed by reference to the SetCookie() function.
  2932. Upon return the requested flags will have been merged with flags from parsing */
  2933. BOOL fSession = (dwCookieFlags & COOKIE_SESSION);
  2934. fSession ? cSession++ : cPersistent++;
  2935. INET_ASSERT(dwAction<=COOKIE_STATE_MAX);
  2936. if (dwRet == COOKIE_SUCCESS)
  2937. {
  2938. *lpdwHeaderIndex = iQuery;
  2939. error = ERROR_SUCCESS;
  2940. cCount[dwAction]++;
  2941. AddCacheEntryType(cacheFlagFromAction(dwAction));
  2942. }
  2943. else if (dwRet == COOKIE_PENDING)
  2944. {
  2945. error = ERROR_IO_PENDING;
  2946. INET_ASSERT(iQuery != 0);
  2947. *lpdwHeaderIndex = iQuery - 1; // back up and retry this cookie
  2948. _fBlockedOnPrompt = TRUE;
  2949. break;
  2950. }
  2951. else if (dwRet == COOKIE_FAIL)
  2952. {
  2953. /* Only consider cookies blocked because of privacy reasons.
  2954. Other reasons for rejecting the cookie (syntax errors,
  2955. incorrect domain/path etc.) are not reported */
  2956. if (dwAction==COOKIE_STATE_REJECT)
  2957. {
  2958. cCount[dwAction]++;
  2959. AddCacheEntryType(COOKIE_REJECTED_CACHE_ENTRY);
  2960. }
  2961. }
  2962. cbHeader = CCH_COOKIE_MAX - 1;
  2963. _fBlockedOnPrompt = FALSE;
  2964. }
  2965. _ResponseHeaders.UnlockHeaders();
  2966. SendNotification:
  2967. // Postpone notifications if user has not answered the prompt yet
  2968. if (error == ERROR_IO_PENDING)
  2969. goto Cleanup;
  2970. else
  2971. {
  2972. IncomingCookieState recvState = {0};
  2973. recvState.cPersistent = cPersistent;
  2974. recvState.cSession = cSession;
  2975. recvState.cAccepted = cCount[COOKIE_STATE_ACCEPT];
  2976. recvState.cLeashed = cCount[COOKIE_STATE_LEASH];
  2977. recvState.cDowngraded = cCount[COOKIE_STATE_DOWNGRADE];
  2978. recvState.cBlocked = cCount[COOKIE_STATE_REJECT];
  2979. // performance optimization-- same URL as the request
  2980. recvState.pszLocation = NULL;
  2981. // Send notification about P3P state
  2982. InternetIndicateStatus(INTERNET_STATUS_COOKIE_RECEIVED,
  2983. (LPBYTE) & recvState,
  2984. sizeof(recvState));
  2985. }
  2986. CheckForPolicyRef:
  2987. /* If P3P header contains URL of the policy-ref, this information
  2988. must be communicated to WININET clients */
  2989. char *pszPolicyRef = NULL;
  2990. unsigned long dwLength = 0;
  2991. if (CS.pszP3PHeader && seekPolicyRef(CS.pszP3PHeader, &pszPolicyRef, &dwLength))
  2992. {
  2993. pszPolicyRef[dwLength] = '\0'; // create nil-terminated string containing policy-ref URL
  2994. InternetIndicateStatus(INTERNET_STATUS_P3P_POLICYREF,
  2995. (LPBYTE) pszPolicyRef,
  2996. dwLength+1);
  2997. }
  2998. Cleanup:
  2999. if (pchHeader)
  3000. FREE_MEMORY(pchHeader);
  3001. if (pchP3PHeader)
  3002. FREE_MEMORY(pchP3PHeader);
  3003. return error;
  3004. }
  3005. INTERNETAPI_(DWORD) InternetSetCookieExW(
  3006. LPCWSTR lpszUrl,
  3007. LPCWSTR lpszCookieName,
  3008. LPCWSTR lpszCookieData,
  3009. DWORD dwFlags,
  3010. DWORD_PTR dwReserved
  3011. )
  3012. {
  3013. DEBUG_ENTER_API((DBG_INET,
  3014. Bool,
  3015. "InternetSetCookieExW",
  3016. "%wq, %#x, %#x, %#x, %#x",
  3017. lpszUrl,
  3018. lpszCookieName,
  3019. lpszCookieData,
  3020. dwFlags,
  3021. dwReserved
  3022. ));
  3023. DWORD dwErr = ERROR_SUCCESS;
  3024. DWORD dwResult = FALSE;
  3025. MEMORYPACKET mpUrl, mpCookieName, mpCookieData, mpP3PHeader;
  3026. void *lpReserved = (void*) dwReserved;
  3027. if (lpszUrl)
  3028. {
  3029. ALLOC_MB(lpszUrl,0,mpUrl);
  3030. if (!mpUrl.psStr)
  3031. {
  3032. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  3033. goto cleanup;
  3034. }
  3035. UNICODE_TO_ANSI(lpszUrl,mpUrl);
  3036. }
  3037. if (lpszCookieName)
  3038. {
  3039. ALLOC_MB(lpszCookieName,0,mpCookieName);
  3040. if (!mpCookieName.psStr)
  3041. {
  3042. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  3043. goto cleanup;
  3044. }
  3045. UNICODE_TO_ANSI(lpszCookieName,mpCookieName);
  3046. }
  3047. if (lpszCookieData)
  3048. {
  3049. ALLOC_MB(lpszCookieData,0,mpCookieData);
  3050. if (!mpCookieData.psStr)
  3051. {
  3052. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  3053. goto cleanup;
  3054. }
  3055. UNICODE_TO_ANSI(lpszCookieData,mpCookieData);
  3056. }
  3057. /* Reserved parameter is used for passing in the P3P header */
  3058. if (dwReserved && (dwFlags & INTERNET_COOKIE_EVALUATE_P3P))
  3059. {
  3060. LPWSTR pwszP3PHeader = (LPWSTR) dwReserved;
  3061. ALLOC_MB(pwszP3PHeader, 0, mpP3PHeader);
  3062. if (!mpP3PHeader.psStr)
  3063. {
  3064. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  3065. goto cleanup;
  3066. }
  3067. UNICODE_TO_ANSI(pwszP3PHeader, mpP3PHeader);
  3068. lpReserved = mpP3PHeader.psStr;
  3069. }
  3070. dwResult = InternalInternetSetCookie(mpUrl.psStr, mpCookieName.psStr, mpCookieData.psStr, dwFlags, lpReserved);
  3071. cleanup:
  3072. if (dwErr!=ERROR_SUCCESS)
  3073. {
  3074. SetLastError(dwErr);
  3075. DEBUG_ERROR(INET, dwErr);
  3076. }
  3077. DEBUG_LEAVE_API(dwResult);
  3078. return dwResult;
  3079. }
  3080. INTERNETAPI_(DWORD) InternetSetCookieExA(
  3081. LPCSTR lpszUrl,
  3082. LPCSTR lpszCookieName,
  3083. LPCSTR lpszCookieData,
  3084. DWORD dwFlags,
  3085. DWORD_PTR dwReserved
  3086. )
  3087. {
  3088. DEBUG_ENTER_API((DBG_INET,
  3089. Bool,
  3090. "InternetSetCookieExA",
  3091. "%wq, %#x, %#x, %#x, %#x",
  3092. lpszUrl,
  3093. lpszCookieName,
  3094. lpszCookieData,
  3095. dwFlags,
  3096. dwReserved
  3097. ));
  3098. DWORD dwResult = InternalInternetSetCookie(lpszUrl, lpszCookieName, lpszCookieData, dwFlags, (void*) dwReserved);
  3099. DEBUG_LEAVE_API(dwResult);
  3100. return dwResult;
  3101. }
  3102. DWORD
  3103. HTTP_REQUEST_HANDLE_OBJECT::CreateCookieHeaderIfNeeded(int *pcCookie)
  3104. {
  3105. char * pchRDomain = NULL;
  3106. char * pchPath = NULL;
  3107. DWORD cch;
  3108. int cchName;
  3109. int cchValue;
  3110. FILETIME ftCurrent, ftExpire;
  3111. BOOL fSecure;
  3112. CCookieLocation *pLocation;
  3113. CCookie *pCookie;
  3114. DWORD dwError = 0;
  3115. DWORD dwMainSwitch = GetCookieMainSwitch(GetSecurityZone());
  3116. BOOL fNoReplay = (dwMainSwitch==URLPOLICY_DISALLOW);
  3117. BOOL fReplayAll = (dwMainSwitch==URLPOLICY_ALLOW);
  3118. BOOL f3rdPartyRequest = Is3rdPartyCookies();
  3119. int cCookie = 0; // # of cookies added
  3120. int cSuppressed = 0; // # of cookies suppressed
  3121. char * pchHeader = (char *) ALLOCATE_FIXED_MEMORY(CCH_COOKIE_MAX);
  3122. if (pchHeader == NULL)
  3123. {
  3124. dwError = ERROR_NOT_ENOUGH_MEMORY;
  3125. goto Cleanup;
  3126. }
  3127. char *pchHeaderStart = pchHeader;
  3128. // remove cookie header if it exists
  3129. // BUGBUG - we are overriding the app. Original cookie code has this. Don't know why.
  3130. ReplaceRequestHeader(HTTP_QUERY_COOKIE, NULL, 0, 0, 0);
  3131. memset(&ftExpire, 0, sizeof(FILETIME));
  3132. if (!PathAndRDomainFromURL(GetURL(), &pchRDomain, &pchPath, &fSecure, FALSE))
  3133. goto Cleanup;
  3134. fSecure = GetOpenFlags() & INTERNET_FLAG_SECURE;
  3135. GetCurrentGmtTime(&ftCurrent);
  3136. {
  3137. CCookieCriticalSection cs;
  3138. if (!s_pJar->SyncWithCacheIfNeeded())
  3139. goto Cleanup;
  3140. LockHeaders();
  3141. for (pLocation = *s_pJar->GetBucket(pchRDomain); pLocation; pLocation = pLocation->_pLocationNext)
  3142. {
  3143. if (pLocation->IsMatch(pchRDomain, pchPath))
  3144. {
  3145. pLocation->ReadCacheFileIfNeeded();
  3146. for (pCookie = pLocation->_pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  3147. {
  3148. if (IsLegacyCookie(pCookie))
  3149. convertLegacyCookie(pCookie, pLocation);
  3150. if (pCookie->CanSend(&ftCurrent, fSecure))
  3151. {
  3152. pchHeader = pchHeaderStart;
  3153. cch = 0;
  3154. cch += cchName = strlen(pCookie->_pchName);
  3155. cch += cchValue = strlen(pCookie->_pchValue);
  3156. if (cchName) cch += 1; // for equal sign
  3157. if (cch < CCH_COOKIE_MAX)
  3158. {
  3159. if (cchName > 0)
  3160. {
  3161. memcpy(pchHeader, pCookie->_pchName, cchName);
  3162. pchHeader += cchName;
  3163. *pchHeader++ = '=';
  3164. }
  3165. if (cchValue > 0)
  3166. {
  3167. memcpy(pchHeader, pCookie->_pchValue, cchValue);
  3168. pchHeader += cchValue;
  3169. }
  3170. /* IF the cookie is marked 1st party only,
  3171. OR cookie feature is not enabled for this zone,
  3172. suppress the cookie */
  3173. if (fNoReplay ||
  3174. (!fReplayAll && f3rdPartyRequest && pCookie->IsRestricted()))
  3175. {
  3176. cSuppressed++;
  3177. continue;
  3178. }
  3179. cCookie += 1;
  3180. AddRequestHeader(HTTP_QUERY_COOKIE,
  3181. pchHeaderStart,
  3182. cch,
  3183. 0,
  3184. HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON);
  3185. }
  3186. } // if CanSend
  3187. } // for pCookie
  3188. } // if IsMatch
  3189. } // for
  3190. UnlockHeaders();
  3191. }
  3192. Cleanup:
  3193. // Send notification about sent/suppressed in this request
  3194. if (cCookie || cSuppressed)
  3195. {
  3196. OutgoingCookieState sendState = { cCookie, cSuppressed };
  3197. InternetIndicateStatus(INTERNET_STATUS_COOKIE_SENT, (LPBYTE) &sendState, sizeof(sendState));
  3198. }
  3199. if (pchHeaderStart)
  3200. FREE_MEMORY(pchHeaderStart);
  3201. if (pchRDomain)
  3202. FREE_MEMORY(pchRDomain);
  3203. if (pchPath)
  3204. FREE_MEMORY(pchPath);
  3205. if(pcCookie)
  3206. {
  3207. *pcCookie = cCookie;
  3208. }
  3209. return dwError;
  3210. }
  3211. // IsDomainLegalCookieDomain - exported in wininet.w for private use..
  3212. //
  3213. // example: ( "yahoo.com", "www.yahoo.com") -> TRUE
  3214. // ( "com", "www.yahoo.com") -> FALSE
  3215. // ( "0.255.192", "255.0.255.192") -> FALSE
  3216. // ( "255.0.255.192", "255.0.255.192") -> TRUE
  3217. BOOLAPI IsDomainLegalCookieDomainA( IN LPCSTR pchDomain, IN LPCSTR pchFullDomain)
  3218. {
  3219. BOOL returnValue = FALSE;
  3220. DWORD dwError = ERROR_SUCCESS;
  3221. LPSTR pchReversedDomain = NULL;
  3222. LPSTR pchReversedFullDomain = NULL;
  3223. long iDomainSize, iFullDomainSize;
  3224. if(!pchDomain || IsBadStringPtr( pchDomain, INTERNET_MAX_URL_LENGTH))
  3225. {
  3226. dwError = ERROR_INVALID_PARAMETER;
  3227. goto doneIsDomainLegalCookieDomainA;
  3228. }
  3229. if(!pchFullDomain || IsBadStringPtr( pchFullDomain, INTERNET_MAX_URL_LENGTH))
  3230. {
  3231. dwError = ERROR_INVALID_PARAMETER;
  3232. goto doneIsDomainLegalCookieDomainA;
  3233. }
  3234. iDomainSize = strlen( pchDomain) + 1;
  3235. iFullDomainSize = strlen( pchFullDomain) + 1;
  3236. pchReversedDomain = new char[ iDomainSize];
  3237. pchReversedFullDomain = new char[ iFullDomainSize];
  3238. if( pchReversedDomain == NULL || pchReversedFullDomain == NULL)
  3239. goto doneIsDomainLegalCookieDomainA;
  3240. memcpy( pchReversedDomain, pchDomain, iDomainSize);
  3241. memcpy( pchReversedFullDomain, pchFullDomain, iFullDomainSize);
  3242. ReverseString( pchReversedDomain);
  3243. ReverseString( pchReversedFullDomain);
  3244. returnValue = IsDomainLegal( pchReversedDomain, pchReversedFullDomain);
  3245. doneIsDomainLegalCookieDomainA:
  3246. if( dwError != ERROR_SUCCESS)
  3247. SetLastError( dwError);
  3248. if( pchReversedDomain != NULL)
  3249. delete [] pchReversedDomain;
  3250. if( pchReversedFullDomain != NULL)
  3251. delete [] pchReversedFullDomain;
  3252. return returnValue;
  3253. }
  3254. BOOLAPI IsDomainLegalCookieDomainW( IN LPCWSTR pwchDomain, IN LPCWSTR pwchFullDomain)
  3255. {
  3256. MEMORYPACKET mpDomain;
  3257. ALLOC_MB(pwchDomain,0,mpDomain);
  3258. if (!mpDomain.psStr)
  3259. {
  3260. return FALSE;
  3261. }
  3262. UNICODE_TO_ANSI(pwchDomain, mpDomain);
  3263. MEMORYPACKET mpFullDomain;
  3264. ALLOC_MB(pwchFullDomain,0,mpFullDomain);
  3265. if (!mpFullDomain.psStr)
  3266. {
  3267. return FALSE;
  3268. }
  3269. UNICODE_TO_ANSI(pwchFullDomain, mpFullDomain);
  3270. return IsDomainLegalCookieDomainA( mpDomain.psStr, mpFullDomain.psStr);
  3271. }