LCOV - code coverage report
Current view: top level - edhttp - http_cookie.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 170 0.0 %
Date: 2022-07-09 10:44:38 Functions: 0 25 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/edhttp
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : 
      20             : // self
      21             : //
      22             : #include    "edhttp/http_cookie.h"
      23             : 
      24             : #include    "edhttp/exception.h"
      25             : #include    "edhttp/http_date.h"
      26             : #include    "edhttp/mkgmtime.h"
      27             : #include    "edhttp/names.h"
      28             : 
      29             : 
      30             : // snapdev
      31             : //
      32             : #include    <snapdev/hexadecimal_string.h>
      33             : 
      34             : 
      35             : // C
      36             : //
      37             : #include    <sys/time.h>
      38             : 
      39             : 
      40             : // last include
      41             : //
      42             : #include    <snapdev/poison.h>
      43             : 
      44             : 
      45             : 
      46             : namespace edhttp
      47             : {
      48             : 
      49             : 
      50             : namespace
      51             : {
      52             : 
      53             : //     CHAR           = <any US-ASCII character (octets 0 - 127)>
      54             : //     token          = 1*<any CHAR except CTLs or separators>
      55             : //     separators     = "(" | ")" | "<" | ">" | "@"
      56             : //                    | "," | ";" | ":" | "\" | <">
      57             : //                    | "/" | "[" | "]" | "?" | "="
      58             : //                    | "{" | "}" | SP | HT
      59             : #define SNAP_HTTP_TOKEN_CHAR(base, c) (static_cast<uint32_t>(1)<<(((c)-(base))&0x1F))
      60             : constexpr uint32_t const g_http_token[4] = {
      61             :     /* 00-1F */ 0x00000000,
      62             :     /* 20-3F */
      63             :           SNAP_HTTP_TOKEN_CHAR(0x20, '!')
      64             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '#')
      65             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '$')
      66             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '%')
      67             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '&')
      68             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '\'')
      69             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '*')
      70             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '+')
      71             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '-')
      72             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '.')
      73             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '0')
      74             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '1')
      75             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '2')
      76             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '3')
      77             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '4')
      78             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '5')
      79             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '6')
      80             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '7')
      81             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '8')
      82             :         | SNAP_HTTP_TOKEN_CHAR(0x20, '9')
      83             :     ,
      84             :     /* 40-5F */
      85             :           SNAP_HTTP_TOKEN_CHAR(0x40, 'A')
      86             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'B')
      87             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'C')
      88             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'D')
      89             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'E')
      90             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'F')
      91             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'G')
      92             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'H')
      93             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'I')
      94             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'J')
      95             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'K')
      96             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'L')
      97             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'M')
      98             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'N')
      99             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'O')
     100             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'P')
     101             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'Q')
     102             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'R')
     103             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'S')
     104             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'T')
     105             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'U')
     106             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'V')
     107             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'W')
     108             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'X')
     109             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'Y')
     110             :         | SNAP_HTTP_TOKEN_CHAR(0x40, 'Z')
     111             :         | SNAP_HTTP_TOKEN_CHAR(0x40, '^')
     112             :         | SNAP_HTTP_TOKEN_CHAR(0x40, '_')
     113             :     ,
     114             :     /* 60-7F */
     115             :           SNAP_HTTP_TOKEN_CHAR(0x60, '`')
     116             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'a')
     117             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'b')
     118             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'c')
     119             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'd')
     120             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'e')
     121             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'f')
     122             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'g')
     123             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'h')
     124             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'i')
     125             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'j')
     126             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'k')
     127             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'l')
     128             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'm')
     129             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'n')
     130             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'o')
     131             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'p')
     132             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'q')
     133             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'r')
     134             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 's')
     135             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 't')
     136             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'u')
     137             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'v')
     138             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'w')
     139             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'x')
     140             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'y')
     141             :         | SNAP_HTTP_TOKEN_CHAR(0x60, 'z')
     142             :         | SNAP_HTTP_TOKEN_CHAR(0x60, '|')
     143             :         | SNAP_HTTP_TOKEN_CHAR(0x60, '~')
     144             : };
     145             : #undef SNAP_HTTP_TOKEN_CHAR
     146             : 
     147             : 
     148           0 : void safe_comment(std::string & result, std::string const & comment)
     149             : {
     150           0 :     for(auto const c : comment)
     151             :     {
     152           0 :         if(c > ' ' && c != '"' && c < 0x7F)
     153             :         {
     154           0 :             result += c;
     155             :         }
     156             :     }
     157           0 : }
     158             : 
     159             : 
     160             : 
     161             : 
     162             : } // no name namespace
     163             : 
     164             : 
     165             : /** \brief Create an invalid cookie (no name).
     166             :  *
     167             :  * This function creates an invalid cookie. It is here because the QMap
     168             :  * implementation requires it. You should not use it otherwise since the
     169             :  * f_snap pointer will be set to NULL and is likely going to crash your
     170             :  * server.
     171             :  */
     172           0 : http_cookie::http_cookie()
     173             : {
     174           0 : }
     175             : 
     176             : 
     177             : /** \brief Initializes the cookie.
     178             :  *
     179             :  * This function initializes the cookie. The default for any cookie
     180             :  * is to have the following parameters:
     181             :  *
     182             :  * \li The name as supplied here.
     183             :  * \li The cookie contents, empty by default.
     184             :  * \li The domain set to this website full domain unless the user defined a
     185             :  *     cookie domain as a site parameter (SNAP_NAME_CORE_COOKIE_DOMAIN).
     186             :  * \li A path set to "/".
     187             :  * \li No expiration date (i.e. cookie is for this session only, deleted when browser is closed)
     188             :  * \li Not secure
     189             :  * \li Not limited to HTTP
     190             :  *
     191             :  * Note that the name of the cookie is set when you create it. I cannot be
     192             :  * changed later.
     193             :  *
     194             :  * \note
     195             :  * The name of a cookie is case sensitive. In other words cookie "foo" and
     196             :  * cookie "Foo" can cohexist (although it certainly should not be used!)
     197             :  *
     198             :  * \warning
     199             :  * The cookie domain cannot be determined without a pointer to the snap
     200             :  * child object. If you do not have access to that pointer, make sure that
     201             :  * an object that has access calls the set_domain() at some point or the
     202             :  * cookie is likely to fail.
     203             :  *
     204             :  * \todo
     205             :  * If there is a redirect (i.e. we show website A but the user was
     206             :  * accessible website B,) then the default domain name will be wrong.
     207             :  * We should have a way to retrieve the primary domain name for this
     208             :  * purpose.
     209             :  *
     210             :  * \param[in] snap  The snap child creating this cookie.
     211             :  * \param[in] name  The name of the cookie.
     212             :  * \param[in] value  The value of the cookie. To set a binary value, use set_value() with a QByteArray instead.
     213             :  *
     214             :  * \sa set_value()
     215             :  * \sa set_domain()
     216             :  * \sa set_path()
     217             :  * \sa set_delete()
     218             :  * \sa set_session()
     219             :  * \sa set_expire()
     220             :  * \sa set_expire_in()
     221             :  * \sa set_secure()
     222             :  * \sa set_http_only()
     223             :  * \sa set_comment()
     224             :  * \sa set_comment_url()
     225             :  */
     226           0 : http_cookie::http_cookie(std::string const & name, std::string const & value)
     227             :     : f_name(name)
     228           0 :     , f_path("/")
     229             : {
     230             :     // TODO: make use of libtuf8 instead
     231           0 :     int const max_len(f_name.length());
     232           0 :     if(max_len == 0)
     233             :     {
     234           0 :         throw cookie_parse_exception("the name of a cookie cannot be empty");
     235             :     }
     236           0 :     for(int i(0); i < max_len; ++i)
     237             :     {
     238           0 :         std::uint8_t c(static_cast<std::uint8_t>(f_name[i]));
     239           0 :         if(c <= ' ' || c >= 127 || (g_http_token[c >> 5] & (1 << (c & 0x1F))) == 0)
     240             :         {
     241             :             throw cookie_parse_exception(
     242             :                   "the name of a cookie must only include token compatible characters (offensive character: "
     243           0 :                 + std::string(c, 1)
     244           0 :                 + ")");
     245             :         }
     246             :     }
     247           0 :     if(f_name[0] == '$')
     248             :     {
     249           0 :         throw cookie_parse_exception("cookie name cannot start with '$'; those are reserved by the HTTP protocol");
     250             :     }
     251             : 
     252             :     // TODO: here f_snap would always be nullptr but in snap we still need
     253             :     //       to fix this
     254             :     //
     255             :     //if(f_snap)
     256             :     //{
     257             :     //    libdbproxy::value cookie_domain(f_snap->get_site_parameter(snap::get_name(name_t::SNAP_NAME_CORE_COOKIE_DOMAIN)));
     258             :     //    if(cookie_domain.nullValue())
     259             :     //    {
     260             :     //        // use the fully qualified website domain name
     261             :     //        f_domain = f_snap->get_website_key();
     262             :     //    }
     263             :     //    else
     264             :     //    {
     265             :     //        f_domain = cookie_domain.stringValue();
     266             :     //    }
     267             :     //}
     268             : 
     269           0 :     set_value(value);
     270           0 : }
     271             : 
     272             : 
     273             : /** \brief Set the value of the cookie.
     274             :  *
     275             :  * This function allows you to change the value of the cookie.
     276             :  * By default it is set to an empty string unless you define
     277             :  * the value in the constructor.
     278             :  *
     279             :  * The value is encoded using the usual urlencode mechanism
     280             :  * as to avoid problems with controls and other data.
     281             :  *
     282             :  * We support binary data by calling the set_value() with
     283             :  * the QByteArray parameter.
     284             :  *
     285             :  * \param[in] value  The new value of the cookie.
     286             :  */
     287           0 : void http_cookie::set_value(std::string const & value)
     288             : {
     289           0 :     f_value = value;
     290           0 : }
     291             : 
     292             : 
     293             : /** \brief Set the cookie domain.
     294             :  *
     295             :  * This function is used to set the cookie domain although
     296             :  * it generally should not be required because the constructor
     297             :  * already does so automatically for you using the website
     298             :  * key as defined in the snap child.
     299             :  *
     300             :  * If you want to support many sub-domains, then you should
     301             :  * define the cookie domain as a site parameter instead.
     302             :  *
     303             :  * \note
     304             :  * Using the wrong domain name does nothing as the browser
     305             :  * ignores cookies with wrong domain names.
     306             :  *
     307             :  * \param[in] domain  The new domain name for this cookie.
     308             :  *
     309             :  * \sa get_domain();
     310             :  */
     311           0 : void http_cookie::set_domain(std::string const & domain)
     312             : {
     313           0 :     f_domain = domain;
     314             : 
     315             :     // TODO?
     316             :     // Enhance the check so we don't accept two periods one after
     317             :     // another or two dashes or a name that starts/ends with invalid
     318             :     // characters (i.e. cannot start/end with a dash.) Although some
     319             :     // of those would not be necessary if we check the domain against
     320             :     // the website domain name.
     321           0 :     int max_len(f_domain.length());
     322           0 :     if(max_len > 0 && f_domain[0] == '.')
     323             :     {
     324           0 :         f_domain = f_domain.substr(1);
     325           0 :         max_len = f_domain.length();
     326             :     }
     327           0 :     if(max_len == 0)
     328             :     {
     329           0 :         throw cookie_parse_exception("the domain of a cookie cannot be empty.");
     330             :     }
     331           0 :     for(int i(0); i < max_len; ++i)
     332             :     {
     333             :         // TODO: use libtld instead
     334             :         // TBD -- How is that supporting Unicode characters in domain names?
     335           0 :         char c(f_domain[i]);
     336           0 :         if((c < 'A' || c > 'Z')
     337           0 :         && (c < 'a' || c > 'z')
     338           0 :         && (c < '0' || c > '9')
     339           0 :         && c != '.' && c != '-' && c != '_')
     340             :         {
     341           0 :             throw cookie_parse_exception("the domain of a cookie must only include domain name compatible characters.");
     342             :         }
     343             :     }
     344             : 
     345             :     // TODO: add a check to make sure this is a valid domain name
     346             :     //       (i.e. to the minimum "domain + TLD")
     347           0 : }
     348             : 
     349             : 
     350             : /** \brief Set the path where the cookie is to be saved.
     351             :  *
     352             :  * By default the cookie is setup to be viewed everywhere (i.e. the path
     353             :  * is set to "/".) To constrain the cookie to a section of a website
     354             :  * set the path to that section here.
     355             :  *
     356             :  * This can be useful to setup a secure cookie so administrators can
     357             :  * get a special cookie that really only works in the administrative
     358             :  * part of the website.
     359             :  *
     360             :  * \param[in] path  The new cookie path.
     361             :  *
     362             :  * \sa get_path();
     363             :  */
     364           0 : void http_cookie::set_path(std::string const & path)
     365             : {
     366             :     // TODO:
     367             :     // TBD -- How is that supporting Unicode characters in paths?
     368             :     // (we may have to change them to some %XX syntax
     369           0 :     int max_len(path.length());
     370           0 :     for(int i(0); i < max_len; ++i)
     371             :     {
     372           0 :         char c(f_domain[i]);
     373           0 :         if((c < ' ' || c > '~')
     374           0 :         && c != ',' && c != ';')
     375             :         {
     376           0 :             throw cookie_parse_exception("the path of a cookie must only include ASCII characters except controls, ',' and ';'.");
     377             :         }
     378             :     }
     379             : 
     380           0 :     f_path = path;
     381           0 : }
     382             : 
     383             : 
     384             : /** \brief Mark the cookie for deletion.
     385             :  *
     386             :  * This function sets the expiration date in the past so the cookie
     387             :  * gets deleted. It is usual to set the date to January 1, 1970
     388             :  * (i.e. Unix time of 0) and we do so here.
     389             :  *
     390             :  * \sa set_expire();
     391             :  * \sa set_session();
     392             :  * \sa get_type();
     393             :  */
     394           0 : void http_cookie::set_delete()
     395             : {
     396             :     // January 1, 1970 00:00:00 is represented as 0
     397           0 :     f_expire = 0;
     398           0 : }
     399             : 
     400             : 
     401             : /** \brief Mark the cookie as a session cookie.
     402             :  *
     403             :  * This function invalidates the expiration date of the cookie, which is the
     404             :  * default. When the expiration date is invalid, it is not sent to the browser
     405             :  * and it transforms the cookie in a session cookie.
     406             :  *
     407             :  * This type of cookie only lasts until you close the browser window. For
     408             :  * sensitive accounts (dealing with e-Commerce and similar) it is a good idea
     409             :  * to use this form of cookie.
     410             :  *
     411             :  * \sa set_delete();
     412             :  * \sa set_expire();
     413             :  * \sa get_type();
     414             :  */
     415           0 : void http_cookie::set_session()
     416             : {
     417             :     // invalid expiration date
     418           0 :     f_expire = -1;
     419           0 : }
     420             : 
     421             : 
     422             : /** \brief Set the expiration date of the cookie.
     423             :  *
     424             :  * This function changes the expiration date to the specified date and time.
     425             :  *
     426             :  * In most cases, it is easier to use the set_expire_in() function which uses
     427             :  * now + the specified number of seconds. The result will be the same either
     428             :  * way because we only send an explicit expiration date and not a Max-Age
     429             :  * parameter.
     430             :  *
     431             :  * In order to create a session cookie (the default), you may set the date
     432             :  * to an invalid date or call the set_session() function:
     433             :  *
     434             :  * \code
     435             :  *   QDateTime invalid;
     436             :  *   cookie.set_expiration(invalid);
     437             :  * \endcode
     438             :  *
     439             :  * To delete a cookie, you can set the expiration date to a date in the past.
     440             :  * This is also achieved by calling the set_delete() function.
     441             :  *
     442             :  * \note
     443             :  * If the date represents a date more than 1 year in the future, then it
     444             :  * gets clamped.
     445             :  *
     446             :  * \param[in] date_time  The new expiration date and time.
     447             :  *
     448             :  * \sa set_session();
     449             :  * \sa set_delete();
     450             :  * \sa get_expire();
     451             :  * \sa get_type();
     452             :  */
     453           0 : void http_cookie::set_expire(std::string const & date_time)
     454             : {
     455           0 :     time_t const now(time(nullptr));
     456           0 :     time_t const seconds(string_to_date(date_time));
     457           0 :     if(seconds - now > 86400LL * 365LL)
     458             :     {
     459             :         // save 'now + 1 year' instead of date_time which is further in
     460             :         // the future and thus not HTTP 1.1 compatible
     461             :         //
     462           0 :         f_expire = now + 86400LL * 365LL;
     463             :     }
     464           0 :     else if(seconds < 0)
     465             :     {
     466             :         // the date is past, that means we want to delete
     467             :         // (TBD: should this be an error instead?)
     468             :         //
     469           0 :         f_expire = 0;
     470             :     }
     471             :     else
     472             :     {
     473           0 :         f_expire = seconds;
     474             :     }
     475           0 : }
     476             : 
     477             : 
     478             : /** \brief This function sets the expiration date seconds in the future.
     479             :  *
     480             :  * This function is most often the one used and allows you to set the
     481             :  * expiration date of the cookie the specified number of seconds in
     482             :  * the future.
     483             :  *
     484             :  * The function makes use of the snap child start date plus that number
     485             :  * of seconds, but it sends the cookie with an Expire field (i.e. we do
     486             :  * not make use of the Max-Age field.)
     487             :  *
     488             :  * \note
     489             :  * If the HTTP cookie object was passed a pointer to the snap child
     490             :  * object, then the request start date is used, otherwise the current
     491             :  * date is used as the fallback.
     492             :  *
     493             :  * \param[in] seconds  The number of seconds from now when the cookie expires.
     494             :  *
     495             :  * \sa set_expire();
     496             :  * \sa get_expire();
     497             :  * \sa set_delete();
     498             :  * \sa set_session();
     499             :  */
     500           0 : void http_cookie::set_expire_in(int64_t seconds)
     501             : {
     502             :     // clamp to 1 year (max. allowed by HTTP 1.1)
     503           0 :     if(seconds > 86400LL * 365LL)
     504             :     {
     505           0 :         seconds = 86400LL * 365LL;
     506             :     }
     507             : 
     508           0 :     time_t const now(time(nullptr));
     509           0 :     f_expire = now + seconds;
     510           0 : }
     511             : 
     512             : 
     513             : /** \brief Mark the cookie as secure.
     514             :  *
     515             :  * By default cookies are not marked as secure. Call this function with
     516             :  * the \p secure parameter set to true so the cookie only travels between
     517             :  * the browser and the server if SSL is used.
     518             :  *
     519             :  * Note that a secure cookie is not seen if the user decides to access
     520             :  * your website using the HTTP protocol (opposed to the HTTPS protocol.)
     521             :  * Websites make use of a secure cookie when they have a certificate.
     522             :  *
     523             :  * \note
     524             :  * Snap! implements ways to support logged in users on non-secure
     525             :  * connections, but with much lower rights.
     526             :  *
     527             :  * \param[in] secure  Whether the cookie should be made secure or not.
     528             :  *
     529             :  * \sa get_secure();
     530             :  */
     531           0 : void http_cookie::set_secure(bool secure)
     532             : {
     533           0 :     f_secure = secure;
     534           0 : }
     535             : 
     536             : 
     537             : /** \brief Set the HttpOnly flag.
     538             :  *
     539             :  * This function changes the value of the HttpOnly flag. This flag is
     540             :  * used by browsers to prevent cookies from being visible form JavaScript.
     541             :  * This is important to avoid all sorts of session highjack attacks.
     542             :  *
     543             :  * By default the cookies are visible by JavaScript and other client
     544             :  * supported languages.
     545             :  *
     546             :  * \param[in] http_only  Whether this cookie is only for HTTP.
     547             :  *
     548             :  * \sa get_http_only();
     549             :  */
     550           0 : void http_cookie::set_http_only(bool http_only)
     551             : {
     552           0 :     f_http_only = http_only;
     553           0 : }
     554             : 
     555             : 
     556             : /** \brief Set a comment.
     557             :  *
     558             :  * This function sets the comment of the cookie.
     559             :  *
     560             :  * In general this is verbatim information about the cookie in regard to
     561             :  * the user privacy.
     562             :  *
     563             :  * The set_comment_url() can also be used to set a page where the cookie
     564             :  * privacy information can be found.
     565             :  *
     566             :  * \param[in] comment  The comment about this cookie.
     567             :  *
     568             :  * \sa get_comment();
     569             :  */
     570           0 : void http_cookie::set_comment(std::string const & comment)
     571             : {
     572           0 :     f_comment = comment;
     573           0 : }
     574             : 
     575             : 
     576             : /** \brief Set a comment URL.
     577             :  *
     578             :  * This function sets the comment URL of the cookie. This is actually
     579             :  * made mandatory in the Snap! webserver.
     580             :  *
     581             :  * \param[in] comment_url  The URL to a page tha explains the cookie usage.
     582             :  *
     583             :  * \sa get_comment_url();
     584             :  */
     585           0 : void http_cookie::set_comment_uri(std::string const & comment_uri)
     586             : {
     587           0 :     f_comment_uri = comment_uri;
     588           0 : }
     589             : 
     590             : 
     591             : /** \brief Retrive the name of the cookie.
     592             :  *
     593             :  * The name of the cookie is set when you create it. It cannot be
     594             :  * changed.
     595             :  *
     596             :  * \return The name of the cookie.
     597             :  */
     598           0 : std::string const & http_cookie::get_name() const
     599             : {
     600           0 :     return f_name;
     601             : }
     602             : 
     603             : 
     604             : /** \brief Retrieve the cookie value.
     605             :  *
     606             :  * This function returns the cookie value as a QByteValue. If you set the cookie
     607             :  * as a string, then you can convert it back to a string with the following:
     608             :  *
     609             :  * \code
     610             :  * std::string const & v(cookie.get_value());
     611             :  * QString str(QString::fromUtf8(v.data(), v.size()));
     612             :  * \endcode
     613             :  *
     614             :  * \return A constant reference to the cookie contents.
     615             :  *
     616             :  * \sa set_value()
     617             :  */
     618           0 : std::string const & http_cookie::get_value() const
     619             : {
     620           0 :     return f_value;
     621             : }
     622             : 
     623             : 
     624             : /** \brief Get the current cookie type.
     625             :  *
     626             :  * Depending on how the expiration date is setup, the cookie may have
     627             :  * one of the following types:
     628             :  *
     629             :  * \li HTTP_COOKIE_TYPE_PERMANENT -- the expiration date and time is valid and in the future
     630             :  * \li HTTP_COOKIE_TYPE_SESSION -- the expiration date and time is not valid
     631             :  * \li HTTP_COOKIE_TYPE_DELETE -- the expiration date and time is in the past
     632             :  *
     633             :  * \return One of the HTTP_COOKIE_TYPE_... values.
     634             :  *
     635             :  * \sa set_expire()
     636             :  * \sa set_delete()
     637             :  * \sa set_session()
     638             :  * \sa get_expire()
     639             :  * \sa set_expire_in()
     640             :  */
     641           0 : http_cookie::http_cookie_type_t http_cookie::get_type() const
     642             : {
     643           0 :     if(f_expire < 0)
     644             :     {
     645           0 :         return http_cookie_type_t::HTTP_COOKIE_TYPE_SESSION;
     646             :     }
     647           0 :     if(f_expire == 0)
     648             :     {
     649           0 :         return http_cookie_type_t::HTTP_COOKIE_TYPE_DELETE;
     650             :     }
     651           0 :     return http_cookie_type_t::HTTP_COOKIE_TYPE_PERMANENT;
     652             : }
     653             : 
     654             : 
     655             : /** \brief Get the cookie domain information.
     656             :  *
     657             :  * This function retreives the current domain information of the cookie.
     658             :  * In general the domain is equal to the website key.
     659             :  *
     660             :  * \return The domain defined in this cookie.
     661             :  *
     662             :  * \sa set_domain();
     663             :  */
     664           0 : std::string const & http_cookie::get_domain() const
     665             : {
     666           0 :     return f_domain;
     667             : }
     668             : 
     669             : 
     670             : /** \brief Retrieve the path under which the cookie is valid.
     671             :  *
     672             :  * This function retrieves the current path for this cookie.
     673             :  * By default it is set to "/" which means the cookie is
     674             :  * available over the entire website. It is rarely necessary
     675             :  * to change this value.
     676             :  *
     677             :  * \return The current path for this cookie.
     678             :  *
     679             :  * \sa set_path();
     680             :  */
     681           0 : std::string const & http_cookie::get_path() const
     682             : {
     683           0 :     return f_path;
     684             : }
     685             : 
     686             : 
     687             : /** \brief Get the expiration date.
     688             :  *
     689             :  * This function returns the current expiration date. The date represents
     690             :  * different status of the cookie which can be determined by calling the
     691             :  * get_type() function.
     692             :  *
     693             :  * The default is an invalid cookie which means that the cookie is a
     694             :  * session cookie (lasts until the browser is closed.)
     695             :  *
     696             :  * \return The expiration date of this cookie.
     697             :  *
     698             :  * \sa set_expire();
     699             :  * \sa set_expire_in();
     700             :  * \sa set_delete();
     701             :  * \sa set_session();
     702             :  * \sa get_type();
     703             :  */
     704           0 : time_t http_cookie::get_expire() const
     705             : {
     706           0 :     return f_expire;
     707             : }
     708             : 
     709             : 
     710             : /** \brief Retrieve whether the cookie is secure.
     711             :  *
     712             :  * This function returns whether the cookie should only travel on a
     713             :  * secure (SSL) connection.
     714             :  *
     715             :  * The default is false (cookie is visible on non-secure connections.)
     716             :  *
     717             :  * \return true if the cookie is only for secure connections, false otherwise.
     718             :  *
     719             :  * \sa set_secure();
     720             :  */
     721           0 : bool http_cookie::get_secure() const
     722             : {
     723           0 :     return f_secure;
     724             : }
     725             : 
     726             : 
     727             : /** \brief Retrieve whether the cookie is only for HTTP.
     728             :  *
     729             :  * By default cookies are visible to JavaScript and other client languages.
     730             :  * This parameter can be used to hide the content of the cookie from praying
     731             :  * eyes and only allow the data to travel between the browser and server.
     732             :  *
     733             :  * The default is false.
     734             :  *
     735             :  * \return The current HttpOnly flag status.
     736             :  *
     737             :  * \sa set_http_only();
     738             :  */
     739           0 : bool http_cookie::get_http_only() const
     740             : {
     741           0 :     return f_http_only;
     742             : }
     743             : 
     744             : 
     745             : /** \brief Retrieve the cookie comment.
     746             :  *
     747             :  * This function returns the verbatim cookie comment.
     748             :  *
     749             :  * \return The verbatim comment of the cookie.
     750             :  *
     751             :  * \sa set_comment();
     752             :  */
     753           0 : std::string const & http_cookie::get_comment() const
     754             : {
     755           0 :     return f_comment;
     756             : }
     757             : 
     758             : 
     759             : /** \brief Retrieve the cookie comment URL.
     760             :  *
     761             :  * This function returns the cookie comment URL.
     762             :  *
     763             :  * \return The comment URL of the cookie.
     764             :  *
     765             :  * \sa set_comment_url();
     766             :  */
     767           0 : std::string const & http_cookie::get_comment_uri() const
     768             : {
     769           0 :     return f_comment_uri;
     770             : }
     771             : 
     772             : 
     773             : /** \brief Transform the cookie for the HTTP header.
     774             :  *
     775             :  * This function transforms the cookie so it works as an HTTP header.
     776             :  * It follows the HTTP 1.1 specifications, although it never makes
     777             :  * use of the Max-Age field.
     778             :  *
     779             :  * \return A valid HTTP cookie header.
     780             :  */
     781           0 : std::string http_cookie::to_http_header() const
     782             : {
     783             :     // Note: the name was already checked for invalid characters
     784           0 :     std::string result(g_name_edhttp_field_set_cookie);
     785           0 :     result += ": ";
     786           0 :     result += f_name;
     787           0 :     result += '=';
     788             : 
     789           0 :     for(auto const c : f_value)
     790             :     {
     791           0 :         if(c == 0x21
     792           0 :         || (c >= 0x23 && c <= 0x2B)
     793           0 :         || (c >= 0x2D && c <= 0x3A)
     794           0 :         || (c >= 0x3C && c <= 0x5B)
     795           0 :         || (c >= 0x5D && c <= 0x7E))
     796             :         {
     797           0 :             result += c;
     798             :         }
     799             :         else
     800             :         {
     801             :             // add the byte as %XX
     802           0 :             result += '%';
     803           0 :             result += snapdev::int_to_hex(c, true, 2);
     804             :         }
     805             :     }
     806             : 
     807           0 :     switch(get_type())
     808             :     {
     809           0 :     case http_cookie_type_t::HTTP_COOKIE_TYPE_PERMANENT:
     810             :         // compute date/time
     811             :         // HTTP format generates: Sun, 06 Nov 1994 08:49:37 GMT
     812             :         // (see http://tools.ietf.org/html/rfc2616#section-3.3.1)
     813             :         //
     814           0 :         result += "; ";
     815           0 :         result += g_name_edhttp_param_expires;
     816           0 :         result += '=';
     817           0 :         result += date_to_string(f_expire, date_format_t::DATE_FORMAT_HTTP);
     818             : 
     819             :         // Modern browsers are expected to use the Max-Age=... field
     820             :         // instead of the Expires to avoid potential date synchronization
     821             :         // problems between our server and the client
     822             :         // (see http://tools.ietf.org/html/rfc6265#section-4.1.2.2)
     823             :         //
     824             :         // TBD: although this works, we may want to know the exact
     825             :         //      intend of the person setting the expiration time and
     826             :         //      maybe use that amount (or even change our current
     827             :         //      expire to a max-age and calculate the date in Expires=...
     828             :         //      and not the one in Max-Age.)
     829             :         {
     830           0 :             time_t const max_age(f_expire - time(nullptr));
     831           0 :             if(max_age > 0)
     832             :             {
     833           0 :                 result += "; ";
     834           0 :                 result += g_name_edhttp_param_max_age;
     835           0 :                 result += '=';
     836           0 :                 result += std::to_string(max_age);
     837             :             }
     838             :         }
     839           0 :         break;
     840             : 
     841           0 :     case http_cookie_type_t::HTTP_COOKIE_TYPE_SESSION:
     842             :         // no Expires
     843           0 :         break;
     844             : 
     845           0 :     case http_cookie_type_t::HTTP_COOKIE_TYPE_DELETE:
     846             :         // no need to waste time computing that date
     847           0 :         result += "; ";
     848           0 :         result += g_name_edhttp_param_expires;
     849           0 :         result += '=';
     850           0 :         result += g_name_edhttp_jan1_1970;
     851           0 :         break;
     852             : 
     853             :     }
     854             : 
     855           0 :     if(!f_domain.empty())
     856             :     {
     857             :         // the domain sanity was already checked so we can save it as it here
     858           0 :         result += "; ";
     859           0 :         result += g_name_edhttp_param_domain;
     860           0 :         result += '=';
     861           0 :         result += f_domain;
     862             :     }
     863             : 
     864           0 :     if(!f_path.empty())
     865             :     {
     866             :         // the path sanity was already checked so we can save it as it here
     867           0 :         result += "; ";
     868           0 :         result += g_name_edhttp_param_path;
     869           0 :         result += '=';
     870           0 :         result += f_path;
     871             :     }
     872             : 
     873           0 :     if(f_secure)
     874             :     {
     875           0 :         result += "; ";
     876           0 :         result += g_name_edhttp_param_secure;
     877             :     }
     878             : 
     879           0 :     if(f_http_only)
     880             :     {
     881           0 :         result += "; ";
     882           0 :         result += g_name_edhttp_param_http_only;
     883             :     }
     884             : 
     885           0 :     if(!f_comment.empty())
     886             :     {
     887             :         // we need to escape all "bad" characters, not just quotes
     888           0 :         result += "; ";
     889           0 :         result += g_name_edhttp_param_comment;
     890           0 :         result += "=\"";
     891           0 :         safe_comment(result, f_comment);
     892           0 :         result += '"';
     893             :     }
     894             : 
     895           0 :     if(!f_comment_uri.empty())
     896             :     {
     897             :         // we need to escape all "bad" characters, not just quotes
     898           0 :         result += "; ";
     899           0 :         result += g_name_edhttp_param_comment_url;
     900           0 :         result += "=\"";
     901           0 :         safe_comment(result, f_comment_uri);
     902           0 :         result += '"';
     903             :     }
     904             : 
     905           0 :     return result;
     906             : }
     907             : 
     908             : 
     909             : 
     910             : } // namespace edhttp
     911             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13