HttpCookie.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Author : Bruce Liang
  5. * Website : https://github.com/ldcsaa
  6. * Project : https://github.com/ldcsaa/HP-Socket
  7. * Blog : http://www.cnblogs.com/ldcsaa
  8. * Wiki : http://www.oschina.net/p/hp-socket
  9. * QQ Group : 44636872, 75375912
  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 "HttpCookie.h"
  24. #ifdef _HTTP_SUPPORT
  25. static const char* s_short_week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  26. static const char* s_short_month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  27. CCookieMgr g_CookieMgr;
  28. CCookie* CCookie::FromString(const CStringA& strCookie, LPCSTR lpszDefaultDomain, LPCSTR lpszDefaultPath)
  29. {
  30. CStringA strName;
  31. CStringA strValue;
  32. CStringA strDomain;
  33. CStringA strPath;
  34. int iMaxAge = -1;
  35. BOOL bHttpOnly = FALSE;
  36. BOOL bSecure = FALSE;
  37. EnSameSite enSameSite = SS_UNKNOWN;
  38. int i = 0;
  39. int iStart = 0;
  40. while(TRUE)
  41. {
  42. CStringA strField = strCookie.Tokenize(COOKIE_FIELD_SEP, iStart);
  43. strField.Trim();
  44. if(i == 0)
  45. {
  46. ParseFieldKV(strField, strName, strValue, COOKIE_KV_SEP_CHAR);
  47. if(strName.IsEmpty())
  48. return nullptr;
  49. }
  50. else
  51. {
  52. if(strField.IsEmpty())
  53. break;
  54. CStringA strKey;
  55. CStringA strVal;
  56. ParseFieldKV(strField, strKey, strVal, COOKIE_KV_SEP_CHAR);
  57. if(strKey.CompareNoCase(COOKIE_DOMAIN) == 0)
  58. strDomain = strVal;
  59. else if(strKey.CompareNoCase(COOKIE_PATH) == 0)
  60. strPath = strVal;
  61. else if(strKey.CompareNoCase(COOKIE_MAX_AGE) == 0 && !strVal.IsEmpty())
  62. iMaxAge = atoi(strVal);
  63. else if(strKey.CompareNoCase(COOKIE_EXPIRES) == 0 && !strVal.IsEmpty() && iMaxAge == -1)
  64. {
  65. __time64_t tmExpires = -1;
  66. if(!ParseExpires(strVal, tmExpires))
  67. return nullptr;
  68. iMaxAge = ExpiresToMaxAge(tmExpires);
  69. }
  70. else if(strKey.CompareNoCase(COOKIE_HTTPONLY) == 0)
  71. bHttpOnly = TRUE;
  72. else if(strKey.CompareNoCase(COOKIE_SECURE) == 0)
  73. bSecure = TRUE;
  74. else if(strKey.CompareNoCase(COOKIE_SAMESITE) == 0)
  75. {
  76. if(strVal.IsEmpty() || strVal.CompareNoCase(COOKIE_SAMESITE_LAX) == 0)
  77. enSameSite = SS_LAX;
  78. else if(strVal.CompareNoCase(COOKIE_SAMESITE_STRICT) == 0)
  79. enSameSite = SS_STRICT;
  80. else if(strVal.CompareNoCase(COOKIE_SAMESITE_NONE) == 0)
  81. enSameSite = SS_NONE;
  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", (LPCSTR)name, (LPCSTR)value);
  115. if(!domain.IsEmpty())
  116. strCookie.AppendFormat("; %s=%s", COOKIE_DOMAIN, (LPCSTR)domain);
  117. if(!path.IsEmpty())
  118. strCookie.AppendFormat("; %s=%s", COOKIE_PATH, (LPCSTR)path);
  119. if(expires >= 0)
  120. strCookie.AppendFormat("; %s=%s", COOKIE_EXPIRES, (LPCSTR)MakeExpiresStr(expires));
  121. if(httpOnly)
  122. strCookie.AppendFormat("; %s", COOKIE_HTTPONLY);
  123. if(secure)
  124. strCookie.AppendFormat("; %s", COOKIE_SECURE);
  125. if(sameSite != SS_UNKNOWN)
  126. strCookie.AppendFormat("; %s=%s", COOKIE_SAMESITE, sameSite == SS_LAX ? COOKIE_SAMESITE_LAX : (sameSite == SS_STRICT ? COOKIE_SAMESITE_STRICT : COOKIE_SAMESITE_NONE));
  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( lpszExpires, "%*[^, ]%*[, ]%2d%*[-/ ]%8[^-/ ]%*[-/ ]%4d %2d:%2d:%2d %8s",
  184. &t.tm_mday, szMonth, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec, 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(&t, &tmExpires) != nullptr);
  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((pFile = fopen(lpszFile, "r")) == nullptr)
  289. goto _ERROR_END;
  290. {
  291. CStringA strDomain;
  292. CStringA strPath;
  293. CCookie cookie;
  294. char szBuffer[8192];
  295. int iBufferSize = _countof(szBuffer);
  296. __time64_t tmCurrent = _time64(nullptr);
  297. CCookieSet* pCookieSet = nullptr;
  298. CWriteLock locallock(m_cs);
  299. if(!bKeepExists)
  300. ClearDomainCookiesNoLock();
  301. while(fgets(szBuffer, iBufferSize, pFile) != nullptr)
  302. {
  303. char c = szBuffer[0];
  304. if(c == '\n' || c == '\r')
  305. continue;
  306. else if(c != '\t')
  307. {
  308. if(!LoadDomainAndPath(szBuffer, strDomain, strPath))
  309. goto _ERROR_END;
  310. pCookieSet = GetCookieSetNoLock(strDomain, strPath);
  311. }
  312. else
  313. {
  314. if(!LoadCookie(szBuffer, strDomain, strPath, cookie))
  315. goto _ERROR_END;
  316. if(cookie.expires <= tmCurrent)
  317. continue;
  318. if(pCookieSet)
  319. {
  320. if(bKeepExists)
  321. {
  322. CCookieSetCI it = pCookieSet->find(cookie);
  323. if(it != pCookieSet->end())
  324. continue;
  325. }
  326. pCookieSet->emplace(move(cookie));
  327. }
  328. else
  329. {
  330. SetCookieNoLock(cookie, FALSE);
  331. pCookieSet = GetCookieSetNoLock(strDomain, strPath);
  332. }
  333. }
  334. }
  335. if(!feof(pFile))
  336. goto _ERROR_END;
  337. }
  338. isOK = TRUE;
  339. _ERROR_END:
  340. if(pFile) fclose(pFile);
  341. return isOK;
  342. }
  343. BOOL CCookieMgr::SaveToFile(LPCSTR lpszFile, BOOL bKeepExists)
  344. {
  345. if(bKeepExists)
  346. {
  347. if(!LoadFromFile(lpszFile, TRUE) && !IS_ERROR(ERROR_FILE_NOT_FOUND))
  348. return FALSE;
  349. }
  350. BOOL isOK = FALSE;
  351. FILE* pFile = nullptr;
  352. if((pFile = fopen(lpszFile, "w")) == nullptr)
  353. goto _ERROR_END;
  354. {
  355. __time64_t tmCurrent = _time64(nullptr);
  356. CReadLock locallock(m_cs);
  357. for(CCookieDomainMapCI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
  358. {
  359. const CStringA& strDomain = it->first;
  360. const CCookiePathMap& paths = it->second;
  361. for(CCookiePathMapCI it2 = paths.begin(), end2 = paths.end(); it2 != end2; ++it2)
  362. {
  363. const CStringA& strPath = it2->first;
  364. const CCookieSet& cookies = it2->second;
  365. if(fprintf(pFile, "%s %s\n", (LPCSTR)strDomain, (LPCSTR)strPath) < 0)
  366. goto _ERROR_END;
  367. for(CCookieSetCI it3 = cookies.begin(), end3 = cookies.end(); it3 != end3; ++it3)
  368. {
  369. const CCookie& cookie = *it3;
  370. if(cookie.expires <= tmCurrent)
  371. continue;
  372. LPCSTR lpszValue = (LPCSTR)cookie.value;
  373. if(lpszValue[0] == 0)
  374. lpszValue = " ";
  375. if(fprintf(pFile, "\t%s;%s;%lld;%d;%d;%d\n", (LPCSTR)cookie.name, lpszValue, cookie.expires, cookie.httpOnly, cookie.secure, cookie.sameSite) < 0)
  376. goto _ERROR_END;
  377. }
  378. }
  379. }
  380. }
  381. isOK = TRUE;
  382. _ERROR_END:
  383. if(pFile) fclose(pFile);
  384. return isOK;
  385. }
  386. BOOL CCookieMgr::ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
  387. {
  388. CStringA strDomain;
  389. CStringA strPath;
  390. if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
  391. return FALSE;
  392. CWriteLock locallock(m_cs);
  393. ClearDomainCookiesNoLock(lpszDomain, lpszPath);
  394. return TRUE;
  395. }
  396. BOOL CCookieMgr::RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
  397. {
  398. CStringA strDomain;
  399. CStringA strPath;
  400. if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
  401. return FALSE;
  402. CWriteLock locallock(m_cs);
  403. RemoveExpiredCookiesNoLock(lpszDomain, lpszPath);
  404. return TRUE;
  405. }
  406. BOOL CCookieMgr::GetCookies(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
  407. {
  408. ASSERT(lpszDomain && lpszPath);
  409. CStringA strDomain;
  410. CStringA strPath;
  411. if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, FALSE))
  412. return FALSE;
  413. list<LPCSTR> lsDomains(1, lpszDomain);
  414. list<CStringA> lsPaths(1, lpszPath);
  415. char c;
  416. LPCSTR lpszTemp = lpszDomain;
  417. while((c = *(++lpszTemp)) != 0)
  418. {
  419. if(c == COOKIE_DOMAIN_SEP_CHAR)
  420. {
  421. if((c = *(++lpszTemp)) != 0)
  422. lsDomains.push_back(lpszTemp);
  423. else
  424. break;
  425. }
  426. }
  427. lpszTemp = lpszPath + strlen(lpszPath) - 1;
  428. while(--lpszTemp >= lpszPath)
  429. {
  430. if((c = *lpszTemp) == COOKIE_PATH_SEP_CHAR)
  431. {
  432. *(LPSTR)(lpszTemp + 1) = 0;
  433. lsPaths.push_back(lpszPath);
  434. }
  435. }
  436. CReadLock locallock(m_cs);
  437. for(list<LPCSTR>::const_iterator it = lsDomains.begin(), end = lsDomains.end(); it != end; ++it)
  438. {
  439. for(list<CStringA>::const_iterator it2 = lsPaths.begin(), end2 = lsPaths.end(); it2 != end2; ++it2)
  440. MatchCookiesNoLock(cookies, *it, *it2, bHttp, bSecure);
  441. }
  442. return TRUE;
  443. }
  444. BOOL CCookieMgr::SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, CCookie::EnSameSite enSameSite, BOOL bOnlyUpdateValueIfExists)
  445. {
  446. CCookie cookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
  447. return SetCookie(cookie, bOnlyUpdateValueIfExists);
  448. }
  449. BOOL CCookieMgr::SetCookie(const CStringA& strCookie, BOOL bOnlyUpdateValueIfExists)
  450. {
  451. unique_ptr<CCookie> pCookie(CCookie::FromString(strCookie));
  452. if(!pCookie) return FALSE;
  453. return SetCookie(*pCookie, bOnlyUpdateValueIfExists);
  454. }
  455. BOOL CCookieMgr::SetCookie(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
  456. {
  457. if(!cookie.IsValid()) return FALSE;
  458. CWriteLock locallock(m_cs);
  459. return SetCookieNoLock(cookie, bOnlyUpdateValueIfExists);
  460. }
  461. BOOL CCookieMgr::DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
  462. {
  463. CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
  464. return DeleteCookie(cookie);
  465. }
  466. BOOL CCookieMgr::DeleteCookie(const CCookie& cookie)
  467. {
  468. if(!cookie.IsValid()) return FALSE;
  469. CWriteLock locallock(m_cs);
  470. return DeleteCookieNoLock(cookie);
  471. }
  472. void CCookieMgr::ClearDomainCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
  473. {
  474. if(!lpszDomain && !lpszPath)
  475. m_cookies.clear();
  476. else if(!lpszPath)
  477. m_cookies.erase(lpszDomain);
  478. else
  479. {
  480. if(!lpszDomain)
  481. {
  482. for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
  483. ClearPathCookiesNoLock(it->second, lpszPath);
  484. }
  485. else
  486. {
  487. CCookieDomainMapI it = m_cookies.find(lpszDomain);
  488. if(it != m_cookies.end())
  489. ClearPathCookiesNoLock(it->second, lpszPath);
  490. }
  491. }
  492. }
  493. void CCookieMgr::ClearPathCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
  494. {
  495. if(!lpszPath)
  496. paths.clear();
  497. else
  498. {
  499. CCookiePathMapI it = paths.find(lpszPath);
  500. if(it != paths.end())
  501. paths.erase(it->first);
  502. }
  503. }
  504. void CCookieMgr::RemoveExpiredCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
  505. {
  506. if(!lpszDomain)
  507. {
  508. for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
  509. RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
  510. }
  511. else
  512. {
  513. CCookieDomainMapI it = m_cookies.find(lpszDomain);
  514. if(it != m_cookies.end())
  515. RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
  516. }
  517. }
  518. void CCookieMgr::RemoveDomainExpiredCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
  519. {
  520. if(!lpszPath)
  521. {
  522. for(CCookiePathMapI it = paths.begin(), end = paths.end(); it != end; ++it)
  523. RemovePathExpiredCookiesNoLock(it->second);
  524. }
  525. else
  526. {
  527. CCookiePathMapI it = paths.find(lpszPath);
  528. if(it != paths.end())
  529. RemovePathExpiredCookiesNoLock(it->second);
  530. }
  531. }
  532. void CCookieMgr::RemovePathExpiredCookiesNoLock(CCookieSet& cookies)
  533. {
  534. CCookiePtrSet ptrs;
  535. for(CCookieSetI it = cookies.begin(), end = cookies.end(); it != end; ++it)
  536. {
  537. if(it->IsExpired())
  538. ptrs.emplace(&*it);
  539. }
  540. if(!ptrs.empty())
  541. {
  542. for(CCookiePtrSetI it = ptrs.begin(), end = ptrs.end(); it != end; ++it)
  543. cookies.erase(**it);
  544. }
  545. }
  546. const CCookie* CCookieMgr::GetCookieNoLock(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
  547. {
  548. const CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
  549. return GetCookieNoLock(cookie);
  550. }
  551. const CCookie* CCookieMgr::GetCookieNoLock(const CCookie& cookie)
  552. {
  553. const CCookie* pCookie = nullptr;
  554. CCookieDomainMapCI it = m_cookies.find(cookie.domain);
  555. if(it != m_cookies.end())
  556. {
  557. CCookiePathMapCI it2 = it->second.find(cookie.path);
  558. if(it2 != it->second.end())
  559. {
  560. CCookieSetCI it3 = it2->second.find(cookie);
  561. if(it3 != it2->second.end())
  562. pCookie = &*it3;
  563. }
  564. }
  565. return pCookie;
  566. }
  567. void CCookieMgr::MatchCookiesNoLock(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
  568. {
  569. CCookieDomainMapCI it = m_cookies.find(lpszDomain);
  570. if(it != m_cookies.end())
  571. {
  572. CCookiePathMapCI it2 = it->second.find(lpszPath);
  573. if(it2 != it->second.end())
  574. {
  575. for(CCookieSetCI it3 = it2->second.begin(), end3 = it2->second.end(); it3 != end3; ++it3)
  576. {
  577. const CCookie& cookie = *it3;
  578. if(!cookie.IsExpired() && (bHttp || !cookie.httpOnly) && (bSecure || !cookie.secure))
  579. cookies.emplace(cookie);
  580. }
  581. }
  582. }
  583. }
  584. BOOL CCookieMgr::SetCookieNoLock(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
  585. {
  586. if(cookie.IsExpired())
  587. return DeleteCookieNoLock(cookie);
  588. CCookieDomainMapI it = m_cookies.find(cookie.domain);
  589. if(it == m_cookies.end())
  590. it = m_cookies.emplace(CCookieDomainMap::value_type(cookie.domain, CCookiePathMap())).first;
  591. CCookiePathMapI it2 = it->second.find(cookie.path);
  592. if(it2 == it->second.end())
  593. it2 = it->second.emplace(CCookiePathMap::value_type(cookie.path, CCookieSet())).first;
  594. CCookieSet& cookies = it2->second;
  595. CCookieSetI it3 = cookies.find(cookie);
  596. if(it3 != cookies.end())
  597. {
  598. if(bOnlyUpdateValueIfExists && !it3->IsExpired() && cookie.IsTransient())
  599. {
  600. ((CCookie*)&*it3)->value = cookie.value;
  601. return TRUE;
  602. }
  603. cookies.erase(*it3);
  604. }
  605. return cookies.emplace(cookie).second;
  606. }
  607. BOOL CCookieMgr::DeleteCookieNoLock(const CCookie& cookie)
  608. {
  609. BOOL isOK = FALSE;
  610. CCookieDomainMapI it = m_cookies.find(cookie.domain);
  611. if(it != m_cookies.end())
  612. {
  613. CCookiePathMapI it2 = it->second.find(cookie.path);
  614. if(it2 != it->second.end())
  615. {
  616. CCookieSetI it3 = it2->second.find(cookie);
  617. if(it3 != it2->second.end())
  618. {
  619. it2->second.erase(*it3);
  620. isOK = TRUE;
  621. }
  622. }
  623. }
  624. return isOK;
  625. }
  626. CCookieSet* CCookieMgr::GetCookieSetNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
  627. {
  628. CCookieSet* pCookieSet = nullptr;
  629. CCookieDomainMapI it = m_cookies.find(lpszDomain);
  630. if(it != m_cookies.end())
  631. {
  632. CCookiePathMapI it2 = it->second.find(lpszPath);
  633. if(it2 != it->second.end())
  634. pCookieSet = &(it2->second);
  635. }
  636. return pCookieSet;
  637. }
  638. BOOL CCookieMgr::LoadDomainAndPath(LPSTR lpszBuff, CStringA& strDomain, CStringA& strPath)
  639. {
  640. int i = 0;
  641. char* lpszCtx = nullptr;
  642. for(; i < 2; i++)
  643. {
  644. char* lpszToken = strtok_r(lpszBuff, " \n\r", &lpszCtx);
  645. if(!lpszToken)
  646. {
  647. ::SetLastError(ERROR_BAD_FORMAT);
  648. return FALSE;
  649. }
  650. if(i == 0)
  651. {
  652. strDomain = lpszToken;
  653. lpszBuff = nullptr;
  654. }
  655. else
  656. strPath = lpszToken;
  657. }
  658. if(!CCookie::AdjustDomain(strDomain))
  659. return FALSE;
  660. if(!CCookie::AdjustPath(strPath))
  661. return FALSE;
  662. return TRUE;
  663. }
  664. BOOL CCookieMgr::LoadCookie(LPSTR lpszBuff, LPCSTR lpszDomain, LPCSTR lpszPath, CCookie& cookie)
  665. {
  666. cookie.domain = lpszDomain;
  667. cookie.path = lpszPath;
  668. int i = 0;
  669. char* lpszCtx = nullptr;
  670. for(; i < 6; i++)
  671. {
  672. char* lpszToken = strtok_r(lpszBuff, "\t;\n\r", &lpszCtx);
  673. if(!lpszToken)
  674. {
  675. ::SetLastError(ERROR_BAD_FORMAT);
  676. return FALSE;
  677. }
  678. if(i == 0)
  679. {
  680. cookie.name = lpszToken;
  681. lpszBuff = nullptr;
  682. }
  683. else if(i == 1)
  684. cookie.value = lpszToken;
  685. else if(i == 2)
  686. cookie.expires = atoll(lpszToken);
  687. else if(i == 3)
  688. cookie.httpOnly = (BOOL)atoi(lpszToken);
  689. else if(i == 4)
  690. cookie.secure = (BOOL)atoi(lpszToken);
  691. else if(i == 5)
  692. cookie.sameSite = (CCookie::EnSameSite)atoi(lpszToken);
  693. }
  694. cookie.name.Trim();
  695. cookie.value.Trim();
  696. if(cookie.name.IsEmpty() || cookie.expires <= 0 /*|| cookie.httpOnly < 0 || cookie.secure < 0*/ || cookie.sameSite < CCookie::SS_UNKNOWN || cookie.sameSite > CCookie::SS_NONE)
  697. {
  698. ::SetLastError(ERROR_INVALID_DATA);
  699. return FALSE;
  700. }
  701. return TRUE;
  702. }
  703. BOOL CCookieMgr::AdjustDomainAndPath(LPCSTR& lpszDomain, LPCSTR& lpszPath, CStringA& strDomain, CStringA& strPath, BOOL bKeepNull)
  704. {
  705. if(!bKeepNull || lpszDomain)
  706. {
  707. strDomain = lpszDomain;
  708. if(!CCookie::AdjustDomain(strDomain))
  709. return FALSE;
  710. lpszDomain = (LPCSTR)strDomain;
  711. }
  712. if(!bKeepNull || lpszPath)
  713. {
  714. strPath = lpszPath;
  715. if(!CCookie::AdjustPath(strPath))
  716. return FALSE;
  717. lpszPath = (LPCSTR)strPath;
  718. }
  719. return TRUE;
  720. }
  721. CCookieMgr::CCookieMgr(BOOL bEnableThirdPartyCookie)
  722. : m_bEnableThirdPartyCookie(bEnableThirdPartyCookie)
  723. {
  724. }
  725. #endif