17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
18 """abstract class for http request""" |
18 """abstract class for http request""" |
19 |
19 |
20 __docformat__ = "restructuredtext en" |
20 __docformat__ = "restructuredtext en" |
21 |
21 |
22 import Cookie |
|
23 import hashlib |
22 import hashlib |
24 import time |
23 import time |
25 import random |
24 import random |
26 import base64 |
25 import base64 |
|
26 from Cookie import SimpleCookie |
|
27 from calendar import timegm |
27 from datetime import date |
28 from datetime import date |
28 from urlparse import urlsplit |
29 from urlparse import urlsplit |
29 from itertools import count |
30 from itertools import count |
30 from warnings import warn |
31 from warnings import warn |
31 |
32 |
40 from cubicweb.uilib import remove_html_tags, js |
41 from cubicweb.uilib import remove_html_tags, js |
41 from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid |
42 from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid |
42 from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT |
43 from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT |
43 from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit, |
44 from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit, |
44 RequestError, StatusResponse) |
45 RequestError, StatusResponse) |
45 from cubicweb.web.http_headers import Headers |
46 from cubicweb.web.httpcache import GMTOFFSET |
|
47 from cubicweb.web.http_headers import Headers, Cookie |
46 |
48 |
47 _MARKER = object() |
49 _MARKER = object() |
48 |
50 |
49 def build_cb_uid(seed): |
51 def build_cb_uid(seed): |
50 sha = hashlib.sha1('%s%s%s' % (time.time(), seed, random.random())) |
52 sha = hashlib.sha1('%s%s%s' % (time.time(), seed, random.random())) |
516 else: |
518 else: |
517 return self.header_authorization() |
519 return self.header_authorization() |
518 |
520 |
519 def get_cookie(self): |
521 def get_cookie(self): |
520 """retrieve request cookies, returns an empty cookie if not found""" |
522 """retrieve request cookies, returns an empty cookie if not found""" |
|
523 # XXX use http_headers implementation |
521 try: |
524 try: |
522 return Cookie.SimpleCookie(self.get_header('Cookie')) |
525 return SimpleCookie(self.get_header('Cookie')) |
523 except KeyError: |
526 except KeyError: |
524 return Cookie.SimpleCookie() |
527 return SimpleCookie() |
525 |
528 |
526 def set_cookie(self, cookie, key, maxage=300, expires=None): |
529 def set_cookie(self, name, value, maxage=300, expires=None, secure=False): |
527 """set / update a cookie key |
530 """set / update a cookie |
528 |
531 |
529 by default, cookie will be available for the next 5 minutes. |
532 by default, cookie will be available for the next 5 minutes. |
530 Give maxage = None to have a "session" cookie expiring when the |
533 Give maxage = None to have a "session" cookie expiring when the |
531 client close its browser |
534 client close its browser |
532 """ |
535 """ |
533 morsel = cookie[key] |
536 if isinstance(name, SimpleCookie): |
534 if maxage is not None: |
537 warn('[3.13] set_cookie now takes name and value as two first ' |
535 morsel['Max-Age'] = maxage |
538 'argument, not anymore cookie object and name', |
536 if expires: |
539 DeprecationWarning, stacklevel=2) |
537 morsel['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S %z') |
540 secure = name[value]['secure'] |
|
541 name, value = value, name[value].value |
|
542 if maxage: # don't check is None, 0 may be specified |
|
543 expires = maxage + time.time() |
|
544 assert expires is None, 'both max age and expires cant be specified' |
|
545 elif expires: |
|
546 expires = timegm((expires + GMTOFFSET).timetuple()) |
|
547 else: |
|
548 expires = None |
538 # make sure cookie is set on the correct path |
549 # make sure cookie is set on the correct path |
539 morsel['path'] = self.base_url_path() |
550 cookie = Cookie(name, value, self.base_url_path(), expires=expires, |
540 self.add_header('Set-Cookie', morsel.OutputString()) |
551 secure=secure) |
541 |
552 self.headers_out.addHeader('Set-cookie', cookie) |
542 def remove_cookie(self, cookie, key): |
553 |
|
554 def remove_cookie(self, name, bwcompat=None): |
543 """remove a cookie by expiring it""" |
555 """remove a cookie by expiring it""" |
544 self.set_cookie(cookie, key, maxage=0, expires=date(1970, 1, 1)) |
556 if bwcompat is not None: |
|
557 warn('[3.13] remove_cookie now take only a name as argument', |
|
558 DeprecationWarning, stacklevel=2) |
|
559 name = bwcompat |
|
560 self.set_cookie(key, '', maxage=0, expires=date(1970, 1, 1)) |
545 |
561 |
546 def set_content_type(self, content_type, filename=None, encoding=None): |
562 def set_content_type(self, content_type, filename=None, encoding=None): |
547 """set output content type for this request. An optional filename |
563 """set output content type for this request. An optional filename |
548 may be given |
564 may be given |
549 """ |
565 """ |