##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2003 Nexedi SARL and Contributors. All Rights Reserved.
#          Sebastien Robin <seb@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

"""
Patch CookieCrumbler to prevent came_from to appear in the URL
when ERP5 runs in "require_referer" mode.
"""

from Products.CMFCore.CookieCrumbler import CookieCrumbler
from Products.CMFCore.CookieCrumbler import CookieCrumblerDisabled
from urllib import quote, unquote
from ZPublisher.HTTPRequest import HTTPRequest

ATTEMPT_NONE = 0       # No attempt at authentication
ATTEMPT_LOGIN = 1      # Attempt to log in
ATTEMPT_RESUME = 2     # Attempt to resume session

from base64 import standard_b64encode, standard_b64decode
from DateTime import DateTime

class PatchedCookieCrumbler(CookieCrumbler):
  """
    This class is only for backward compatibility.
  """
  pass
    
def getLoginURL(self):
    '''
    Redirects to the login page.
    '''
    if self.auto_login_page:
        req = self.REQUEST
        resp = req['RESPONSE']
        iself = getattr(self, 'aq_inner', self)
        parent = getattr(iself, 'aq_parent', None)
        page = getattr(parent, self.auto_login_page, None)
        if page is not None:
            retry = getattr(resp, '_auth', 0) and '1' or ''
            came_from = req.get('came_from', None)
            if came_from is None:
                came_from = req['URL']
            if hasattr(self, 'getPortalObject') and self.getPortalObject()\
                          .getProperty('require_referer', 0) :
              url = '%s?retry=%s&disable_cookie_login__=1' % (
                page.absolute_url(), retry)
            else :
              url = '%s?came_from=%s&retry=%s&disable_cookie_login__=1' % (
                page.absolute_url(), quote(came_from), retry)
            return url
    return None

CookieCrumbler.getLoginURL = getLoginURL

def modifyRequest(self, req, resp):
  """Copies cookie-supplied credentials to the basic auth fields.
  
  Returns a flag indicating what the user is trying to do with
  cookies: ATTEMPT_NONE, ATTEMPT_LOGIN, or ATTEMPT_RESUME.  If
  cookie login is disabled for this request, raises
  CookieCrumblerDisabled.
  """
  if (req.__class__ is not HTTPRequest
      or not req['REQUEST_METHOD'] in ('HEAD', 'GET', 'PUT', 'POST')
      or req.environ.has_key('WEBDAV_SOURCE_PORT')):
      raise CookieCrumblerDisabled

  # attempt may contain information about an earlier attempt to
  # authenticate using a higher-up cookie crumbler within the
  # same request.
  attempt = getattr(req, '_cookie_auth', ATTEMPT_NONE)

  if attempt == ATTEMPT_NONE:
    if req._auth:
      # An auth header was provided and no cookie crumbler
      # created it.  The user must be using basic auth.
      raise CookieCrumblerDisabled

    if req.has_key(self.pw_cookie) and req.has_key(self.name_cookie):
      # Attempt to log in and set cookies.
      attempt = ATTEMPT_LOGIN
      name = req[self.name_cookie]
      pw = req[self.pw_cookie]
      ac = standard_b64encode('%s:%s' % (name, pw))
      self._setAuthHeader(ac, req, resp)
      if req.get(self.persist_cookie, 0):
        # Persist the user name (but not the pw or session)
        expires = (DateTime() + 365).toZone('GMT').rfc822()
        resp.setCookie(self.name_cookie, name,
                       path=self.getCookiePath(),
                       expires=expires)
      else:
        # Expire the user name
        resp.expireCookie(self.name_cookie,
                          path=self.getCookiePath())
      method = self.getCookieMethod( 'setAuthCookie'
                                     , self.defaultSetAuthCookie )
      method( resp, self.auth_cookie, quote( ac ) )
      self.delRequestVar(req, self.name_cookie)
      self.delRequestVar(req, self.pw_cookie)

    elif req.has_key(self.auth_cookie):
      # Attempt to resume a session if the cookie is valid.
      # Copy __ac to the auth header.
      ac = unquote(req[self.auth_cookie])
      if ac and ac != 'deleted':
        try:
          standard_b64decode(ac)
        except:
          # Not a valid auth header.
          pass
        else:
          attempt = ATTEMPT_RESUME
          self._setAuthHeader(ac, req, resp)
          self.delRequestVar(req, self.auth_cookie)
          method = self.getCookieMethod(
            'twiddleAuthCookie', None)
          if method is not None:
            method(resp, self.auth_cookie, quote(ac))
              
  req._cookie_auth = attempt
  return attempt

CookieCrumbler.modifyRequest = modifyRequest


def credentialsChanged(self, user, name, pw):
  ac = standard_b64encode('%s:%s' % (name, pw))
  method = self.getCookieMethod( 'setAuthCookie'
                                 , self.defaultSetAuthCookie )
  resp = self.REQUEST['RESPONSE']
  method( resp, self.auth_cookie, quote( ac ) )

CookieCrumbler.credentialsChanged = credentialsChanged
