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.

1516 lines
40 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 "cookiejar.h"
  91. #define CCH_COOKIE_MAX (5 * 1024)
  92. static char s_achEmpty[] = "";
  93. // Hard-coded list of special domains. If any of these are present between the
  94. // second-to-last and last dot we will require 2 embedded dots.
  95. // The domain strings are reversed to make the compares easier
  96. static const char *s_pachSpecialDomains[] =
  97. {"MOC", "UDE", "TEN", "GRO", "VOG", "LIM", "TNI" };
  98. struct CookieInfo {
  99. char *pchRDomain;
  100. char *pchPath;
  101. char *pchName;
  102. char *pchValue;
  103. unsigned long dwFlags;
  104. FILETIME ftExpiration;
  105. };
  106. //---------------------------------------------------------------------------
  107. //
  108. // String utilities
  109. //
  110. //---------------------------------------------------------------------------
  111. static BOOL
  112. IsZero(FILETIME *pft)
  113. {
  114. return pft->dwLowDateTime == 0 && pft->dwHighDateTime == 0;
  115. }
  116. static char *
  117. StrnDup(const char *pch, int cch)
  118. {
  119. char *pchAlloc = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cch + 1);
  120. if (!pchAlloc)
  121. {
  122. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  123. return NULL;
  124. }
  125. memcpy(pchAlloc, pch, cch);
  126. pchAlloc[cch] = 0;
  127. return pchAlloc;
  128. }
  129. static BOOL
  130. IsPathMatch(const char *pchPrefix, const char *pchStr)
  131. {
  132. while (*pchPrefix == *pchStr && *pchStr)
  133. {
  134. pchPrefix += 1;
  135. pchStr += 1;
  136. }
  137. return *pchPrefix == 0;
  138. }
  139. static BOOL
  140. IsDomainMatch(const char *pchPrefix, const char *pchStr)
  141. {
  142. while (*pchPrefix == *pchStr && *pchStr)
  143. {
  144. pchPrefix += 1;
  145. pchStr += 1;
  146. }
  147. return *pchPrefix == 0 && (*pchStr == 0 || *pchStr == '.');
  148. }
  149. static BOOL
  150. IsPathLegal(const char *pchHeader, const char *pchDocument)
  151. {
  152. return TRUE;
  153. /*
  154. We attempted to implement the specification here.
  155. It looks like Navigator does not reject cookies
  156. based on the path attribute. We now consider
  157. all path attributes to be legal.
  158. while (*pchHeader == *pchDocument && *pchDocument)
  159. {
  160. pchHeader += 1;
  161. pchDocument += 1;
  162. }
  163. if (*pchDocument == 0)
  164. {
  165. while (*pchHeader && *pchHeader != '/' && *pchHeader != '\\')
  166. {
  167. pchHeader += 1;
  168. }
  169. }
  170. return *pchHeader == 0;
  171. */
  172. }
  173. extern PTSTR GlobalSpecialDomains;
  174. extern PTSTR *GlobalSDOffsets;
  175. static BOOL
  176. IsVerySpecialDomain(const char *pch, int nTopLevel, int nSecond)
  177. {
  178. if (!GlobalSpecialDomains)
  179. {
  180. HKEY hk;
  181. if (ERROR_SUCCESS==RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  182. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\5.0"),
  183. 0,
  184. KEY_READ,
  185. &hk))
  186. {
  187. DWORD dwType, dwSize;
  188. if ((ERROR_SUCCESS==RegQueryValueEx(hk, "SpecialDomains", NULL, &dwType, NULL, &dwSize))
  189. && (REG_SZ==dwType))
  190. {
  191. GlobalSpecialDomains = New TCHAR[dwSize];
  192. if (GlobalSpecialDomains
  193. && (ERROR_SUCCESS==RegQueryValueEx(hk, "SpecialDomains", NULL, &dwType, (LPBYTE)GlobalSpecialDomains, &dwSize)))
  194. {
  195. // We're going to scan a string stored in the registry to gather the domains we should
  196. // allow. Format:
  197. // [domain] [domain] [domain]
  198. // The delimiter is a space character.
  199. PTSTR psz = GlobalSpecialDomains;
  200. DWORD dwDomains = 0;
  201. BOOL fWord = FALSE;
  202. while (*psz)
  203. {
  204. if (*psz==TEXT(' '))
  205. {
  206. if (fWord)
  207. {
  208. fWord = FALSE;
  209. dwDomains++;
  210. }
  211. }
  212. else
  213. {
  214. fWord = TRUE;
  215. }
  216. psz++;
  217. }
  218. if (fWord)
  219. {
  220. dwDomains++;
  221. }
  222. GlobalSDOffsets = (PTSTR*)New PTSTR[dwDomains+1];
  223. if (GlobalSDOffsets)
  224. {
  225. psz = GlobalSpecialDomains;
  226. for (DWORD dw = 0; dw < dwDomains; dw++)
  227. {
  228. INET_ASSERT(*psz);
  229. while (*psz==TEXT(' '))
  230. psz++;
  231. INET_ASSERT(*psz);
  232. GlobalSDOffsets[dw] = psz;
  233. while (*psz && *psz!=TEXT(' '))
  234. {
  235. psz++;
  236. }
  237. *psz = TEXT('\0');
  238. }
  239. GlobalSDOffsets[dwDomains] = NULL;
  240. }
  241. }
  242. }
  243. RegCloseKey(hk);
  244. }
  245. }
  246. // WARNING: The following lines of code make it possible for cookies to be set for *.uk,
  247. // (for example) if "ku." is in the registry
  248. BOOL fRet = FALSE;
  249. if (GlobalSDOffsets)
  250. {
  251. for (DWORD i = 0; GlobalSDOffsets[i]; i++)
  252. {
  253. if (!StrCmpNI(pch, GlobalSDOffsets[i], nTopLevel)
  254. || !StrCmpNI(pch, GlobalSDOffsets[i], nTopLevel+nSecond+1))
  255. {
  256. fRet = TRUE;
  257. break;
  258. }
  259. }
  260. }
  261. return fRet;
  262. }
  263. static BOOL
  264. IsSpecialDomain(const char *pch, int nCount)
  265. {
  266. // Currently all the special strings are exactly 3 characters long.
  267. if (pch == NULL || nCount != 3)
  268. return FALSE;
  269. for (int i = 0 ; i < ARRAY_ELEMENTS(s_pachSpecialDomains) ; i++ )
  270. {
  271. if (StrCmpNIC(pch, s_pachSpecialDomains[i], nCount) == 0)
  272. return TRUE;
  273. }
  274. return FALSE;
  275. }
  276. static BOOL
  277. IsDomainLegal(const char *pchHeader, const char *pchDocument)
  278. {
  279. const char *pchCurrent = pchHeader;
  280. int nSegment = 0;
  281. int dwCharCount = 0;
  282. int rgcch[2] = { 0, 0 }; // How many characters between dots
  283. // Must have at least one period in name.
  284. // and contains nothing but '.' is illegal
  285. int dwSegmentLength = 0;
  286. const char * pchSecondPart = NULL; // for a domain string such as
  287. for (; *pchCurrent; pchCurrent++)
  288. {
  289. if (*pchCurrent == '.')
  290. {
  291. if (nSegment < 2)
  292. {
  293. // Remember how many characters we have between the last two dots
  294. // For example if domain header is .microsoft.com
  295. // rgcch[0] should be 3 for "com"
  296. // rgcch[1] should be 9 for "microsoft"
  297. rgcch[nSegment] = dwSegmentLength;
  298. if (nSegment == 1)
  299. {
  300. pchSecondPart = pchCurrent - dwSegmentLength;
  301. }
  302. }
  303. dwSegmentLength = 0;
  304. nSegment += 1;
  305. }
  306. else
  307. {
  308. dwSegmentLength++;
  309. }
  310. dwCharCount++;
  311. }
  312. // The code below depends on the leading dot being removed from the domain header.
  313. // The parse code does that, but an assert here just in case something changes in the
  314. // parse code.
  315. INET_ASSERT(*(pchCurrent - 1) != '.');
  316. // Remember the count of the characters between the begining of the header and
  317. // the first dot. So for domain=abc.com this will set rgch[1] = 3.
  318. // Note that this assumes that if domain=.abc.com the leading dot has been stripped
  319. // out in the parse code. See assert above.
  320. if (nSegment < 2 )
  321. {
  322. rgcch[nSegment] = dwSegmentLength;
  323. if (nSegment==1)
  324. {
  325. pchSecondPart = pchCurrent - dwSegmentLength;
  326. }
  327. }
  328. // If the domain name is of the form abc.xx.yy where the number of characters between the last two dots is less than
  329. // 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
  330. // example. However this rule is not sufficient and we special case things like .edu.nz as well.
  331. int cEmbeddedDotsNeeded = 1;
  332. if (rgcch[0] <= 2)
  333. {
  334. if ((rgcch[1] <= 2 && !IsVerySpecialDomain(pchHeader, rgcch[0], rgcch[1]))
  335. || (pchSecondPart && IsSpecialDomain(pchSecondPart, rgcch[1])))
  336. cEmbeddedDotsNeeded = 2;
  337. }
  338. if (nSegment < cEmbeddedDotsNeeded || dwCharCount == nSegment)
  339. return FALSE;
  340. // Mismatch between header and document not allowed.
  341. // Must match full components of domain name.
  342. while (*pchHeader == *pchDocument && *pchDocument)
  343. {
  344. pchHeader += 1;
  345. pchDocument += 1;
  346. }
  347. return *pchHeader == 0 && (*pchDocument == 0 || *pchDocument == '.' );
  348. }
  349. void
  350. LowerCaseString(char *pch)
  351. {
  352. for (; *pch; pch++)
  353. {
  354. if (*pch >= 'A' && *pch <= 'Z')
  355. *pch += 'a' - 'A';
  356. }
  357. }
  358. static void
  359. ReverseString(char *pchFront)
  360. {
  361. char *pchBack;
  362. char ch;
  363. int cch;
  364. cch = strlen(pchFront);
  365. pchBack = pchFront + cch - 1;
  366. cch = cch / 2;
  367. while (--cch >= 0)
  368. {
  369. ch = tolower(*pchFront);
  370. *pchFront = tolower(*pchBack);
  371. *pchBack = ch;
  372. pchFront += 1;
  373. pchBack -= 1;
  374. }
  375. }
  376. static BOOL
  377. PathAndRDomainFromURL(const char *pchURL, char **ppchRDomain, char **ppchPath, BOOL *pfSecure, BOOL bStrip = TRUE)
  378. {
  379. char *pchDomainBuf;
  380. char *pchRDomain = NULL;
  381. char *pchPathBuf;
  382. char *pchPath = NULL;
  383. char *pchExtra;
  384. DWORD cchDomain;
  385. DWORD cchPath;
  386. DWORD cchExtra;
  387. BOOL fSuccess = FALSE;
  388. DWORD dwError;
  389. INTERNET_SCHEME ustScheme;
  390. dwError = CrackUrl((char *)pchURL,
  391. 0,
  392. FALSE,
  393. &ustScheme,
  394. NULL, // Scheme Name
  395. NULL, // Scheme Lenth
  396. &pchDomainBuf,
  397. &cchDomain,
  398. NULL, // Internet Port
  399. NULL, // UserName
  400. NULL, // UserName Length
  401. NULL, // Password
  402. NULL, // Password Lenth
  403. &pchPathBuf,
  404. &cchPath,
  405. &pchExtra, // Extra
  406. &cchExtra, // Extra Length
  407. NULL);
  408. if (dwError != ERROR_SUCCESS)
  409. {
  410. SetLastError(dwError);
  411. goto Cleanup;
  412. }
  413. if ( ustScheme != INTERNET_SCHEME_HTTP &&
  414. ustScheme != INTERNET_SCHEME_HTTPS &&
  415. ustScheme != INTERNET_SCHEME_UNKNOWN)
  416. {
  417. //
  418. // known scheme should be supported
  419. // e.g. 3rd party pluggable protocol should be able to
  420. // call cookie api to setup cookies...
  421. //
  422. SetLastError(ERROR_INVALID_PARAMETER);
  423. goto Cleanup;
  424. }
  425. *pfSecure = ustScheme == INTERNET_SCHEME_HTTPS;
  426. if(bStrip)
  427. {
  428. while (cchPath > 0)
  429. {
  430. if (pchPathBuf[cchPath - 1] == '/' || pchPathBuf[cchPath - 1] == '\\')
  431. {
  432. break;
  433. }
  434. cchPath -= 1;
  435. }
  436. }
  437. pchRDomain = StrnDup(pchDomainBuf, cchDomain);
  438. if (!pchRDomain)
  439. goto Cleanup;
  440. LowerCaseString(pchRDomain);
  441. ReverseString(pchRDomain);
  442. pchPath = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cchPath + 2);
  443. if (!pchPath)
  444. goto Cleanup;
  445. if (*pchPathBuf != '/')
  446. {
  447. *pchPath = '/';
  448. memcpy(pchPath + 1, pchPathBuf, cchPath);
  449. pchPath[cchPath + 1] = TEXT('\0');
  450. }
  451. else
  452. {
  453. memcpy(pchPath, pchPathBuf, cchPath);
  454. pchPath[cchPath] = TEXT('\0');
  455. }
  456. fSuccess = TRUE;
  457. Cleanup:
  458. if (!fSuccess)
  459. {
  460. if (pchRDomain)
  461. FREE_MEMORY(pchRDomain);
  462. if (pchPath)
  463. FREE_MEMORY(pchPath);
  464. }
  465. else
  466. {
  467. *ppchRDomain = pchRDomain;
  468. *ppchPath = pchPath;
  469. }
  470. return fSuccess;
  471. }
  472. //---------------------------------------------------------------------------
  473. //
  474. // CCookieBase implementation
  475. //
  476. //---------------------------------------------------------------------------
  477. void *
  478. CCookieBase::operator new(size_t cb, size_t cbExtra)
  479. {
  480. void *pv = ALLOCATE_MEMORY(LMEM_FIXED, cb + cbExtra);
  481. if (!pv)
  482. {
  483. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  484. return NULL;
  485. }
  486. memset(pv, 0, cb);
  487. return pv;
  488. }
  489. inline void
  490. CCookieBase::operator delete(void *pv)
  491. {
  492. FREE_MEMORY(pv);
  493. }
  494. //---------------------------------------------------------------------------
  495. //
  496. // CCookie implementation
  497. //
  498. //---------------------------------------------------------------------------
  499. CCookie *
  500. CCookie::Construct(const char *pchName)
  501. {
  502. CCookie *pCookie = new(strlen(pchName) + 1) CCookie();
  503. if (!pCookie)
  504. return NULL;
  505. pCookie->_pchName = (char *)(pCookie + 1);
  506. pCookie->_pchValue = s_achEmpty;
  507. strcpy(pCookie->_pchName, pchName);
  508. pCookie->_dwFlags = COOKIE_SESSION;
  509. return pCookie;
  510. }
  511. CCookie::~CCookie()
  512. {
  513. if (_pchValue != s_achEmpty)
  514. FREE_MEMORY(_pchValue);
  515. }
  516. BOOL
  517. CCookie::SetValue(const char *pchValue)
  518. {
  519. int cchValue;
  520. if (_pchValue != s_achEmpty)
  521. FREE_MEMORY(_pchValue);
  522. if (!pchValue || !*pchValue)
  523. {
  524. _pchValue = s_achEmpty;
  525. }
  526. else
  527. {
  528. cchValue = strlen(pchValue) + 1;
  529. _pchValue = (char *)ALLOCATE_MEMORY(LMEM_FIXED, cchValue);
  530. if (!_pchValue)
  531. {
  532. _pchValue = s_achEmpty;
  533. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  534. return FALSE;
  535. }
  536. memcpy(_pchValue, pchValue, cchValue);
  537. }
  538. return TRUE;
  539. }
  540. BOOL
  541. CCookie::CanSend(BOOL fSecure)
  542. {
  543. return (fSecure || !(_dwFlags & COOKIE_SECURE));
  544. }
  545. BOOL CCookie::PurgeAll(void *)
  546. {
  547. return TRUE;
  548. }
  549. static BOOL
  550. WriteString(HANDLE hFile, const char *pch)
  551. {
  552. DWORD cb;
  553. return pch && *pch ? WriteFile(hFile, pch, strlen(pch), &cb, NULL) : TRUE;
  554. }
  555. static BOOL
  556. WriteStringLF(HANDLE hFile, const char *pch)
  557. {
  558. DWORD cb;
  559. if (!WriteString(hFile, pch)) return FALSE;
  560. return WriteFile(hFile, "\n", 1, &cb, NULL);
  561. }
  562. //---------------------------------------------------------------------------
  563. //
  564. // CCookieLocation implementation
  565. //
  566. //---------------------------------------------------------------------------
  567. CCookieLocation *
  568. CCookieLocation::Construct(const char *pchRDomain, const char *pchPath)
  569. {
  570. int cchPath = strlen(pchPath);
  571. CCookieLocation *pLocation = new(strlen(pchRDomain) + cchPath + 2) CCookieLocation();
  572. if (!pLocation)
  573. return NULL;
  574. pLocation->_cchPath = cchPath;
  575. pLocation->_pchPath = (char *)(pLocation + 1);
  576. pLocation->_pchRDomain = pLocation->_pchPath + cchPath + 1;
  577. strcpy(pLocation->_pchRDomain, pchRDomain);
  578. strcpy(pLocation->_pchPath, pchPath);
  579. return pLocation;
  580. }
  581. CCookieLocation::~CCookieLocation()
  582. {
  583. Purge(CCookie::PurgeAll, NULL);
  584. }
  585. CCookie *
  586. CCookieLocation::GetCookie(const char *pchName, BOOL fCreate)
  587. {
  588. CCookie *pCookie;
  589. CCookie **ppCookie = &_pCookieKids;
  590. for (pCookie = _pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  591. {
  592. if (strcmp(pchName, pCookie->_pchName) == 0)
  593. return pCookie;
  594. ppCookie = &pCookie->_pCookieNext;
  595. }
  596. if (!fCreate)
  597. return NULL;
  598. pCookie = CCookie::Construct(pchName);
  599. if (!pCookie)
  600. return NULL;
  601. //
  602. // Insert cookie at end of list to match Navigator's behavior.
  603. //
  604. pCookie->_pCookieNext = NULL;
  605. *ppCookie = pCookie;
  606. return pCookie;
  607. }
  608. void
  609. CCookieLocation::Purge(BOOL (CCookie::*pfnPurge)(void *), void *pv)
  610. {
  611. CCookie **ppCookie = &_pCookieKids;
  612. CCookie *pCookie;
  613. while ((pCookie = *ppCookie) != NULL)
  614. {
  615. if ((pCookie->*pfnPurge)(pv))
  616. {
  617. *ppCookie = pCookie->_pCookieNext;
  618. delete pCookie;
  619. }
  620. else
  621. {
  622. ppCookie = &pCookie->_pCookieNext;
  623. }
  624. }
  625. }
  626. static char *
  627. ScanString(char *pch, char **pchStr)
  628. {
  629. *pchStr = pch;
  630. for (; *pch; *pch++)
  631. {
  632. if (*pch == '\n')
  633. {
  634. *pch = 0;
  635. pch += 1;
  636. break;
  637. }
  638. }
  639. return pch;
  640. }
  641. static char *
  642. ScanNumber(char *pch, DWORD *pdw)
  643. {
  644. DWORD dw = 0;
  645. char *pchJunk;
  646. for (; *pch >= '0' && *pch <= '9'; *pch++)
  647. {
  648. dw = (dw * 10) + *pch - '0';
  649. }
  650. *pdw = dw;
  651. return ScanString(pch, &pchJunk);
  652. }
  653. BOOL
  654. CCookieLocation::IsMatch(const char *pchRDomain, const char *pchPath)
  655. {
  656. return IsDomainMatch(_pchRDomain, pchRDomain) &&
  657. IsPathMatch(_pchPath, pchPath);
  658. }
  659. //---------------------------------------------------------------------------
  660. //
  661. // CCookieJar implementation
  662. //
  663. //---------------------------------------------------------------------------
  664. CCookieJar *
  665. CCookieJar::Construct()
  666. {
  667. return new(0) CCookieJar();
  668. }
  669. CCookieJar::CCookieJar()
  670. {
  671. _csCookieJar.Init();
  672. }
  673. CCookieJar::~CCookieJar()
  674. {
  675. for (int i = ARRAY_ELEMENTS(_apLocation); --i >= 0; )
  676. {
  677. CCookieLocation *pLocation = _apLocation[i];
  678. while (pLocation)
  679. {
  680. CCookieLocation *pLocationT = pLocation->_pLocationNext;
  681. delete pLocation;
  682. pLocation = pLocationT;
  683. }
  684. }
  685. }
  686. CCookieLocation **
  687. CCookieJar::GetBucket(const char *pchRDomain)
  688. {
  689. int ch;
  690. int cPeriod = 0;
  691. unsigned int hash = 0;
  692. for (; (ch = *pchRDomain) != 0; pchRDomain++)
  693. {
  694. if (ch == '.')
  695. {
  696. cPeriod += 1;
  697. if (cPeriod >= 2)
  698. break;
  699. }
  700. hash = (hash * 29) + ch;
  701. }
  702. hash = hash % ARRAY_ELEMENTS(_apLocation);
  703. return &_apLocation[hash];
  704. }
  705. CCookieLocation *
  706. CCookieJar::GetLocation(const char *pchRDomain, const char *pchPath, BOOL fCreate)
  707. {
  708. int cchPath = strlen(pchPath);
  709. CCookieLocation *pLocation = NULL;
  710. CCookieLocation **ppLocation = GetBucket(pchRDomain);
  711. // To support sending more specific cookies before less specific,
  712. // we keep list sorted by path length.
  713. while ((pLocation = *ppLocation) != NULL)
  714. {
  715. if (pLocation->_cchPath < cchPath)
  716. break;
  717. if (strcmp(pLocation->_pchPath, pchPath) == 0 &&
  718. strcmp(pLocation->_pchRDomain, pchRDomain) == 0)
  719. return pLocation;
  720. ppLocation = &pLocation->_pLocationNext;
  721. }
  722. if (!fCreate)
  723. goto Cleanup;
  724. pLocation = CCookieLocation::Construct(pchRDomain, pchPath);
  725. if (!pLocation)
  726. goto Cleanup;
  727. pLocation->_pLocationNext = *ppLocation;
  728. *ppLocation = pLocation;
  729. Cleanup:
  730. return pLocation;
  731. }
  732. void
  733. CCookieJar::expireCookies(CCookieLocation *pLocation, FILETIME *pftNow) {
  734. FILETIME ftCurrent;
  735. if (pftNow==NULL)
  736. GetSystemTimeAsFileTime(&ftCurrent);
  737. else
  738. ftCurrent = *pftNow;
  739. CCookie **previous = & pLocation->_pCookieKids;
  740. CCookie *pCookie = pLocation->_pCookieKids;
  741. while (pCookie)
  742. {
  743. /* Session cookies do not expire so we only check persistent cookies */
  744. if ((pCookie->_dwFlags & COOKIE_SESSION) == 0)
  745. {
  746. /* "CompareFileTime" macro returns {+1, 0, -1} similar to "strcmp" */
  747. int cmpresult = CompareFileTime(ftCurrent, pCookie->_ftExpiry);
  748. if (cmpresult==1) /* Cookie has expired: remove from linked list & delete */
  749. {
  750. *previous = pCookie->_pCookieNext;
  751. delete pCookie;
  752. pCookie = *previous;
  753. continue;
  754. }
  755. }
  756. /* Otherwise cookie is still valid: advance to next node */
  757. previous = & (pCookie->_pCookieNext);
  758. pCookie = pCookie->_pCookieNext;
  759. }
  760. }
  761. CCookieLocation*
  762. CCookieJar::GetCookies(const char *pchRDomain, const char *pchPath, CCookieLocation *pLast, FILETIME *ftCurrentTime) {
  763. for (CCookieLocation *pLocation = pLast ? pLast->_pLocationNext : *GetBucket(pchRDomain);
  764. pLocation;
  765. pLocation = pLocation->_pLocationNext)
  766. {
  767. if (pLocation->IsMatch(pchRDomain, pchPath))
  768. {
  769. /* Found matching cookies...
  770. Before returning linked list to the user, check expiration
  771. times, deleting cookies which are no longer valid */
  772. expireCookies(pLocation, ftCurrentTime);
  773. return pLocation;
  774. }
  775. }
  776. /* Reaching this point means no matching cookies were found */
  777. return NULL;
  778. }
  779. void
  780. CCookieJar::Purge()
  781. {
  782. // NOT IMPLEMENTED
  783. }
  784. struct PARSE
  785. {
  786. char *pchBuffer;
  787. char *pchToken;
  788. BOOL fEqualFound;
  789. };
  790. static char *
  791. SkipWS(char *pch)
  792. {
  793. while (*pch == ' ' || *pch == '\t')
  794. pch += 1;
  795. return pch;
  796. }
  797. static BOOL
  798. ParseToken(PARSE *pParse, BOOL fBreakOnSpecialTokens, BOOL fBreakOnEqual)
  799. {
  800. char ch;
  801. char *pch;
  802. char *pchEndToken;
  803. pParse->fEqualFound = FALSE;
  804. pch = SkipWS(pParse->pchBuffer);
  805. if (*pch == 0)
  806. {
  807. pParse->pchToken = pch;
  808. return FALSE;
  809. }
  810. pParse->pchToken = pch;
  811. pchEndToken = pch;
  812. while ((ch = *pch) != 0)
  813. {
  814. pch += 1;
  815. if (ch == ';')
  816. {
  817. break;
  818. }
  819. else if (fBreakOnEqual && ch == '=')
  820. {
  821. pParse->fEqualFound = TRUE;
  822. break;
  823. }
  824. else if (ch == ' ' || ch == '\t')
  825. {
  826. if (fBreakOnSpecialTokens)
  827. {
  828. if ((strnicmp(pch, "expires", sizeof("expires") - 1) == 0) ||
  829. (strnicmp(pch, "path", sizeof("path") - 1) == 0) ||
  830. (strnicmp(pch, "domain", sizeof("domain") - 1) == 0) ||
  831. (strnicmp(pch, "secure", sizeof("secure") - 1) == 0))
  832. {
  833. break;
  834. }
  835. }
  836. }
  837. else
  838. {
  839. pchEndToken = pch;
  840. }
  841. }
  842. *pchEndToken = 0;
  843. pParse->pchBuffer = pch;
  844. return TRUE;
  845. }
  846. static void
  847. ParseHeader(
  848. char *pchHeader,
  849. CookieInfo *pCookie
  850. )
  851. {
  852. char **ppchName = & (pCookie->pchName);
  853. char **ppchValue = & (pCookie->pchValue);
  854. char **ppchPath = & (pCookie->pchPath);
  855. char **ppchRDomain = & (pCookie->pchRDomain);
  856. PARSE parse;
  857. parse.pchBuffer = pchHeader;
  858. *ppchName = NULL;
  859. *ppchValue = NULL;
  860. *ppchPath = NULL;
  861. *ppchRDomain = NULL;
  862. pCookie->dwFlags = COOKIE_SESSION;
  863. // If only one of name or value is specified, Navigator
  864. // uses name=<blank> and value as what ever was specified.
  865. // Example: =foo -> name: <blank> value: foo
  866. // foo -> name: <blank> value: foo
  867. // foo= -> name: foo value: <blank>
  868. if (ParseToken(&parse, FALSE, TRUE))
  869. {
  870. *ppchName = parse.pchToken;
  871. if (parse.fEqualFound)
  872. {
  873. if (ParseToken(&parse, FALSE, FALSE))
  874. {
  875. *ppchValue = parse.pchToken;
  876. }
  877. else
  878. {
  879. *ppchValue = s_achEmpty;
  880. }
  881. }
  882. else
  883. {
  884. *ppchValue = *ppchName;
  885. *ppchName = s_achEmpty;
  886. }
  887. }
  888. while (ParseToken(&parse, FALSE, TRUE))
  889. {
  890. if (stricmp(parse.pchToken, "expires") == 0)
  891. {
  892. if (parse.fEqualFound && ParseToken(&parse, TRUE, FALSE))
  893. {
  894. // WinHttpX treats persistent cookies as session cookies with expiration
  895. if (FParseHttpDate(& pCookie->ftExpiration, parse.pchToken))
  896. {
  897. // Don't make the cookie persistent if the parsing fails
  898. pCookie->dwFlags &= ~COOKIE_SESSION;
  899. }
  900. }
  901. }
  902. else if (stricmp(parse.pchToken, "domain") == 0)
  903. {
  904. if (parse.fEqualFound )
  905. {
  906. if( ParseToken(&parse, TRUE, FALSE))
  907. {
  908. // Previous versions of IE tossed the leading
  909. // "." on domain names. We continue this behavior
  910. // to maintain compatiblity with old cookie files.
  911. // See comments at the top of this file for more
  912. // information.
  913. if (*parse.pchToken == '.') parse.pchToken += 1;
  914. ReverseString(parse.pchToken);
  915. *ppchRDomain = parse.pchToken;
  916. }
  917. else
  918. {
  919. *ppchRDomain = parse.pchToken;
  920. }
  921. }
  922. }
  923. else if (stricmp(parse.pchToken, "path") == 0)
  924. {
  925. if (parse.fEqualFound && ParseToken(&parse, TRUE, FALSE))
  926. {
  927. *ppchPath = parse.pchToken;
  928. }
  929. else
  930. {
  931. *ppchPath = s_achEmpty;
  932. }
  933. }
  934. else if (stricmp(parse.pchToken, "secure") == 0)
  935. {
  936. pCookie->dwFlags |= COOKIE_SECURE;
  937. if (parse.fEqualFound)
  938. {
  939. ParseToken(&parse, TRUE, FALSE);
  940. }
  941. }
  942. else
  943. {
  944. if (parse.fEqualFound)
  945. {
  946. ParseToken(&parse, TRUE, FALSE);
  947. }
  948. }
  949. }
  950. if (!*ppchName)
  951. {
  952. *ppchName = *ppchValue = s_achEmpty;
  953. }
  954. }
  955. // free's an INTERNET_COOKIE structure
  956. static VOID
  957. DestroyInternetCookie(INTERNET_COOKIE *pic)
  958. {
  959. if ( pic != NULL )
  960. {
  961. if ( pic->pszDomain ) {
  962. FREE_MEMORY(pic->pszDomain);
  963. }
  964. if ( pic->pszPath ) {
  965. FREE_MEMORY(pic->pszPath);
  966. }
  967. if ( pic->pszName ) {
  968. FREE_MEMORY(pic->pszName);
  969. }
  970. if ( pic->pszData ) {
  971. FREE_MEMORY(pic->pszData);
  972. }
  973. if ( pic->pszUrl ) {
  974. FREE_MEMORY(pic->pszUrl);
  975. }
  976. if( pic->pftExpires ) {
  977. delete pic->pftExpires;
  978. pic->pftExpires = NULL;
  979. }
  980. FREE_MEMORY(pic);
  981. }
  982. }
  983. // allocate's an INTERNET_COOKIE structure
  984. static INTERNET_COOKIE *
  985. MakeInternetCookie(
  986. const char *pchURL,
  987. char *pchRDomain,
  988. char *pchPath,
  989. char *pchName,
  990. char *pchValue,
  991. DWORD dwFlags,
  992. FILETIME ftExpire
  993. )
  994. {
  995. INTERNET_COOKIE *pic = NULL;
  996. pic = (INTERNET_COOKIE *) ALLOCATE_MEMORY(LMEM_ZEROINIT, sizeof(INTERNET_COOKIE));
  997. if ( pic == NULL ) {
  998. return NULL;
  999. }
  1000. pic->cbSize = sizeof(INTERNET_COOKIE);
  1001. pic->pszDomain = pchRDomain ? NewString(pchRDomain) : NULL;
  1002. if (pic->pszDomain) {
  1003. ReverseString(pic->pszDomain);
  1004. }
  1005. pic->pszPath = pchPath ? NewString(pchPath) : NULL;
  1006. pic->pszName = pchName ? NewString(pchName) : NULL;
  1007. pic->pszData = pchValue ? NewString(pchValue) : NULL;
  1008. pic->pszUrl = pchURL ? NewString(pchURL) : NULL;
  1009. #if COOKIE_SECURE != INTERNET_COOKIE_IS_SECURE
  1010. #error MakeInternetCookie depends on cookie flags to remain the same
  1011. #endif
  1012. pic->dwFlags = dwFlags;
  1013. if( dwFlags & COOKIE_SESSION )
  1014. {
  1015. pic->pftExpires = NULL;
  1016. }
  1017. else
  1018. {
  1019. pic->pftExpires = New FILETIME;
  1020. if( pic->pftExpires )
  1021. {
  1022. memcpy(pic->pftExpires, &ftExpire, sizeof(FILETIME));
  1023. }
  1024. }
  1025. return pic;
  1026. }
  1027. DWORD
  1028. CCookieJar::SetCookie(HTTP_REQUEST_HANDLE_OBJECT *pRequest, const char *pchURL, char *pchHeader, DWORD dwFlags = 0)
  1029. {
  1030. char *pchDocumentRDomain = NULL;
  1031. char *pchDocumentPath = NULL;
  1032. BOOL fDocumentSecure;
  1033. BOOL fDelete;
  1034. DWORD dwRet = SET_COOKIE_FAIL;
  1035. CCookieLocation *pLocation;
  1036. CookieInfo cookieStats;
  1037. ParseHeader(pchHeader, &cookieStats);
  1038. char *pchName = cookieStats.pchName;
  1039. char *pchValue = cookieStats.pchValue;
  1040. char *pchHeaderPath = cookieStats.pchPath;
  1041. char *pchHeaderRDomain = cookieStats.pchRDomain;
  1042. DWORD dwFlagsFromParse = cookieStats.dwFlags;
  1043. // merge flags given with those found by the parser.
  1044. dwFlags |= dwFlagsFromParse;
  1045. if (!PathAndRDomainFromURL(pchURL, &pchDocumentRDomain, &pchDocumentPath, &fDocumentSecure))
  1046. goto Cleanup;
  1047. //
  1048. // Verify domain and path
  1049. //
  1050. if ((pchHeaderRDomain && !IsDomainLegal(pchHeaderRDomain, pchDocumentRDomain)) ||
  1051. (pchHeaderPath && !IsPathLegal(pchHeaderPath, pchDocumentPath)))
  1052. {
  1053. SetLastError(ERROR_INVALID_PARAMETER);
  1054. goto Cleanup;
  1055. }
  1056. if (!pchHeaderRDomain)
  1057. pchHeaderRDomain = pchDocumentRDomain;
  1058. if (!pchHeaderPath)
  1059. pchHeaderPath = pchDocumentPath;
  1060. // We need to discard any extra info (i.e. query strings and fragments)
  1061. // from the url.
  1062. if (pchHeaderPath)
  1063. {
  1064. PTSTR psz = pchHeaderPath;
  1065. while (*psz)
  1066. {
  1067. if (*psz==TEXT('?') || *psz==TEXT('#'))
  1068. {
  1069. *psz = TEXT('\0');
  1070. break;
  1071. }
  1072. psz++;
  1073. }
  1074. }
  1075. // WinHttpX treats persistent cookies as session cookies, subject
  1076. // to the expiration rules
  1077. // Also it does not implement zone policies set by URLMON
  1078. //
  1079. // Finally, we can add the cookie!
  1080. //
  1081. {
  1082. if (_csCookieJar.Lock())
  1083. {
  1084. pLocation = GetLocation(pchHeaderRDomain, pchHeaderPath, TRUE);
  1085. if (pLocation)
  1086. {
  1087. CCookie *pCookie;
  1088. pCookie = pLocation->GetCookie(pchName, TRUE);
  1089. if (!pCookie)
  1090. goto Cleanup;
  1091. //
  1092. // If the cookie's value or flags have changed, update it.
  1093. //
  1094. if (strcmp(pchValue, pCookie->_pchValue) || dwFlags != pCookie->_dwFlags)
  1095. {
  1096. pCookie->_dwFlags = dwFlags;
  1097. pCookie->_ftExpiry = cookieStats.ftExpiration;
  1098. pCookie->SetValue(pchValue);
  1099. }
  1100. }
  1101. _csCookieJar.Unlock();
  1102. }
  1103. }
  1104. dwRet = SET_COOKIE_SUCCESS;
  1105. Cleanup:
  1106. if (pchDocumentRDomain)
  1107. FREE_MEMORY(pchDocumentRDomain);
  1108. if (pchDocumentPath)
  1109. FREE_MEMORY(pchDocumentPath);
  1110. return dwRet;
  1111. }
  1112. //---------------------------------------------------------------------------
  1113. //
  1114. // External APIs
  1115. //
  1116. //---------------------------------------------------------------------------
  1117. CCookieJar *
  1118. CreateCookieJar()
  1119. {
  1120. return CCookieJar::Construct();
  1121. }
  1122. void
  1123. CloseCookieJar(CCookieJar * CookieJar)
  1124. {
  1125. if (CookieJar)
  1126. {
  1127. delete CookieJar;
  1128. }
  1129. }
  1130. #ifndef WININET_SERVER_CORE
  1131. void
  1132. PurgeCookieJar()
  1133. {
  1134. }
  1135. #endif
  1136. //
  1137. // rambling comments, delete before checkin...
  1138. //
  1139. // returns struc, and pending, error
  1140. // on subsequent attempts passes back, with index, or index incremented
  1141. // perhaps can store index in fsm, and the rest in UI
  1142. // need to handle multi dlgs, perhaps via checking added Cookie.
  1143. //
  1144. DWORD
  1145. HTTP_REQUEST_HANDLE_OBJECT::ExtractSetCookieHeaders(LPDWORD lpdwHeaderIndex)
  1146. {
  1147. char *pchHeader = NULL;
  1148. DWORD cbHeader;
  1149. DWORD iQuery = 0;
  1150. int cCookies = 0;
  1151. DWORD error = ERROR_WINHTTP_HEADER_NOT_FOUND;
  1152. const DWORD cbHeaderInit = CCH_COOKIE_MAX * sizeof(char) - 1;
  1153. pchHeader = New char[CCH_COOKIE_MAX];
  1154. if (pchHeader == NULL || !_ResponseHeaders.LockHeaders())
  1155. {
  1156. error = ERROR_NOT_ENOUGH_MEMORY;
  1157. goto Cleanup;
  1158. }
  1159. INET_ASSERT(lpdwHeaderIndex);
  1160. cbHeader = cbHeaderInit;
  1161. iQuery = *lpdwHeaderIndex;
  1162. while (QueryResponseHeader(HTTP_QUERY_SET_COOKIE,
  1163. pchHeader,
  1164. &cbHeader,
  1165. 0,
  1166. &iQuery) == ERROR_SUCCESS)
  1167. {
  1168. pchHeader[cbHeader] = 0;
  1169. INTERNET_HANDLE_OBJECT *pRoot = GetRootHandle (this);
  1170. CCookieJar* pcj = pRoot->_CookieJar;
  1171. DWORD dwRet = pcj->SetCookie(this, GetURL(), pchHeader);
  1172. if (dwRet == SET_COOKIE_SUCCESS)
  1173. {
  1174. cCookies += 1;
  1175. *lpdwHeaderIndex = iQuery;
  1176. error = ERROR_SUCCESS;
  1177. }
  1178. else if (dwRet == SET_COOKIE_PENDING)
  1179. {
  1180. error = ERROR_IO_PENDING;
  1181. INET_ASSERT(iQuery != 0);
  1182. *lpdwHeaderIndex = iQuery - 1; // back up and retry this cookie
  1183. break;
  1184. }
  1185. cbHeader = cbHeaderInit;
  1186. }
  1187. _ResponseHeaders.UnlockHeaders();
  1188. Cleanup:
  1189. if (pchHeader)
  1190. delete [] pchHeader;
  1191. return error;
  1192. }
  1193. int
  1194. HTTP_REQUEST_HANDLE_OBJECT::CreateCookieHeaderIfNeeded(VOID)
  1195. {
  1196. int cCookie = 0;
  1197. char * pchRDomain = NULL;
  1198. char * pchPath = NULL;
  1199. BOOL fSecure;
  1200. DWORD cch;
  1201. int cchName;
  1202. int cchValue;
  1203. char * pchHeader = NULL;
  1204. char * pchHeaderStart = NULL;
  1205. pchHeaderStart = (char *) ALLOCATE_FIXED_MEMORY(CCH_COOKIE_MAX * sizeof(char));
  1206. if (pchHeaderStart == NULL)
  1207. goto Cleanup;
  1208. // remove cookie header if it exists
  1209. // BUGBUG - we are overriding the app. Original cookie code has this. Don't know why.
  1210. ReplaceRequestHeader(HTTP_QUERY_COOKIE, NULL, 0, 0, 0);
  1211. if (!PathAndRDomainFromURL(GetURL(), &pchRDomain, &pchPath, &fSecure, FALSE))
  1212. goto Cleanup;
  1213. fSecure = GetOpenFlags() & WINHTTP_FLAG_SECURE;
  1214. if (LockHeaders())
  1215. {
  1216. INTERNET_HANDLE_OBJECT *pRoot = GetRootHandle (this);
  1217. CCookieJar* pcj = pRoot->_CookieJar;
  1218. if (pcj->_csCookieJar.Lock())
  1219. {
  1220. FILETIME ftNow;
  1221. GetSystemTimeAsFileTime(&ftNow);
  1222. CCookieLocation *pLocation = pcj->GetCookies(pchRDomain, pchPath, NULL, &ftNow);
  1223. while (pLocation)
  1224. {
  1225. for (CCookie *pCookie = pLocation->_pCookieKids; pCookie; pCookie = pCookie->_pCookieNext)
  1226. {
  1227. if (pCookie->CanSend(fSecure))
  1228. {
  1229. pchHeader = pchHeaderStart;
  1230. cch = 0;
  1231. cch += cchName = strlen(pCookie->_pchName);
  1232. cch += cchValue = strlen(pCookie->_pchValue);
  1233. if (cchName) cch += 1; // for equal sign
  1234. if (cch < CCH_COOKIE_MAX)
  1235. {
  1236. if (cchName > 0)
  1237. {
  1238. memcpy(pchHeader, pCookie->_pchName, cchName);
  1239. pchHeader += cchName;
  1240. *pchHeader++ = '=';
  1241. }
  1242. if (cchValue > 0)
  1243. {
  1244. memcpy(pchHeader, pCookie->_pchValue, cchValue);
  1245. pchHeader += cchValue;
  1246. }
  1247. cCookie += 1;
  1248. AddRequestHeader(HTTP_QUERY_COOKIE,
  1249. pchHeaderStart,
  1250. cch,
  1251. 0,
  1252. HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON);
  1253. }
  1254. } // if (CanSend)
  1255. } // for (pCookie)
  1256. pLocation = pcj->GetCookies(pchRDomain, pchPath, pLocation, &ftNow);
  1257. } // while (pLocation)
  1258. pcj->_csCookieJar.Unlock();
  1259. } // if pcj->_csCookieJar.Lock()
  1260. UnlockHeaders();
  1261. }
  1262. Cleanup:
  1263. if (pchHeaderStart)
  1264. FREE_MEMORY(pchHeaderStart);
  1265. if (pchRDomain)
  1266. FREE_MEMORY(pchRDomain);
  1267. if (pchPath)
  1268. FREE_MEMORY(pchPath);
  1269. return cCookie;
  1270. }