##############################################################################
#
# Copyright (c) 2008 Nexedi SARL and Contributors. All Rights Reserved.
#                    Aurelien Calonne <aurel@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 program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################


from AccessControl import ClassSecurityInfo
from Globals import InitializeClass, DTMLFile, get_request
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type import Permissions
from Products.ERP5 import _dtmldir
from zLOG import LOG
import time, random, md5
from DateTime import DateTime
from Products.ERP5Type.Message import translateString
from Acquisition import aq_base
from BTrees.OOBTree import OOBTree


class PasswordTool(BaseTool):
  """
    PasswoordTool is used to allow a user to change its password
  """
  title = 'Password Tool'
  id = 'portal_password'
  meta_type = 'ERP5 Password Tool'
  portal_type = 'Password Tool'
  allowed_types = ()

  # Declarative Security
  security = ClassSecurityInfo()

  security.declareProtected(Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainPasswordTool', _dtmldir )


  _expiration_day = 1
  password_request_dict = {}
  
  def __init__(self):
    self.password_request_dict = OOBTree()

  def mailPasswordResetRequest(self, user_login=None, REQUEST=None):
    """
    Create a ramdom string and expiration date for request
    """
    if user_login is None:
      user_login = REQUEST["user_login"]

    # check user exists
    user_list = self.portal_catalog.unrestrictedSearchResults(portal_type="Person", reference=user_login)
    if len(user_list) == 0:
      msg = translateString("User ${user} does not exist.",
                            mapping={'user':user_login})
      if REQUEST is not None:
        ret_url = '%s/login_form?portal_status_message=%s' % \
                  (self.getPortalObject().absolute_url(),msg)
        return REQUEST.RESPONSE.redirect( ret_url )
      else:
        return msg

    user = user_list[0].getObject()
    # generate a ramdom string
    random_url = self._generateUUID()
    url = "%s/portal_password/resetPassword?key=%s" %(self.getPortalObject().absolute_url() , random_url)
    # generate expiration date
    expiration_date = DateTime() + self._expiration_day
    # register request
    self.password_request_dict = {random_url : (user_login, expiration_date)}

    # send mail
    subject = "[%s] Reset of your password" %(self.getPortalObject().getTitle())
    message = "\nYou requested to reset your %s account password.\n\n" \
              "Please copy and paste the following link into your browser: \n%s\n\n" \
              "Please note that this link will be valid only one time, until %s.\n" \
              "After this date, or after having used this link, you will have to make " \
              "a new request\n\n" \
              "Thank you" %(self.getPortalObject().getTitle(), url, expiration_date)    
    self.portal_notifications.sendMessage(sender=None, recipient=[user,], subject=subject, message=message)
    if REQUEST is not None:
      msg = translateString("An email has been sent to you.")
      ret_url = '%s/login_form?portal_status_message=%s' % \
                (self.getPortalObject().absolute_url(),msg)
      return REQUEST.RESPONSE.redirect( ret_url )
  

  def _generateUUID(self, args=""):
    """
    Generate a unique id that will be used as url for password
    """
    # this code is based on
    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213761
    # by Carl Free Jr
    # as uuid module is only available in pyhton 2.5
    t = long( time.time() * 1000 )
    r = long( random.random()*100000000000000000L )
    try:
      a = socket.gethostbyname( socket.gethostname() )
    except:
      # if we can't get a network address, just imagine one
      a = random.random()*100000000000000000L
    data = str(t)+' '+str(r)+' '+str(a)+' '+str(args)
    data = md5.md5(data).hexdigest()
    return data


  def resetPassword(self, key=None, REQUEST=None):
    """
    """
    if REQUEST is None:
      REQUEST = get_request()
    user_login, expiration_date = self.password_request_dict.get(key, (None, None))
    if key is None or user_login is None:
      ret_url = '%s/login_form' % self.getPortalObject().absolute_url()
      return REQUEST.RESPONSE.redirect( ret_url )

    # check date
    current_date = DateTime()
    if current_date > expiration_date:
      msg = translateString("Date has expire.")
      ret_url = '%s/login_form?portal_status_message=%s' % \
                (self.getPortalObject().absolute_url(), msg)
      return REQUEST.RESPONSE.redirect( ret_url )
      
    # redirect to form as all is ok
    REQUEST.set("password_key", key)
    return self.reset_password_form(REQUEST=REQUEST)


  def removeExpiredRequests(self, **kw):
    """
    Browse dict and remove expired request
    """
    current_date = DateTime()
    for key, (login, date) in self.password_request_dict.items():
      if date < current_date:
        self.password_request_dict.pop(key)
        
         
  def changeUserPassword(self, user_login, password, password_confirmation, password_key, REQUEST=None):
    """
    Reset the password for a given login    
    """
    # check the key
    register_user_login, expiration_date = self.password_request_dict.get(password_key, (None, None))

    current_date = DateTime()
    msg = None
    if register_user_login is None:
      msg = ""
    elif register_user_login != user_login:
      msg = translateString("Bad login provided.")
    elif current_date > expiration_date:
      msg = translateString("Date has expire.")
    elif password != password_confirmation:
      msg = translateString("Passwords do not match.")
    if msg is not None:
      if REQUEST is not None:
        ret_url = '%s/login_form?portal_status_message=%s' % \
                  (self.getPortalObject().absolute_url(), msg)
        return REQUEST.RESPONSE.redirect( ret_url )
      else:
        return msg

    # all is OK, change password and remove it from request dict
    self.password_request_dict.pop(password_key)
    persons = self.acl_users.erp5_users.getUserByLogin(user_login)              
    person = persons[0]
    person._setPassword(password)
    person.reindexObject()
    if REQUEST is not None:
      msg = translateString("Password changed.")
      ret_url = '%s/login_form?portal_status_message=%s' % \
                (self.getPortalObject().absolute_url(), msg)
      return REQUEST.RESPONSE.redirect( ret_url )
    
InitializeClass(PasswordTool)
