工具项目

HttpCookie.cpp 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Author : Bruce Liang
  5. * Website : http://www.jessma.org
  6. * Project : https://github.com/ldcsaa
  7. * Blog : http://www.cnblogs.com/ldcsaa
  8. * Wiki : http://www.oschina.net/p/hp-socket
  9. * QQ Group : 75375912, 44636872
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "License");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. */
  23. #include "stdafx.h"
  24. #include "HttpCookie.h"
  25. #ifdef _HTTP_SUPPORT
  26. #pragma warning(disable: 4503)
  27. static const char* s_short_week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  28. static const char* s_short_month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  29. CCookieMgr g_CookieMgr;
  30. CCookie* CCookie::FromString(const CStringA& strCookie, LPCSTR lpszDefaultDomain, LPCSTR lpszDefaultPath)
  31. {
  32. CStringA strName;
  33. CStringA strValue;
  34. CStringA strDomain;
  35. CStringA strPath;
  36. int iMaxAge = -1;
  37. BOOL bHttpOnly = FALSE;
  38. BOOL bSecure = FALSE;
  39. EnSameSite enSameSite = SS_NONE;
  40. int i = 0;
  41. int iStart = 0;
  42. while(TRUE)
  43. {
  44. CStringA strField = strCookie.Tokenize(COOKIE_FIELD_SEP, iStart);
  45. strField.Trim();
  46. if(strField.IsEmpty())
  47. break;
  48. if(i == 0)
  49. {
  50. ParseFieldKV(strField, strName, strValue, COOKIE_KV_SEP_CHAR);
  51. if(strName.IsEmpty())
  52. return nullptr;
  53. }
  54. else
  55. {
  56. CStringA strKey;
  57. CStringA strVal;
  58. ParseFieldKV(strField, strKey, strVal, COOKIE_KV_SEP_CHAR);
  59. if(strKey.CompareNoCase(COOKIE_DOMAIN) == 0)
  60. strDomain = strVal;
  61. else if(strKey.CompareNoCase(COOKIE_PATH) == 0)
  62. strPath = strVal;
  63. else if(strKey.CompareNoCase(COOKIE_MAX_AGE) == 0 && !strVal.IsEmpty())
  64. iMaxAge = atoi(strVal);
  65. else if(strKey.CompareNoCase(COOKIE_EXPIRES) == 0 && !strVal.IsEmpty() && iMaxAge == -1)
  66. {
  67. __time64_t tmExpires = -1;
  68. if(!ParseExpires(strVal, tmExpires))
  69. return nullptr;
  70. iMaxAge = ExpiresToMaxAge(tmExpires);
  71. }
  72. else if(strKey.CompareNoCase(COOKIE_HTTPONLY) == 0)
  73. bHttpOnly = TRUE;
  74. else if(strKey.CompareNoCase(COOKIE_SECURE) == 0)
  75. bSecure = TRUE;
  76. else if(strKey.CompareNoCase(COOKIE_SAMESITE) == 0)
  77. {
  78. if(strVal.IsEmpty() || strVal.CompareNoCase(COOKIE_SAMESITE_STRICT) == 0)
  79. enSameSite = SS_STRICT;
  80. else if(strVal.CompareNoCase(COOKIE_SAMESITE_LAX) == 0)
  81. enSameSite = SS_LAX;
  82. }
  83. }
  84. ++i;
  85. }
  86. if(!AdjustDomain(strDomain, lpszDefaultDomain) || !AdjustPath(strPath, lpszDefaultPath))
  87. return nullptr;
  88. CCookie* pCookie = new CCookie(strName, strValue, strDomain, strPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
  89. ASSERT(pCookie->IsValid());
  90. return pCookie;
  91. }
  92. CStringA CCookie::ToString(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, EnSameSite enSameSite)
  93. {
  94. CCookie cookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
  95. return cookie.ToString();
  96. }
  97. BOOL CCookie::ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, EnSameSite enSameSite)
  98. {
  99. BOOL isOK = FALSE;
  100. CStringA str = ToString(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
  101. int iLength = str.GetLength() + 1;
  102. if(lpszBuff && iBuffLen >= iLength)
  103. {
  104. memcpy(lpszBuff, (LPCSTR)str, iLength * sizeof(char));
  105. isOK = TRUE;
  106. }
  107. iBuffLen = iLength;
  108. return isOK;
  109. }
  110. CStringA CCookie::ToString()
  111. {
  112. ASSERT(!name.IsEmpty());
  113. CStringA strCookie;
  114. strCookie.AppendFormat("%s=%s", name, value);
  115. if(!domain.IsEmpty())
  116. strCookie.AppendFormat("; %s=%s", COOKIE_DOMAIN, domain);
  117. if(!path.IsEmpty())
  118. strCookie.AppendFormat("; %s=%s", COOKIE_PATH, path);
  119. if(expires >= 0)
  120. strCookie.AppendFormat("; %s=%s", COOKIE_EXPIRES, MakeExpiresStr(expires));
  121. if(httpOnly)
  122. strCookie.AppendFormat("; %s", COOKIE_HTTPONLY);
  123. if(secure)
  124. strCookie.AppendFormat("; %s", COOKIE_SECURE);
  125. if(sameSite != SS_NONE)
  126. strCookie.AppendFormat("; %s=%s", COOKIE_SAMESITE, sameSite == SS_LAX ? COOKIE_SAMESITE_LAX : COOKIE_SAMESITE_STRICT);
  127. return strCookie;
  128. }
  129. BOOL CCookie::Match(LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
  130. {
  131. int iLen = (int)strlen(lpszDomain);
  132. int iDiff = iLen - domain.GetLength();
  133. if(iDiff < 0 || _stricmp(lpszDomain + iDiff, domain) != 0)
  134. return FALSE;
  135. if(iDiff > 0 && *(lpszDomain + iDiff - 1) != COOKIE_DOMAIN_SEP_CHAR)
  136. return FALSE;
  137. if(strncmp(lpszPath, path, path.GetLength()) != 0)
  138. return FALSE;
  139. return (bHttp || !httpOnly) && (bSecure || !secure);
  140. }
  141. BOOL CCookie::IsSameDomain(LPCSTR lpszDomain)
  142. {
  143. int iLen = (int)strlen(lpszDomain);
  144. int iDiff = iLen - domain.GetLength();
  145. LPCSTR lpszLong, lpszShort;
  146. if(iDiff < 0)
  147. {
  148. lpszLong = (LPCSTR)domain + iDiff;
  149. lpszShort = lpszDomain;
  150. }
  151. else
  152. {
  153. lpszLong = lpszDomain + iDiff;
  154. lpszShort = (LPCSTR)domain;
  155. }
  156. if(_stricmp(lpszLong, lpszShort) != 0)
  157. return FALSE;
  158. if(iDiff != 0 && *(lpszLong - 1) != COOKIE_DOMAIN_SEP_CHAR)
  159. return FALSE;
  160. return TRUE;
  161. }
  162. void CCookie::ParseFieldKV(const CStringA& strField, CStringA& strKey, CStringA& strVal, char chSep)
  163. {
  164. int i = strField.Find(chSep);
  165. if(i < 0)
  166. strKey = strField;
  167. else
  168. {
  169. strKey = strField.Left(i);
  170. strVal = strField.Mid(i + 1);
  171. strVal.Trim();
  172. }
  173. strKey.Trim();
  174. }
  175. BOOL CCookie::ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires)
  176. {
  177. int iLength = (int)strlen(lpszExpires);
  178. if(iLength == 0 || iLength > 50)
  179. return FALSE;
  180. char szMonth[10];
  181. char szZone[10];
  182. tm t = {0};
  183. if(sscanf_s(lpszExpires, "%*[^, ]%*[, ]%2d%*[-/ ]%[^-/ ]%*[-/ ]%4d %2d:%2d:%2d %s",
  184. &t.tm_mday, szMonth, (UINT)_countof(szMonth), &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec, szZone, (UINT)_countof(szZone)) != 7)
  185. return FALSE;
  186. if(t.tm_year < 70)
  187. t.tm_year += 100;
  188. else if (t.tm_year > 100)
  189. t.tm_year -= 1900;
  190. int i = 0;
  191. int size = _countof(s_short_month);
  192. for(; i < size; i++)
  193. {
  194. if(_strnicmp(szMonth, s_short_month[i], 3) == 0)
  195. break;
  196. }
  197. if(i == size)
  198. return FALSE;
  199. t.tm_mon = i;
  200. CStringA strZone = szZone;
  201. int iZone = 0;
  202. int iMix = 0;
  203. int iPos = strZone.Find('+');
  204. if(iPos >= 0)
  205. iMix = 1;
  206. else
  207. {
  208. iPos = strZone.Find('-');
  209. if(iPos >= 0)
  210. iMix = -1;
  211. }
  212. if(iPos >= 0)
  213. {
  214. strZone = strZone.Mid(iPos + 1);
  215. strZone.Remove(':');
  216. int val = atoi(strZone);
  217. if(val > 0)
  218. {
  219. int minutes = val % 100;
  220. int hours = val / 100;
  221. iZone = iMix * (minutes * 60 + hours * 3600);
  222. }
  223. }
  224. tmExpires = GetUTCTime(t, iZone);
  225. return tmExpires >= 0;
  226. }
  227. BOOL CCookie::AdjustDomain(CStringA& strDomain, LPCSTR lpszDefaultDomain)
  228. {
  229. if(strDomain.IsEmpty() && lpszDefaultDomain)
  230. strDomain = lpszDefaultDomain;
  231. strDomain.TrimLeft(COOKIE_DOMAIN_SEP_CHAR).MakeLower();
  232. return !strDomain.IsEmpty();
  233. }
  234. BOOL CCookie::AdjustPath(CStringA& strPath, LPCSTR lpszDefaultPath)
  235. {
  236. if(strPath.IsEmpty() && lpszDefaultPath)
  237. strPath = lpszDefaultPath;
  238. int iLength = strPath.GetLength();
  239. if(iLength == 0)
  240. return FALSE;
  241. if(strPath.GetAt(iLength - 1) != COOKIE_PATH_SEP_CHAR)
  242. {
  243. int iPos = strPath.ReverseFind(COOKIE_PATH_SEP_CHAR);
  244. if(iPos >= 0)
  245. strPath = strPath.Left(iPos + 1);
  246. else
  247. strPath.Empty();
  248. }
  249. if(!strPath.IsEmpty() && strPath.GetAt(0) != COOKIE_PATH_SEP_CHAR)
  250. strPath.Insert(0, COOKIE_PATH_SEP_CHAR);
  251. return !strPath.IsEmpty();
  252. }
  253. __time64_t CCookie::GetUTCTime(tm& t, int iSecondOffsetTZ)
  254. {
  255. __time64_t v = _mkgmtime64(&t);
  256. if(v >= 0) v -= iSecondOffsetTZ;
  257. return v;
  258. }
  259. CStringA CCookie::MakeExpiresStr(__time64_t tmExpires)
  260. {
  261. ASSERT( tmExpires >= 0);
  262. if(tmExpires < 1) tmExpires = 1;
  263. tm t;
  264. VERIFY(_gmtime64_s(&t, &tmExpires) == 0);
  265. CStringA str;
  266. str.Format("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
  267. s_short_week[t.tm_wday], t.tm_mday, s_short_month[t.tm_mon], t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
  268. return str;
  269. }
  270. BOOL CCookie::MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires)
  271. {
  272. BOOL isOK = FALSE;
  273. CStringA str = MakeExpiresStr(tmExpires);
  274. int iLength = str.GetLength() + 1;
  275. if(lpszBuff && iBuffLen >= iLength)
  276. {
  277. memcpy(lpszBuff, (LPCSTR)str, iLength * sizeof(char));
  278. isOK = TRUE;
  279. }
  280. iBuffLen = iLength;
  281. return isOK;
  282. }
  283. // ------------------------------------------------------------------------------------------------------------- //
  284. BOOL CCookieMgr::LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists)
  285. {
  286. BOOL isOK = FALSE;
  287. FILE* pFile = nullptr;
  288. if(fopen_s(&pFile, lpszFile, "r") != NO_ERROR)
  289. {
  290. ::SetLastError(errno == ENOENT ? ERROR_FILE_NOT_FOUND : (errno == EACCES ? ERROR_ACCESS_DENIED : ERROR_OPEN_FAILED));
  291. goto _ERROR_END;
  292. }
  293. {
  294. CStringA strDomain;
  295. CStringA strPath;
  296. CCookie cookie;
  297. char szBuffer[8192];
  298. int iBufferSize = _countof(szBuffer);
  299. __time64_t tmCurrent = _time64(nullptr);
  300. CCookieSet* pCookieSet = nullptr;
  301. CWriteLock locallock(m_cs);
  302. if(!bKeepExists)
  303. ClearDomainCookiesNoLock();
  304. while(fgets(szBuffer, iBufferSize, pFile) != nullptr)
  305. {
  306. char c = szBuffer[0];
  307. if(c == '\n' || c == '\r')
  308. continue;
  309. else if(c != '\t')
  310. {
  311. if(!LoadDomainAndPath(szBuffer, strDomain, strPath))
  312. goto _ERROR_END;
  313. pCookieSet = GetCookieSetNoLock(strDomain, strPath);
  314. }
  315. else
  316. {
  317. if(!LoadCookie(szBuffer, strDomain, strPath, cookie))
  318. goto _ERROR_END;
  319. if(cookie.expires <= tmCurrent)
  320. continue;
  321. if(pCookieSet)
  322. {
  323. if(bKeepExists)
  324. {
  325. CCookieSetCI it = pCookieSet->find(cookie);
  326. if(it != pCookieSet->end())
  327. continue;
  328. }
  329. pCookieSet->emplace(move(cookie));
  330. }
  331. else
  332. {
  333. SetCookieNoLock(cookie, FALSE);
  334. pCookieSet = GetCookieSetNoLock(strDomain, strPath);
  335. }
  336. }
  337. }
  338. if(!feof(pFile))
  339. {
  340. ::SetLastError(ERROR_READ_FAULT);
  341. goto _ERROR_END;
  342. }
  343. }
  344. isOK = TRUE;
  345. _ERROR_END:
  346. if(pFile) fclose(pFile);
  347. return isOK;
  348. }
  349. BOOL CCookieMgr::SaveToFile(LPCSTR lpszFile, BOOL bKeepExists)
  350. {
  351. if(bKeepExists)
  352. {
  353. if(!LoadFromFile(lpszFile, TRUE) && ::GetLastError() != ERROR_FILE_NOT_FOUND)
  354. return FALSE;
  355. }
  356. BOOL isOK = FALSE;
  357. FILE* pFile = nullptr;
  358. if(fopen_s(&pFile, lpszFile, "w") != NO_ERROR)
  359. {
  360. ::SetLastError(errno == ENOENT ? ERROR_FILE_NOT_FOUND : (errno == EACCES ? ERROR_ACCESS_DENIED : ERROR_OPEN_FAILED));
  361. goto _ERROR_END;
  362. }
  363. {
  364. __time64_t tmCurrent = _time64(nullptr);
  365. CReadLock locallock(m_cs);
  366. for(CCookieDomainMapCI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
  367. {
  368. const CStringA& strDomain = it->first;
  369. const CCookiePathMap& paths = it->second;
  370. for(CCookiePathMapCI it2 = paths.begin(), end2 = paths.end(); it2 != end2; ++it2)
  371. {
  372. const CStringA& strPath = it2->first;
  373. const CCookieSet& cookies = it2->second;
  374. if(fprintf_s(pFile, "%s %s\n", (LPCSTR)strDomain, (LPCSTR)strPath) < 0)
  375. {
  376. ::SetLastError(ERROR_WRITE_FAULT);
  377. goto _ERROR_END;
  378. }
  379. for(CCookieSetCI it3 = cookies.begin(), end3 = cookies.end(); it3 != end3; ++it3)
  380. {
  381. const CCookie& cookie = *it3;
  382. if(cookie.expires <= tmCurrent)
  383. continue;
  384. LPCSTR lpszValue = (LPCSTR)cookie.value;
  385. if(lpszValue[0] == 0)
  386. lpszValue = " ";
  387. if(fprintf_s(pFile, "\t%s;%s;%I64d;%d;%d;%d\n", (LPCSTR)cookie.name, lpszValue, cookie.expires, cookie.httpOnly, cookie.secure, cookie.sameSite) < 0)
  388. {
  389. ::SetLastError(ERROR_WRITE_FAULT);
  390. goto _ERROR_END;
  391. }
  392. }
  393. }
  394. }
  395. }
  396. isOK = TRUE;
  397. _ERROR_END:
  398. if(pFile) fclose(pFile);
  399. return isOK;
  400. }
  401. BOOL CCookieMgr::ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
  402. {
  403. CStringA strDomain;
  404. CStringA strPath;
  405. if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
  406. return FALSE;
  407. CWriteLock locallock(m_cs);
  408. ClearDomainCookiesNoLock(lpszDomain, lpszPath);
  409. return TRUE;
  410. }
  411. BOOL CCookieMgr::RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
  412. {
  413. CStringA strDomain;
  414. CStringA strPath;
  415. if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
  416. return FALSE;
  417. CWriteLock locallock(m_cs);
  418. RemoveExpiredCookiesNoLock(lpszDomain, lpszPath);
  419. return TRUE;
  420. }
  421. BOOL CCookieMgr::GetCookies(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
  422. {
  423. ASSERT(lpszDomain && lpszPath);
  424. CStringA strDomain;
  425. CStringA strPath;
  426. if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, FALSE))
  427. return FALSE;
  428. list<LPCSTR> lsDomains(1, lpszDomain);
  429. list<CStringA> lsPaths(1, lpszPath);
  430. char c;
  431. LPCSTR lpszTemp = lpszDomain;
  432. while((c = *(++lpszTemp)) != 0)
  433. {
  434. if(c == COOKIE_DOMAIN_SEP_CHAR)
  435. {
  436. if((c = *(++lpszTemp)) != 0)
  437. lsDomains.push_back(lpszTemp);
  438. else
  439. break;
  440. }
  441. }
  442. lpszTemp = lpszPath + strlen(lpszPath) - 1;
  443. while(--lpszTemp >= lpszPath)
  444. {
  445. if((c = *lpszTemp) == COOKIE_PATH_SEP_CHAR)
  446. {
  447. *(LPSTR)(lpszTemp + 1) = 0;
  448. lsPaths.push_back(lpszPath);
  449. }
  450. }
  451. CReadLock locallock(m_cs);
  452. for(list<LPCSTR>::const_iterator it = lsDomains.begin(), end = lsDomains.end(); it != end; ++it)
  453. {
  454. for(list<CStringA>::const_iterator it2 = lsPaths.begin(), end2 = lsPaths.end(); it2 != end2; ++it2)
  455. MatchCookiesNoLock(cookies, *it, *it2, bHttp, bSecure);
  456. }
  457. return TRUE;
  458. }
  459. BOOL CCookieMgr::SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, CCookie::EnSameSite enSameSite, BOOL bOnlyUpdateValueIfExists)
  460. {
  461. CCookie cookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
  462. return SetCookie(cookie, bOnlyUpdateValueIfExists);
  463. }
  464. BOOL CCookieMgr::SetCookie(const CStringA& strCookie, BOOL bOnlyUpdateValueIfExists)
  465. {
  466. unique_ptr<CCookie> pCookie(CCookie::FromString(strCookie));
  467. if(!pCookie) return FALSE;
  468. return SetCookie(*pCookie, bOnlyUpdateValueIfExists);
  469. }
  470. BOOL CCookieMgr::SetCookie(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
  471. {
  472. if(!cookie.IsValid()) return FALSE;
  473. CWriteLock locallock(m_cs);
  474. return SetCookieNoLock(cookie, bOnlyUpdateValueIfExists);
  475. }
  476. BOOL CCookieMgr::DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
  477. {
  478. CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
  479. return DeleteCookie(cookie);
  480. }
  481. BOOL CCookieMgr::DeleteCookie(const CCookie& cookie)
  482. {
  483. if(!cookie.IsValid()) return FALSE;
  484. CWriteLock locallock(m_cs);
  485. return DeleteCookieNoLock(cookie);
  486. }
  487. void CCookieMgr::ClearDomainCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
  488. {
  489. if(!lpszDomain && !lpszPath)
  490. m_cookies.clear();
  491. else if(!lpszPath)
  492. m_cookies.erase(lpszDomain);
  493. else
  494. {
  495. if(!lpszDomain)
  496. {
  497. for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
  498. ClearPathCookiesNoLock(it->second, lpszPath);
  499. }
  500. else
  501. {
  502. CCookieDomainMapI it = m_cookies.find(lpszDomain);
  503. if(it != m_cookies.end())
  504. ClearPathCookiesNoLock(it->second, lpszPath);
  505. }
  506. }
  507. }
  508. void CCookieMgr::ClearPathCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
  509. {
  510. if(!lpszPath)
  511. paths.clear();
  512. else
  513. {
  514. CCookiePathMapI it = paths.find(lpszPath);
  515. if(it != paths.end())
  516. paths.erase(it->first);
  517. }
  518. }
  519. void CCookieMgr::RemoveExpiredCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
  520. {
  521. if(!lpszDomain)
  522. {
  523. for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
  524. RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
  525. }
  526. else
  527. {
  528. CCookieDomainMapI it = m_cookies.find(lpszDomain);
  529. if(it != m_cookies.end())
  530. RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
  531. }
  532. }
  533. void CCookieMgr::RemoveDomainExpiredCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
  534. {
  535. if(!lpszPath)
  536. {
  537. for(CCookiePathMapI it = paths.begin(), end = paths.end(); it != end; ++it)
  538. RemovePathExpiredCookiesNoLock(it->second);
  539. }
  540. else
  541. {
  542. CCookiePathMapI it = paths.find(lpszPath);
  543. if(it != paths.end())
  544. RemovePathExpiredCookiesNoLock(it->second);
  545. }
  546. }
  547. void CCookieMgr::RemovePathExpiredCookiesNoLock(CCookieSet& cookies)
  548. {
  549. CCookiePtrSet ptrs;
  550. for(CCookieSetI it = cookies.begin(), end = cookies.end(); it != end; ++it)
  551. {
  552. if(it->IsExpired())
  553. ptrs.emplace(&*it);
  554. }
  555. if(!ptrs.empty())
  556. {
  557. for(CCookiePtrSetI it = ptrs.begin(), end = ptrs.end(); it != end; ++it)
  558. cookies.erase(**it);
  559. }
  560. }
  561. const CCookie* CCookieMgr::GetCookieNoLock(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
  562. {
  563. const CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
  564. return GetCookieNoLock(cookie);
  565. }
  566. const CCookie* CCookieMgr::GetCookieNoLock(const CCookie& cookie)
  567. {
  568. const CCookie* pCookie = nullptr;
  569. CCookieDomainMapCI it = m_cookies.find(cookie.domain);
  570. if(it != m_cookies.end())
  571. {
  572. CCookiePathMapCI it2 = it->second.find(cookie.path);
  573. if(it2 != it->second.end())
  574. {
  575. CCookieSetCI it3 = it2->second.find(cookie);
  576. if(it3 != it2->second.end())
  577. pCookie = &*it3;
  578. }
  579. }
  580. return pCookie;
  581. }
  582. void CCookieMgr::MatchCookiesNoLock(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
  583. {
  584. CCookieDomainMapCI it = m_cookies.find(lpszDomain);
  585. if(it != m_cookies.end())
  586. {
  587. CCookiePathMapCI it2 = it->second.find(lpszPath);
  588. if(it2 != it->second.end())
  589. {
  590. for(CCookieSetCI it3 = it2->second.begin(), end3 = it2->second.end(); it3 != end3; ++it3)
  591. {
  592. const CCookie& cookie = *it3;
  593. if(!cookie.IsExpired() && (bHttp || !cookie.httpOnly) && (bSecure || !cookie.secure))
  594. cookies.emplace(cookie);
  595. }
  596. }
  597. }
  598. }
  599. BOOL CCookieMgr::SetCookieNoLock(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
  600. {
  601. if(cookie.IsExpired())
  602. return DeleteCookieNoLock(cookie);
  603. CCookieDomainMapI it = m_cookies.find(cookie.domain);
  604. if(it == m_cookies.end())
  605. it = m_cookies.emplace(move(CCookieDomainMap::value_type(cookie.domain, move(CCookiePathMap())))).first;
  606. CCookiePathMapI it2 = it->second.find(cookie.path);
  607. if(it2 == it->second.end())
  608. it2 = it->second.emplace(move(CCookiePathMap::value_type(cookie.path, move(CCookieSet())))).first;
  609. CCookieSet& cookies = it2->second;
  610. CCookieSetI it3 = cookies.find(cookie);
  611. if(it3 != cookies.end())
  612. {
  613. if(bOnlyUpdateValueIfExists && !it3->IsExpired() && cookie.IsTransient())
  614. {
  615. ((CCookie*)&*it3)->value = cookie.value;
  616. return TRUE;
  617. }
  618. cookies.erase(*it3);
  619. }
  620. return cookies.emplace(cookie).second;
  621. }
  622. BOOL CCookieMgr::DeleteCookieNoLock(const CCookie& cookie)
  623. {
  624. BOOL isOK = FALSE;
  625. CCookieDomainMapI it = m_cookies.find(cookie.domain);
  626. if(it != m_cookies.end())
  627. {
  628. CCookiePathMapI it2 = it->second.find(cookie.path);
  629. if(it2 != it->second.end())
  630. {
  631. CCookieSetI it3 = it2->second.find(cookie);
  632. if(it3 != it2->second.end())
  633. {
  634. it2->second.erase(*it3);
  635. isOK = TRUE;
  636. }
  637. }
  638. }
  639. return isOK;
  640. }
  641. CCookieSet* CCookieMgr::GetCookieSetNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
  642. {
  643. CCookieSet* pCookieSet = nullptr;
  644. CCookieDomainMapI it = m_cookies.find(lpszDomain);
  645. if(it != m_cookies.end())
  646. {
  647. CCookiePathMapI it2 = it->second.find(lpszPath);
  648. if(it2 != it->second.end())
  649. pCookieSet = &(it2->second);
  650. }
  651. return pCookieSet;
  652. }
  653. BOOL CCookieMgr::LoadDomainAndPath(LPSTR lpszBuff, CStringA& strDomain, CStringA& strPath)
  654. {
  655. int i = 0;
  656. char* lpszCtx = nullptr;
  657. for(; i < 2; i++)
  658. {
  659. char* lpszToken = strtok_s(lpszBuff, " \n\r", &lpszCtx);
  660. if(!lpszToken)
  661. {
  662. ::SetLastError(ERROR_BAD_FORMAT);
  663. return FALSE;
  664. }
  665. if(i == 0)
  666. {
  667. strDomain = lpszToken;
  668. lpszBuff = nullptr;
  669. }
  670. else
  671. strPath = lpszToken;
  672. }
  673. if(!CCookie::AdjustDomain(strDomain))
  674. return FALSE;
  675. if(!CCookie::AdjustPath(strPath))
  676. return FALSE;
  677. return TRUE;
  678. }
  679. BOOL CCookieMgr::LoadCookie(LPSTR lpszBuff, LPCSTR lpszDomain, LPCSTR lpszPath, CCookie& cookie)
  680. {
  681. cookie.domain = lpszDomain;
  682. cookie.path = lpszPath;
  683. int i = 0;
  684. char* lpszCtx = nullptr;
  685. for(; i < 6; i++)
  686. {
  687. char* lpszToken = strtok_s(lpszBuff, "\t;\n\r", &lpszCtx);
  688. if(!lpszToken)
  689. {
  690. ::SetLastError(ERROR_BAD_FORMAT);
  691. return FALSE;
  692. }
  693. if(i == 0)
  694. {
  695. cookie.name = lpszToken;
  696. lpszBuff = nullptr;
  697. }
  698. else if(i == 1)
  699. cookie.value = lpszToken;
  700. else if(i == 2)
  701. cookie.expires = _atoi64(lpszToken);
  702. else if(i == 3)
  703. cookie.httpOnly = atoi(lpszToken);
  704. else if(i == 4)
  705. cookie.secure = atoi(lpszToken);
  706. else if(i == 5)
  707. cookie.sameSite = (CCookie::EnSameSite)atoi(lpszToken);
  708. }
  709. cookie.name.Trim();
  710. cookie.value.Trim();
  711. if(cookie.name.IsEmpty() || cookie.expires <= 0 || cookie.httpOnly < 0 || cookie.secure < 0 || cookie.sameSite < 0)
  712. {
  713. ::SetLastError(ERROR_INVALID_DATA);
  714. return FALSE;
  715. }
  716. return TRUE;
  717. }
  718. BOOL CCookieMgr::AdjustDomainAndPath(LPCSTR& lpszDomain, LPCSTR& lpszPath, CStringA& strDomain, CStringA& strPath, BOOL bKeepNull)
  719. {
  720. if(!bKeepNull || lpszDomain)
  721. {
  722. strDomain = lpszDomain;
  723. if(!CCookie::AdjustDomain(strDomain))
  724. return FALSE;
  725. lpszDomain = (LPCSTR)strDomain;
  726. }
  727. if(!bKeepNull || lpszPath)
  728. {
  729. strPath = lpszPath;
  730. if(!CCookie::AdjustPath(strPath))
  731. return FALSE;
  732. lpszPath = (LPCSTR)strPath;
  733. }
  734. return TRUE;
  735. }
  736. CCookieMgr::CCookieMgr(BOOL bEnableThirdPartyCookie)
  737. : m_bEnableThirdPartyCookie(bEnableThirdPartyCookie)
  738. {
  739. }
  740. #endif