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
|