| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 | /* <!-- copyright *//* * aria2 - The high speed download utility * * Copyright (C) 2010 Tatsuhiro Tsujikawa * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL.  If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so.  If you * do not wish to do so, delete this exception statement from your * version.  If you delete this exception statement from all source * files in the program, then also delete it here. *//* copyright --> */#include "cookie_helper.h"#include <cstring>#include <vector>#include <limits>#include "util.h"#include "array_fun.h"#include "Cookie.h"#include "a2functional.h"namespace aria2 {namespace cookie {namespace {bool isDelimiter(unsigned char c){  return c == 0x09u || in(c, 0x20u, 0x2fu) || in(c, 0x3bu, 0x40u) ||         in(c, 0x5bu, 0x60u) || in(c, 0x7bu, 0x7eu);}} // namespacenamespace {std::string::const_iterator getNextDigit(std::string::const_iterator first,                                         std::string::const_iterator last){  for (; first != last && in(static_cast<unsigned char>(*first), 0x30u, 0x39u);       ++first)    ;  return first;}} // namespacenamespace {template <typename InputIterator>int getInteger(InputIterator first, InputIterator last){  int res = 0;  // We assume *dest won't overflow.  for (; first != last; ++first) {    res *= 10;    res += (*first) - '0';  }  return res;}} // namespacebool parseDate(time_t& time, std::string::const_iterator first,               std::string::const_iterator last){  // Following algorithm is described in  // http://tools.ietf.org/html/rfc6265#section-5.1.1  std::vector<std::string> dateTokens;  for (std::string::const_iterator i = first, eoi = last; i != eoi;) {    unsigned char c = *i;    if (isDelimiter(c)) {      ++i;      continue;    }    std::string::const_iterator s = i;    for (; s != eoi && !isDelimiter(static_cast<unsigned char>(*s)); ++s)      ;    dateTokens.push_back(std::string(i, s));    i = s;  }  int dayOfMonth = 0;  bool foundDayOfMonth = false;  int month = 0;  bool foundMonth = false;  int year = 0;  bool foundYear = false;  int hour = 0;  int minute = 0;  int second = 0;  bool foundTime = false;  for (std::vector<std::string>::const_iterator i = dateTokens.begin(),                                                eoi = dateTokens.end();       i != eoi; ++i) {    if (!foundTime) {      std::string::const_iterator hEnd;      std::string::const_iterator mEnd;      std::string::const_iterator sEnd;      hEnd = getNextDigit((*i).begin(), (*i).end());      size_t len = std::distance((*i).begin(), hEnd);      if (len == 0 || 2 < len || hEnd == (*i).end() || *hEnd != ':') {        goto NOT_TIME;      }      mEnd = getNextDigit(hEnd + 1, (*i).end());      len = std::distance(hEnd + 1, mEnd);      if (len == 0 || 2 < len || mEnd == (*i).end() || *mEnd != ':') {        goto NOT_TIME;      }      sEnd = getNextDigit(mEnd + 1, (*i).end());      len = std::distance(mEnd + 1, sEnd);      if (len == 0 || 2 < len) {        goto NOT_TIME;      }      foundTime = true;      hour = getInteger((*i).begin(), hEnd);      minute = getInteger(hEnd + 1, mEnd);      second = getInteger(mEnd + 1, sEnd);      continue;    NOT_TIME:      ;    }    if (!foundDayOfMonth) {      std::string::const_iterator j = getNextDigit((*i).begin(), (*i).end());      size_t len = std::distance((*i).begin(), j);      if (1 <= len && len <= 2) {        foundDayOfMonth = true;        dayOfMonth = getInteger((*i).begin(), j);        continue;      }    }    if (!foundMonth) {      static const char MONTH[][12] = {"jan", "feb", "mar", "apr",                                       "may", "jun", "jul", "aug",                                       "sep", "oct", "nov", "dec"};      if ((*i).size() >= 3) {        bool found = false;        size_t j;        for (j = 0; j < 12; ++j) {          if (util::strieq((*i).begin(), (*i).begin() + 3, &MONTH[j][0],                           &MONTH[j][3])) {            found = true;            break;          }        }        if (found) {          foundMonth = true;          month = j + 1;          continue;        }      }    }    if (!foundYear) {      std::string::const_iterator j = getNextDigit((*i).begin(), (*i).end());      size_t len = std::distance((*i).begin(), j);      if (1 <= len && len <= 4) {        foundYear = true;        year = getInteger((*i).begin(), j);        continue;      }    }  }  if (in(year, 70, 99)) {    year += 1900;  }  else if (in(year, 0, 69)) {    year += 2000;  }  if (!foundDayOfMonth || !foundMonth || !foundYear || !foundTime ||      !in(dayOfMonth, 1, 31) || year < 1601 || hour > 23 || minute > 59 ||      second > 59) {    return false;  }  if ((month == 4 || month == 6 || month == 9 || month == 11) &&      dayOfMonth > 30) {    return false;  }  if (month == 2) {    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {      if (dayOfMonth > 29) {        return false;      }    }    else if (dayOfMonth > 28) {      return false;    }  }  tm timespec;  memset(×pec, 0, sizeof(timespec));  timespec.tm_sec = second;  timespec.tm_min = minute;  timespec.tm_hour = hour;  timespec.tm_mday = dayOfMonth;  timespec.tm_mon = month - 1;  timespec.tm_year = year - 1900;  time = timegm(×pec);  return time != -1;}std::unique_ptr<Cookie> parse(const std::string& cookieStr,                              const std::string& requestHost,                              const std::string& defaultPath,                              time_t creationTime){  // This implementation is based on the algorithm listed in  // http://tools.ietf.org/html/rfc6265  std::string::const_iterator nvEnd = cookieStr.begin();  std::string::const_iterator end = cookieStr.end();  for (; nvEnd != end && *nvEnd != ';'; ++nvEnd)    ;  std::string::const_iterator eq = cookieStr.begin();  for (; eq != nvEnd && *eq != '='; ++eq)    ;  if (eq == nvEnd) {    return nullptr;  }  std::pair<std::string::const_iterator, std::string::const_iterator> p =      util::stripIter(cookieStr.begin(), eq);  if (p.first == p.second) {    return nullptr;  }  Scip cookieName(p.first, p.second);  p = util::stripIter(eq + 1, nvEnd);  p = util::stripIter(p.first, p.second, "\"");  Scip cookieValue(p.first, p.second);  time_t expiryTime = 0;  bool foundExpires = false;  bool persistent = false;  time_t maxAge = 0;  bool foundMaxAge = false;  std::string cookieDomain;  bool hostOnly = false;  std::string cookiePath;  bool secure = false;  bool httpOnly = false;  if (nvEnd != end) {    ++nvEnd;  }  for (std::string::const_iterator i = nvEnd; i != end;) {    std::string::const_iterator j = std::find(i, end, ';');    std::string::const_iterator eq = std::find(i, j, '=');    p = util::stripIter(i, eq);    std::pair<std::string::const_iterator, std::string::const_iterator> attrp;    if (eq == j) {      attrp.first = attrp.second = j;    }    else {      attrp = util::stripIter(eq + 1, j);    }    i = j;    if (j != end) {      ++i;    }    if (util::strieq(p.first, p.second, "expires")) {      if (parseDate(expiryTime, attrp.first, attrp.second)) {        foundExpires = true;      }      else {        return nullptr;      }    }    else if (util::strieq(p.first, p.second, "max-age")) {      if (attrp.first == attrp.second ||          (!in(static_cast<unsigned char>(*attrp.first), 0x30u, 0x39u) &&           *attrp.first != '-')) {        return nullptr;      }      for (std::string::const_iterator s = attrp.first + 1, eos = attrp.second;           s != eos; ++s) {        if (!in(static_cast<unsigned char>(*s), 0x30u, 0x39u)) {          return nullptr;        }      }      int64_t delta;      if (util::parseLLIntNoThrow(delta,                                  std::string(attrp.first, attrp.second))) {        foundMaxAge = true;        if (delta <= 0) {          maxAge = 0;        }        else {          int64_t n = creationTime;          n += delta;          if (n < 0 || std::numeric_limits<time_t>::max() < n) {            maxAge = std::numeric_limits<time_t>::max();          }          else {            maxAge = n;          }        }      }      else {        return nullptr;      }    }    else if (util::strieq(p.first, p.second, "domain")) {      if (attrp.first == attrp.second) {        return nullptr;      }      std::string::const_iterator noDot = attrp.first;      std::string::const_iterator end = attrp.second;      for (; noDot != end && *noDot == '.'; ++noDot)        ;      if (noDot == end) {        return nullptr;      }      cookieDomain.assign(noDot, end);    }    else if (util::strieq(p.first, p.second, "path")) {      if (goodPath(attrp.first, attrp.second)) {        cookiePath.assign(attrp.first, attrp.second);      }      else {        cookiePath = defaultPath;      }    }    else if (util::strieq(p.first, p.second, "secure")) {      secure = true;    }    else if (util::strieq(p.first, p.second, "httponly")) {      httpOnly = true;    }  }  if (foundMaxAge) {    expiryTime = maxAge;    persistent = true;  }  else if (foundExpires) {    persistent = true;  }  else {    expiryTime = std::numeric_limits<time_t>::max();    persistent = false;  }  std::string canonicalizedHost = canonicalizeHost(requestHost);  if (cookieDomain.empty()) {    hostOnly = true;    cookieDomain = canonicalizedHost;  }  else if (domainMatch(canonicalizedHost, cookieDomain)) {    hostOnly = util::isNumericHost(canonicalizedHost);  }  else {    return nullptr;  }  if (cookiePath.empty()) {    cookiePath = defaultPath;  }  auto cookie = make_unique<Cookie>();  cookie->setName(cookieName.first, cookieName.second);  cookie->setValue(cookieValue.first, cookieValue.second);  cookie->setExpiryTime(expiryTime);  cookie->setPersistent(persistent);  cookie->setDomain(std::move(cookieDomain));  cookie->setHostOnly(hostOnly);  cookie->setPath(std::move(cookiePath));  cookie->setSecure(secure);  cookie->setHttpOnly(httpOnly);  cookie->setCreationTime(creationTime);  cookie->setLastAccessTime(creationTime);  return cookie;}bool goodPath(std::string::const_iterator first,              std::string::const_iterator last){  return first != last && *first == '/';}std::string canonicalizeHost(const std::string& host){  return util::toLower(host);}bool domainMatch(const std::string& requestHost, const std::string& domain){  return requestHost == domain ||         (util::endsWith(requestHost, domain) &&          requestHost[requestHost.size() - domain.size() - 1] == '.' &&          !util::isNumericHost(requestHost));}bool pathMatch(const std::string& requestPath, const std::string& path){  return requestPath == path ||         (util::startsWith(requestPath, path) &&          (path[path.size() - 1] == '/' || requestPath[path.size()] == '/'));}std::string reverseDomainLevel(const std::string& domain){  std::string r;  if (domain.empty()) {    return r;  }  r.reserve(domain.size());  // Cut trailing dots  std::string::const_iterator s = domain.end() - 1;  for (; *s == '.'; --s) {    if (s == domain.begin()) {      return r;    }  }  std::string::const_iterator t = s + 1;  for (;; --s) {    if (*s == '.') {      r.append(s + 1, t);      r += '.';      t = s;    }    if (s == domain.begin()) {      r.append(s, t);      break;    }  }  return r;}} // namespace cookie} // namespace aria2
 |