LCOV - code coverage report
Current view: top level - snapwebsites - http_cookie.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 153 0.7 %
Date: 2019-12-15 17:13:15 Functions: 2 27 7.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Websites Server -- manage HTTP cookies to be sent to the browser
       2             : // Copyright (c) 2013-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      14             : // You should have received a copy of the GNU General Public License
      15             : // along with this program; if not, write to the Free Software
      16             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17             : 
      18             : 
      19             : // self
      20             : //
      21             : #include "snapwebsites/http_cookie.h"
      22             : 
      23             : 
      24             : // snapwebsites lib
      25             : //
      26             : #include "snapwebsites/log.h"
      27             : #include "snapwebsites/snapwebsites.h"
      28             : 
      29             : 
      30             : // libdbproxy lib
      31             : //
      32             : #include <libdbproxy/libdbproxy.h>
      33             : 
      34             : 
      35             : // C lib
      36             : //
      37             : #include <sys/time.h>
      38             : 
      39             : 
      40             : // snapdev lib
      41             : //
      42             : #include <snapdev/poison.h>
      43             : 
      44             : 
      45             : 
      46             : namespace snap
      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             : uint32_t 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             : } // no name namespace
     149             : 
     150             : 
     151             : /** \brief Create an invalid cookie (no name).
     152             :  *
     153             :  * This function creates an invalid cookie. It is here because the QMap
     154             :  * implementation requires it. You should not use it otherwise since the
     155             :  * f_snap pointer will be set to NULL and is likely going to crash your
     156             :  * server.
     157             :  */
     158           0 : http_cookie::http_cookie()
     159             : {
     160           0 : }
     161             : 
     162             : 
     163             : /** \brief Initializes the cookie.
     164             :  *
     165             :  * This function initializes the cookie. The default for any cookie
     166             :  * is to have the following parameters:
     167             :  *
     168             :  * \li The name as supplied here.
     169             :  * \li The cookie contents, empty by default.
     170             :  * \li The domain set to this website full domain unless the user defined a
     171             :  *     cookie domain as a site parameter (SNAP_NAME_CORE_COOKIE_DOMAIN).
     172             :  * \li A path set to "/".
     173             :  * \li No expiration date (i.e. cookie is for this session only, deleted when browser is closed)
     174             :  * \li Not secure
     175             :  * \li Not limited to HTTP
     176             :  *
     177             :  * Note that the name of the cookie is set when you create it. I cannot be
     178             :  * changed later.
     179             :  *
     180             :  * \note
     181             :  * The name of a cookie is case sensitive. In other words cookie "foo" and
     182             :  * cookie "Foo" can cohexist (although it certainly should not be used!)
     183             :  *
     184             :  * \warning
     185             :  * The cookie domain cannot be determined without a pointer to the snap
     186             :  * child object. If you do not have access to that pointer, make sure that
     187             :  * an object that has access calls the set_domain() at some point or the
     188             :  * cookie is likely to fail.
     189             :  *
     190             :  * \todo
     191             :  * If there is a redirect (i.e. we show website A but the user was
     192             :  * accessible website B,) then the default domain name will be wrong.
     193             :  * We should have a way to retrieve the primary domain name for this
     194             :  * purpose.
     195             :  *
     196             :  * \param[in] snap  The snap child creating this cookie.
     197             :  * \param[in] name  The name of the cookie.
     198             :  * \param[in] value  The value of the cookie. To set a binary value, use set_value() with a QByteArray instead.
     199             :  *
     200             :  * \sa set_value()
     201             :  * \sa set_domain()
     202             :  * \sa set_path()
     203             :  * \sa set_delete()
     204             :  * \sa set_session()
     205             :  * \sa set_expire()
     206             :  * \sa set_expire_in()
     207             :  * \sa set_secure()
     208             :  * \sa set_http_only()
     209             :  * \sa set_comment()
     210             :  * \sa set_comment_url()
     211             :  */
     212           0 : http_cookie::http_cookie(snap_child * snap, QString const & name, QString const & value)
     213             :     : f_snap(snap)
     214             :     , f_name(name)
     215           0 :     , f_path("/")
     216             : {
     217             :     // XXX make this check only in debug-like versions
     218           0 :     int const max_len(f_name.length());
     219           0 :     if(max_len == 0)
     220             :     {
     221           0 :         throw http_cookie_parse_exception("the name of a cookie cannot be empty");
     222             :     }
     223           0 :     for(int i(0); i < max_len; ++i)
     224             :     {
     225           0 :         ushort c(f_name[i].unicode());
     226           0 :         if(c <= ' ' || c >= 127 || (http_token[c >> 5] & (1 << (c & 0x1F))) == 0)
     227             :         {
     228           0 :             throw http_cookie_parse_exception(QString("the name of a cookie must only include token compatible characters (offensive character: %1)").arg(QChar(c)));
     229             :         }
     230             :     }
     231           0 :     if(f_name[0] == '$')
     232             :     {
     233           0 :         throw http_cookie_parse_exception("cookie name cannot start with '$'; those are reserved by the HTTP protocol");
     234             :     }
     235             : 
     236           0 :     if(f_snap)
     237             :     {
     238           0 :         libdbproxy::value cookie_domain(f_snap->get_site_parameter(snap::get_name(name_t::SNAP_NAME_CORE_COOKIE_DOMAIN)));
     239           0 :         if(cookie_domain.nullValue())
     240             :         {
     241             :             // use the fully qualified website domain name
     242           0 :             f_domain = f_snap->get_website_key();
     243             :         }
     244             :         else
     245             :         {
     246           0 :             f_domain = cookie_domain.stringValue();
     247             :         }
     248             :     }
     249             : 
     250           0 :     set_value(value);
     251           0 : }
     252             : 
     253             : 
     254             : /** \brief Set the value of the cookie.
     255             :  *
     256             :  * This function allows you to change the value of the cookie.
     257             :  * By default it is set to an empty string unless you define
     258             :  * the value in the constructor.
     259             :  *
     260             :  * The value is encoded using the usual urlencode mechanism
     261             :  * as to avoid problems with controls and other data.
     262             :  *
     263             :  * We support binary data by calling the set_value() with
     264             :  * the QByteArray parameter.
     265             :  *
     266             :  * \param[in] value  The new value of the cookie.
     267             :  */
     268           0 : void http_cookie::set_value(QString const & value)
     269             : {
     270           0 :     set_value(value.toUtf8());
     271           0 : }
     272             : 
     273             : 
     274             : /** \brief Set the value of the cookie.
     275             :  *
     276             :  * This function allows you to change the value of the cookie.
     277             :  * By default it is set to an empty string unless you define
     278             :  * the value in the constructor.
     279             :  *
     280             :  * The value is encoded using the usual urlencode mechanism
     281             :  * as to avoid problems with controls and other data.
     282             :  *
     283             :  * We support binary data by calling the set_value() with
     284             :  * the QByteArray parameter.
     285             :  *
     286             :  * \param[in] value  The new value of the cookie.
     287             :  */
     288           0 : void http_cookie::set_value(QByteArray const & value)
     289             : {
     290           0 :     f_value = value;
     291           0 : }
     292             : 
     293             : 
     294             : /** \brief Set the cookie domain.
     295             :  *
     296             :  * This function is used to set the cookie domain although
     297             :  * it generally should not be required because the constructor
     298             :  * already does so automatically for you using the website
     299             :  * key as defined in the snap child.
     300             :  *
     301             :  * If you want to support many sub-domains, then you should
     302             :  * define the cookie domain as a site parameter instead.
     303             :  *
     304             :  * \note
     305             :  * Using the wrong domain name does nothing as the browser
     306             :  * ignores cookies with wrong domain names.
     307             :  *
     308             :  * \param[in] domain  The new domain name for this cookie.
     309             :  *
     310             :  * \sa get_domain();
     311             :  */
     312           0 : void http_cookie::set_domain(QString const & domain)
     313             : {
     314           0 :     f_domain = domain;
     315             : 
     316             :     // TODO?
     317             :     // Enhance the check so we don't accept two periods one after
     318             :     // another or two dashes or a name that starts/ends with invalid
     319             :     // characters (i.e. cannot start/end with a dash.) Although some
     320             :     // of those would not be necessary if we check the domain against
     321             :     // the website domain name.
     322           0 :     int max_len(f_domain.length());
     323           0 :     if(max_len > 0 && f_domain[0] == '.')
     324             :     {
     325           0 :         f_domain = f_domain.mid(1);
     326           0 :         max_len = f_domain.length();
     327             :     }
     328           0 :     if(max_len == 0)
     329             :     {
     330           0 :         throw http_cookie_parse_exception("the domain of a cookie cannot be empty");
     331             :     }
     332           0 :     for(int i(0); i < max_len; ++i)
     333             :     {
     334             :         // TODO:
     335             :         // TBD -- How is that supporting Unicode characters in domain names?
     336           0 :         ushort c(f_domain[i].unicode());
     337           0 :         if((c < 'A' || c > 'Z')
     338           0 :         && (c < 'a' || c > 'z')
     339           0 :         && (c < '0' || c > '9')
     340           0 :         && c != '.' && c != '-' && c != '_')
     341             :         {
     342           0 :             throw http_cookie_parse_exception("the domain of a cookie must only include domain name compatible characters");
     343             :         }
     344             :     }
     345             : 
     346             :     // TODO: add a check to make sure this is a valid domain name
     347             :     //       (i.e. to the minimum "domain + TLD")
     348           0 : }
     349             : 
     350             : 
     351             : /** \brief Set the path where the cookie is to be saved.
     352             :  *
     353             :  * By default the cookie is setup to be viewed everywhere (i.e. the path
     354             :  * is set to "/".) To constrain the cookie to a section of a website
     355             :  * set the path to that section here.
     356             :  *
     357             :  * This can be useful to setup a secure cookie so administrators can
     358             :  * get a special cookie that really only works in the administrative
     359             :  * part of the website.
     360             :  *
     361             :  * \param[in] path  The new cookie path.
     362             :  *
     363             :  * \sa get_path();
     364             :  */
     365           0 : void http_cookie::set_path(QString const & path)
     366             : {
     367             :     // TODO:
     368             :     // TBD -- How is that supporting Unicode characters in paths?
     369             :     // (we may have to change them to some %XX syntax
     370           0 :     int max_len(path.length());
     371           0 :     for(int i(0); i < max_len; ++i)
     372             :     {
     373           0 :         ushort c(f_domain[i].unicode());
     374           0 :         if((c < ' ' || c > '~')
     375           0 :         && c != ',' && c != ';')
     376             :         {
     377           0 :             throw http_cookie_parse_exception("the path of a cookie must only include ASCII characters except controls, ',' and ';'.");
     378             :         }
     379             :     }
     380             : 
     381           0 :     f_path = path;
     382           0 : }
     383             : 
     384             : 
     385             : /** \brief Mark the cookie for deletion.
     386             :  *
     387             :  * This function sets the expiration date in the past so the cookie
     388             :  * gets deleted. It is usual to set the date to January 1, 1970
     389             :  * (i.e. Unix time of 0) and we do so here.
     390             :  *
     391             :  * \sa set_expire();
     392             :  * \sa set_session();
     393             :  * \sa get_type();
     394             :  */
     395           0 : void http_cookie::set_delete()
     396             : {
     397             :     // January 1, 1970 00:00:00 is represented as 0
     398           0 :     f_expire = QDateTime::fromMSecsSinceEpoch(0);
     399           0 : }
     400             : 
     401             : 
     402             : /** \brief Mark the cookie as a session cookie.
     403             :  *
     404             :  * This function invalidates the expiration date of the cookie, which is the
     405             :  * default. When the expiration date is invalid, it is not sent to the browser
     406             :  * and it transform the cookie in a session cookie.
     407             :  *
     408             :  * This type of cookie only lasts until you close the browser window. For
     409             :  * sensitive accounts (dealing with e-Commerce and similar) it is a good idea
     410             :  * to use this form of cookie.
     411             :  *
     412             :  * \sa set_delete();
     413             :  * \sa set_expire();
     414             :  * \sa get_type();
     415             :  */
     416           0 : void http_cookie::set_session()
     417             : {
     418             :     // use an invalid date
     419           0 :     f_expire = QDateTime();
     420           0 : }
     421             : 
     422             : 
     423             : /** \brief Set the expiration date of the cookie.
     424             :  *
     425             :  * This function changes the expiration date to the specified date and time.
     426             :  *
     427             :  * In most cases, it is easier to use the set_expire_in() function which uses
     428             :  * now + the specified number of seconds. The result will be the same either
     429             :  * way because we only send an explicit expiration date and not a Max-Age
     430             :  * parameter.
     431             :  *
     432             :  * In order to create a session cookie (the default), you may set the date
     433             :  * to an invalid date or call the set_session() function:
     434             :  *
     435             :  * \code
     436             :  *   QDateTime invalid;
     437             :  *   cookie.set_expiration(invalid);
     438             :  * \endcode
     439             :  *
     440             :  * To delete a cookie, you can set the expiration date to a date in the past.
     441             :  * This is also achieved by calling the set_delete() function.
     442             :  *
     443             :  * \note
     444             :  * If the date represents a date more than 1 year in the future, then it
     445             :  * gets clamped.
     446             :  *
     447             :  * \param[in] date_time  The new expiration date and time.
     448             :  *
     449             :  * \sa set_session();
     450             :  * \sa set_delete();
     451             :  * \sa get_expire();
     452             :  * \sa get_type();
     453             :  */
     454           0 : void http_cookie::set_expire(QDateTime const & date_time)
     455             : {
     456           0 :     time_t const seconds(date_time.toTime_t() - (f_snap != nullptr
     457           0 :                                                         ? f_snap->get_start_time()
     458           0 :                                                         : time(nullptr)));
     459           0 :     if(seconds > 86400LL * 365LL)
     460             :     {
     461             :         // save 'now + 1 year' instead of date_time which is further in
     462             :         // the future and thus not HTTP 1.1 compatible
     463             :         //
     464           0 :         int64_t const start_date(f_snap != nullptr
     465           0 :                                     ? f_snap->get_start_date()
     466           0 :                                     : snap_child::get_current_date());
     467           0 :         f_expire = QDateTime::fromMSecsSinceEpoch(start_date / 1000LL + 86400LL * 1000LL);
     468             :     }
     469             :     else
     470             :     {
     471           0 :         f_expire = date_time;
     472             :     }
     473           0 : }
     474             : 
     475             : 
     476             : /** \brief This function sets the expiration date seconds in the future.
     477             :  *
     478             :  * This function is most often the one used and allows you to set the
     479             :  * expiration date of the cookie the specified number of seconds in
     480             :  * the future.
     481             :  *
     482             :  * The function makes use of the snap child start date plus that number
     483             :  * of seconds, but it sends the cookie with an Expire field (i.e. we do
     484             :  * not make use of the Max-Age field.)
     485             :  *
     486             :  * \note
     487             :  * If the HTTP cookie object was passed a pointer to the snap child
     488             :  * object, then the request start date is used, otherwise the current
     489             :  * date is used as the fallback.
     490             :  *
     491             :  * \param[in] seconds  The number of seconds from now when the cookie expires.
     492             :  *
     493             :  * \sa set_expire();
     494             :  * \sa get_expire();
     495             :  * \sa set_delete();
     496             :  * \sa set_session();
     497             :  */
     498           0 : void http_cookie::set_expire_in(int64_t seconds)
     499             : {
     500             :     // clamp to 1 year (max. allowed by HTTP 1.1)
     501           0 :     if(seconds > 86400LL * 365LL)
     502             :     {
     503           0 :         seconds = 86400LL * 365LL;
     504             :     }
     505             : 
     506           0 :     int64_t const start_date(f_snap != nullptr
     507           0 :                                 ? f_snap->get_start_date()
     508           0 :                                 : snap_child::get_current_date());
     509           0 :     f_expire = QDateTime::fromMSecsSinceEpoch(start_date / 1000LL + seconds * 1000LL);
     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(QString 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_url(QString const & comment_url)
     586             : {
     587           0 :     f_comment_url = comment_url;
     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 : const QString& 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             :  * const QByteArray& 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 : const QByteArray& 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.isValid())
     644             :     {
     645           0 :         return http_cookie_type_t::HTTP_COOKIE_TYPE_SESSION;
     646             :     }
     647             :     // TBD -- Utc? or not Utc? As far as I know the cookie date is
     648             :     //        in UTC so we need to compare with the UTC current date
     649           0 :     if(f_expire < QDateTime::currentDateTimeUtc())
     650             :     {
     651           0 :         return http_cookie_type_t::HTTP_COOKIE_TYPE_DELETE;
     652             :     }
     653           0 :     return http_cookie_type_t::HTTP_COOKIE_TYPE_PERMANENT;
     654             : }
     655             : 
     656             : 
     657             : /** \brief Get the cookie domain information.
     658             :  *
     659             :  * This function retreives the current domain information of the cookie.
     660             :  * In general the domain is equal to the website key.
     661             :  *
     662             :  * \return The domain defined in this cookie.
     663             :  *
     664             :  * \sa set_domain();
     665             :  */
     666           0 : const QString& http_cookie::get_domain() const
     667             : {
     668           0 :     return f_domain;
     669             : }
     670             : 
     671             : 
     672             : /** \brief Retrieve the path under which the cookie is valid.
     673             :  *
     674             :  * This function retrieves the current path for this cookie.
     675             :  * By default it is set to "/" which means the cookie is
     676             :  * available over the entire website. It is rarely necessary
     677             :  * to change this value.
     678             :  *
     679             :  * \return The current path for this cookie.
     680             :  *
     681             :  * \sa set_path();
     682             :  */
     683           0 : const QString& http_cookie::get_path() const
     684             : {
     685           0 :     return f_path;
     686             : }
     687             : 
     688             : 
     689             : /** \brief Get the expiration date.
     690             :  *
     691             :  * This function returns the current expiration date. The date represents
     692             :  * different status of the cookie which can be determined by calling the
     693             :  * get_type() function.
     694             :  *
     695             :  * The default is an invalid cookie which means that the cookie is a
     696             :  * session cookie (lasts until the browser is closed.)
     697             :  *
     698             :  * \return The expiration date of this cookie.
     699             :  *
     700             :  * \sa set_expire();
     701             :  * \sa set_expire_in();
     702             :  * \sa set_delete();
     703             :  * \sa set_session();
     704             :  * \sa get_type();
     705             :  */
     706           0 : const QDateTime& http_cookie::get_expire() const
     707             : {
     708           0 :     return f_expire;
     709             : }
     710             : 
     711             : 
     712             : /** \brief Retrieve whether the cookie is secure.
     713             :  *
     714             :  * This function returns whether the cookie should only travel on a
     715             :  * secure (SSL) connection.
     716             :  *
     717             :  * The default is false (cookie is visible on non-secure connections.)
     718             :  *
     719             :  * \return true if the cookie is only for secure connections, false otherwise.
     720             :  *
     721             :  * \sa set_secure();
     722             :  */
     723           0 : bool http_cookie::get_secure() const
     724             : {
     725           0 :     return f_secure;
     726             : }
     727             : 
     728             : 
     729             : /** \brief Retrieve whether the cookie is only for HTTP.
     730             :  *
     731             :  * By default cookies are visible to JavaScript and other client languages.
     732             :  * This parameter can be used to hide the content of the cookie from praying
     733             :  * eyes and only allow the data to travel between the browser and server.
     734             :  *
     735             :  * The default is false.
     736             :  *
     737             :  * \return The current HttpOnly flag status.
     738             :  *
     739             :  * \sa set_http_only();
     740             :  */
     741           0 : bool http_cookie::get_http_only() const
     742             : {
     743           0 :     return f_http_only;
     744             : }
     745             : 
     746             : 
     747             : /** \brief Retrieve the cookie comment.
     748             :  *
     749             :  * This function returns the verbatim cookie comment.
     750             :  *
     751             :  * \return The verbatim comment of the cookie.
     752             :  *
     753             :  * \sa set_comment();
     754             :  */
     755           0 : QString const& http_cookie::get_comment() const
     756             : {
     757           0 :     return f_comment;
     758             : }
     759             : 
     760             : 
     761             : /** \brief Retrieve the cookie comment URL.
     762             :  *
     763             :  * This function returns the cookie comment URL.
     764             :  *
     765             :  * \return The comment URL of the cookie.
     766             :  *
     767             :  * \sa set_comment_url();
     768             :  */
     769           0 : QString const& http_cookie::get_comment_url() const
     770             : {
     771           0 :     return f_comment_url;
     772             : }
     773             : 
     774             : 
     775             : /** \brief Transform the cookie for the HTTP header.
     776             :  *
     777             :  * This function transforms the cookie so it works as an HTTP header.
     778             :  * It follows the HTTP 1.1 specifications, although it never makes
     779             :  * use of the Max-Age field.
     780             :  *
     781             :  * \return A valid HTTP cookie header.
     782             :  */
     783           0 : QString http_cookie::to_http_header() const
     784             : {
     785             :     // Note: the name was already checked for invalid characters
     786           0 :     QString result("Set-Cookie: " + f_name + "=");
     787             : 
     788           0 :     char const * v(f_value.constData());
     789           0 :     int const max_len(f_value.size());
     790           0 :     for(int i(0); i < max_len; ++i)
     791             :     {
     792           0 :         char c(v[i]);
     793           0 :         if(c == 0x21
     794           0 :         || (c >= 0x23 && c <= 0x2B)
     795           0 :         || (c >= 0x2D && c <= 0x3A)
     796           0 :         || (c >= 0x3C && c <= 0x5B)
     797           0 :         || (c >= 0x5D && c <= 0x7E))
     798             :         {
     799           0 :             result += c;
     800             :         }
     801             :         else
     802             :         {
     803             :             // add the byte as %XX
     804           0 :             result += QString("%%%1").arg(c, 2, 16, QChar('0'));
     805             :         }
     806             :     }
     807             : 
     808           0 :     switch(get_type())
     809             :     {
     810           0 :     case http_cookie_type_t::HTTP_COOKIE_TYPE_PERMANENT:
     811             :         // compute date/time
     812             :         // HTTP format generates: Sun, 06 Nov 1994 08:49:37 GMT
     813             :         // (see http://tools.ietf.org/html/rfc2616#section-3.3.1)
     814             :         //
     815           0 :         result += "; Expires=" + f_expire.toString("ddd, dd MMM yyyy hh:mm:ss' GMT'");
     816             : 
     817             :         // Modern browsers are expected to use the Max-Age=... field
     818             :         // instead of the Expires to avoid potential date synchronization
     819             :         // problems between our server and the client
     820             :         // (see http://tools.ietf.org/html/rfc6265#section-4.1.2.2)
     821             :         //
     822             :         // TBD: although this works, we may want to know the exact
     823             :         //      intend of the person setting the expiration time and
     824             :         //      maybe use that amount (or even change our current
     825             :         //      expire to a max-age and calculate the date in Expires=...
     826             :         //      and not the one in Max-Age.)
     827             :         {
     828           0 :             time_t const max_age(f_expire.toTime_t() - time(nullptr));
     829           0 :             if(max_age > 0)
     830             :             {
     831           0 :                 result += QString("; Max-Age=%1").arg(max_age);
     832             :             }
     833             :         }
     834           0 :         break;
     835             : 
     836           0 :     case http_cookie_type_t::HTTP_COOKIE_TYPE_SESSION:
     837             :         // no Expires
     838           0 :         break;
     839             : 
     840           0 :     case http_cookie_type_t::HTTP_COOKIE_TYPE_DELETE:
     841             :         // no need to waste time computing that date
     842           0 :         result += "; Expires=Thu, 01-Jan-1970 00:00:01 GMT";
     843           0 :         break;
     844             : 
     845             :     }
     846             : 
     847           0 :     if(!f_domain.isEmpty())
     848             :     {
     849             :         // the domain sanity was already checked so we can save it as it here
     850           0 :         result += "; Domain=" + f_domain;
     851             :     }
     852             : 
     853           0 :     if(!f_path.isEmpty())
     854             :     {
     855             :         // the path sanity was already checked so we can save it as it here
     856           0 :         result += "; Path=" + f_path;
     857             :     }
     858             : 
     859           0 :     if(f_secure)
     860             :     {
     861           0 :         result += "; Secure";
     862             :     }
     863             : 
     864           0 :     if(f_http_only)
     865             :     {
     866           0 :         result += "; HttpOnly";
     867             :     }
     868             : 
     869           0 :     if(!f_comment.isEmpty())
     870             :     {
     871             :         // we need to escape all "bad" characters, not just quotes
     872           0 :         QString safe(f_comment);
     873           0 :         result += "; Comment=\"" + safe.replace("\"", "") + "\"";
     874             :     }
     875             : 
     876           0 :     if(!f_comment_url.isEmpty())
     877             :     {
     878             :         // we need to escape all "bad" characters, not just quotes
     879           0 :         QString safe(f_comment_url);
     880           0 :         result += "; CommentURL=\"" + safe.replace("\"", "") + "\"";
     881             :     }
     882             : 
     883           0 :     return result;
     884             : }
     885             : 
     886             : 
     887             : 
     888           6 : } // namespace snap
     889             : 
     890             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13