cookie_helper.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /* <!-- copyright */
  2. /*
  3. * aria2 - The high speed download utility
  4. *
  5. * Copyright (C) 2010 Tatsuhiro Tsujikawa
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * In addition, as a special exception, the copyright holders give
  22. * permission to link the code of portions of this program with the
  23. * OpenSSL library under certain conditions as described in each
  24. * individual source file, and distribute linked combinations
  25. * including the two.
  26. * You must obey the GNU General Public License in all respects
  27. * for all of the code used other than OpenSSL. If you modify
  28. * file(s) with this exception, you may extend this exception to your
  29. * version of the file(s), but you are not obligated to do so. If you
  30. * do not wish to do so, delete this exception statement from your
  31. * version. If you delete this exception statement from all source
  32. * files in the program, then also delete it here.
  33. */
  34. /* copyright --> */
  35. #include "cookie_helper.h"
  36. #include <cstring>
  37. #include <vector>
  38. #include "util.h"
  39. #include "array_fun.h"
  40. #include "Cookie.h"
  41. #include "a2functional.h"
  42. namespace aria2 {
  43. namespace cookie {
  44. namespace {
  45. bool isDelimiter(unsigned char c)
  46. {
  47. return c == 0x09U || in(c, 0x20U, 0x2fU) || in(c, 0x3bU, 0x40U) ||
  48. in(c, 0x5bU, 0x60U) || in(c, 0x7bU, 0x7eU);
  49. }
  50. } // namespace
  51. namespace {
  52. std::string::const_iterator getNextDigit
  53. (std::string::const_iterator first, std::string::const_iterator last)
  54. {
  55. for(; first != last && in(static_cast<unsigned char>(*first), 0x30U, 0x39U);
  56. ++first);
  57. return first;
  58. }
  59. }
  60. bool parseDate(time_t& time, const std::string& cookieDate)
  61. {
  62. std::vector<std::string> dateTokens;
  63. for(std::string::const_iterator i = cookieDate.begin(),
  64. eoi = cookieDate.end(); i != eoi;) {
  65. unsigned char c = *i;
  66. if(isDelimiter(c)) {
  67. ++i;
  68. continue;
  69. }
  70. std::string::const_iterator s = i;
  71. for(; s != eoi && !isDelimiter(static_cast<unsigned char>(*s)); ++s);
  72. dateTokens.push_back(std::string(i, s));
  73. i = s;
  74. }
  75. int dayOfMonth = 0;
  76. bool foundDayOfMonth = false;
  77. int month = 0;
  78. bool foundMonth = false;
  79. int year = 0;
  80. bool foundYear = false;
  81. int hour = 0;
  82. int minute = 0;
  83. int second = 0;
  84. bool foundTime = false;
  85. for(std::vector<std::string>::const_iterator i = dateTokens.begin(),
  86. eoi = dateTokens.end(); i != eoi; ++i) {
  87. if(!foundTime) {
  88. std::string::const_iterator hEnd;
  89. std::string::const_iterator mEnd;
  90. std::string::const_iterator sEnd;
  91. hEnd = getNextDigit((*i).begin(),(*i).end());
  92. size_t len = std::distance((*i).begin(), hEnd);
  93. if(len == 0 || 2 < len || hEnd == (*i).end() || *hEnd != ':') {
  94. goto NOT_TIME;
  95. }
  96. mEnd = getNextDigit(hEnd+1, (*i).end());
  97. len = std::distance(hEnd+1, mEnd);
  98. if(len == 0 || 2 < len || mEnd == (*i).end() || *mEnd != ':') {
  99. goto NOT_TIME;
  100. }
  101. sEnd = getNextDigit(mEnd+1, (*i).end());
  102. len = std::distance(mEnd+1, sEnd);
  103. if(len == 0 || 2 < len) {
  104. goto NOT_TIME;
  105. }
  106. foundTime = true;
  107. hour = util::parseInt(std::string((*i).begin(), hEnd));
  108. minute = util::parseInt(std::string(hEnd+1, mEnd));
  109. second = util::parseInt(std::string(mEnd+1, sEnd));
  110. continue;
  111. NOT_TIME:
  112. ;
  113. }
  114. if(!foundDayOfMonth) {
  115. std::string::const_iterator j = getNextDigit((*i).begin(), (*i).end());
  116. size_t len = std::distance((*i).begin(), j);
  117. if(1 <= len && len <= 2) {
  118. foundDayOfMonth = true;
  119. dayOfMonth = util::parseInt(std::string((*i).begin(), j));
  120. continue;
  121. }
  122. }
  123. if(!foundMonth) {
  124. static std::string MONTH[] = {
  125. "jan", "feb", "mar", "apr",
  126. "may", "jun", "jul", "aug",
  127. "sep", "oct", "nov", "dec" };
  128. if((*i).size() >= 3) {
  129. std::string head = (*i).substr(0, 3);
  130. util::lowercase(head);
  131. std::string* mptr = std::find(vbegin(MONTH), vend(MONTH), head);
  132. if(mptr != vend(MONTH)) {
  133. foundMonth = true;
  134. month = std::distance(vbegin(MONTH), mptr)+1;
  135. continue;
  136. }
  137. }
  138. }
  139. if(!foundYear) {
  140. std::string::const_iterator j = getNextDigit((*i).begin(), (*i).end());
  141. size_t len = std::distance((*i).begin(), j);
  142. if(1 <= len && len <= 4) {
  143. foundYear = true;
  144. year = util::parseInt(std::string((*i).begin(), j));
  145. continue;
  146. }
  147. }
  148. }
  149. if(in(year, 70, 99)) {
  150. year += 1900;
  151. } else if(in(year, 0, 69)) {
  152. year += 2000;
  153. }
  154. if(!foundDayOfMonth || !foundMonth || !foundYear || !foundTime ||
  155. !in(dayOfMonth, 1, 31) || year < 1601 || hour > 23 ||
  156. minute > 59 || second > 59) {
  157. return false;
  158. }
  159. if((month == 4 || month == 6 || month == 9 || month == 11) &&
  160. dayOfMonth > 30) {
  161. return false;
  162. }
  163. if(month == 2) {
  164. if((year%4 == 0 && year%100 != 0) || year%400 == 0) {
  165. if(dayOfMonth > 29) {
  166. return false;
  167. }
  168. } else if(dayOfMonth > 28) {
  169. return false;
  170. }
  171. }
  172. tm timespec;
  173. memset(&timespec, 0, sizeof(timespec));
  174. timespec.tm_sec = second;
  175. timespec.tm_min = minute;
  176. timespec.tm_hour = hour;
  177. timespec.tm_mday = dayOfMonth;
  178. timespec.tm_mon = month-1;
  179. timespec.tm_year = year-1900;
  180. time = timegm(&timespec);
  181. return time != -1;
  182. }
  183. bool parse
  184. (Cookie& cookie,
  185. const std::string& cookieStr,
  186. const std::string& requestHost,
  187. const std::string& defaultPath,
  188. time_t creationTime)
  189. {
  190. std::string::const_iterator nvEnd = cookieStr.begin();
  191. std::string::const_iterator end = cookieStr.end();
  192. for(; nvEnd != end && *nvEnd != ';'; ++nvEnd);
  193. std::string::const_iterator eq = cookieStr.begin();
  194. for(; eq != nvEnd && *eq != '='; ++eq);
  195. if(eq == nvEnd) {
  196. return false;
  197. }
  198. std::string cookieName = util::stripIter(cookieStr.begin(), eq);
  199. if(cookieName.empty()) {
  200. return false;
  201. }
  202. std::string cookieValue = util::stripIter(eq+1, nvEnd);
  203. time_t expiryTime = 0;
  204. bool foundExpires = false;
  205. bool persistent = false;
  206. time_t maxAge = 0;
  207. bool foundMaxAge = false;
  208. std::string cookieDomain;
  209. bool hostOnly = false;
  210. std::string cookiePath;
  211. bool secure = false;
  212. bool httpOnly = false;
  213. for(std::string::const_iterator i = nvEnd; i != end;) {
  214. std::string::const_iterator j = i;
  215. for(; j != end && *j != ';'; ++j);
  216. std::string::const_iterator eq = i;
  217. for(; eq != j && *eq != '='; ++eq);
  218. std::string attrName = util::stripIter(i, eq);
  219. util::lowercase(attrName);
  220. std::string attrValue;
  221. if(eq != j) {
  222. attrValue = util::stripIter(eq+1, j);
  223. }
  224. i = j;
  225. if(j != end) {
  226. ++i;
  227. }
  228. if(attrName == "expires") {
  229. if(parseDate(expiryTime, attrValue)) {
  230. foundExpires = true;
  231. } else {
  232. return false;
  233. }
  234. } else if(attrName == "max-age") {
  235. if(attrValue.empty() ||
  236. (!in(static_cast<unsigned char>(attrValue[0]), 0x30u, 0x39u) &&
  237. attrValue[0] != '-')) {
  238. return false;
  239. }
  240. for(std::string::const_iterator s = attrValue.begin()+1,
  241. eos = attrValue.end(); s != eos; ++s) {
  242. if(!in(static_cast<unsigned char>(*s), 0x30u, 0x39u)) {
  243. return false;
  244. }
  245. }
  246. int64_t delta;
  247. if(util::parseLLIntNoThrow(delta, attrValue)) {
  248. foundMaxAge = true;
  249. if(delta <= 0) {
  250. maxAge = 0;
  251. } else {
  252. int64_t n = creationTime;
  253. n += delta;
  254. if(n < 0 || (sizeof(time_t) < 8 && n > INT32_MAX)) {
  255. maxAge = INT32_MAX;
  256. } else {
  257. maxAge = n;
  258. }
  259. }
  260. } else {
  261. return false;
  262. }
  263. } else if(attrName == "domain") {
  264. if(attrValue.empty()) {
  265. return false;
  266. }
  267. std::string::const_iterator noDot = attrValue.begin();
  268. std::string::const_iterator end = attrValue.end();
  269. for(; noDot != end && *noDot == '.'; ++noDot);
  270. if(noDot == end) {
  271. return false;
  272. }
  273. cookieDomain = std::string(noDot, end);
  274. } else if(attrName == "path") {
  275. if(goodPath(attrValue)) {
  276. cookiePath = attrValue;
  277. } else {
  278. cookiePath = defaultPath;
  279. }
  280. } else if(attrName == "secure") {
  281. secure = true;
  282. } else if(attrName == "httponly") {
  283. httpOnly = true;
  284. }
  285. }
  286. if(foundMaxAge) {
  287. expiryTime = maxAge;
  288. persistent = true;
  289. } else if(foundExpires) {
  290. persistent = true;
  291. } else {
  292. expiryTime = INT32_MAX;
  293. persistent = false;
  294. }
  295. std::string canonicalizedHost = canonicalizeHost(requestHost);
  296. if(cookieDomain.empty()) {
  297. hostOnly = true;
  298. cookieDomain = canonicalizedHost;
  299. } else if(domainMatch(canonicalizedHost, cookieDomain)) {
  300. hostOnly = util::isNumericHost(canonicalizedHost);
  301. } else {
  302. return false;
  303. }
  304. if(cookiePath.empty()) {
  305. cookiePath = defaultPath;
  306. }
  307. cookie.setName(cookieName);
  308. cookie.setValue(cookieValue);
  309. cookie.setExpiryTime(expiryTime);
  310. cookie.setPersistent(persistent);
  311. cookie.setDomain(cookieDomain);
  312. cookie.setHostOnly(hostOnly);
  313. cookie.setPath(cookiePath);
  314. cookie.setSecure(secure);
  315. cookie.setHttpOnly(httpOnly);
  316. cookie.setCreationTime(creationTime);
  317. cookie.setLastAccessTime(creationTime);
  318. return true;
  319. }
  320. std::string removePrecedingDots(const std::string& host)
  321. {
  322. std::string::const_iterator noDot = host.begin();
  323. std::string::const_iterator end = host.end();
  324. for(; noDot != end && *noDot == '.'; ++noDot);
  325. return std::string(noDot, end);
  326. }
  327. bool goodPath(const std::string& cookiePath)
  328. {
  329. return !cookiePath.empty() && cookiePath[0] == '/';
  330. }
  331. std::string canonicalizeHost(const std::string& host)
  332. {
  333. std::string ch = util::toLower(host);
  334. return ch;
  335. }
  336. bool domainMatch(const std::string& requestHost, const std::string& domain)
  337. {
  338. return requestHost == domain ||
  339. (util::endsWith(requestHost, domain) &&
  340. requestHost[requestHost.size()-domain.size()-1] == '.' &&
  341. !util::isNumericHost(requestHost));
  342. }
  343. bool pathMatch(const std::string& requestPath, const std::string& path)
  344. {
  345. return requestPath == path ||
  346. (util::startsWith(requestPath, path) &&
  347. (path[path.size()-1] == '/' || requestPath[path.size()] == '/'));
  348. }
  349. std::string reverseDomainLevel(const std::string& domain)
  350. {
  351. std::string r;
  352. for(std::string::const_iterator i = domain.begin(), eoi = domain.end();
  353. i != eoi;) {
  354. std::string::const_iterator j = i;
  355. for(; j != eoi && *j != '.'; ++j);
  356. r.insert(r.begin(), '.');
  357. r.insert(r.begin(), i, j);
  358. i = j;
  359. if(j != eoi) {
  360. ++i;
  361. }
  362. }
  363. if(!r.empty()) {
  364. r.erase(r.size()-1, 1);
  365. }
  366. return r;
  367. }
  368. } // namespace cookie
  369. } // namespace aria2