"""
w2lapp.core.py: some core functions used throughout web2ldap

web2ldap - a web-based LDAP Client,
see http://www.web2ldap.de for details

(c) by Michael Stroeder <michael@stroeder.com>

This module is distributed under the terms of the
GPL (GNU GENERAL PUBLIC LICENSE) Version 2
(see http://www.gnu.org/copyleft/gpl.html)

$Id: core.py,v 1.45 2002/01/09 01:40:10 michael Exp $
"""

import sys,os,signal,types,time,traceback,\
       pyweblib.forms,pyweblib.session,msbase, \
       w2lapp.cnf,ldapoc

from pyweblib.forms import escapeHTML


def utf2display(dispcharset,value,escape_entities=1):
  if type(value)==types.UnicodeType:
    unicode_value = value
  elif type(value) is types.StringType:
    if value[0:6]=='{T.61}':
      unicode_value = unicode(value[6:],'iso-8859-1')
    else:
      try:
        unicode_value = unicode(value,'utf-8')
      except UnicodeError:
        unicode_value = unicode(value,'iso-8859-1')
  elif value is None:
    return ''
  elif type(value) is types.ListType:
    return [
      utf2display(dispcharset,i,escape_entities)
      for i in value
    ]
  else:
    raise TypeError, "expected string or list"
  if escape_entities:
    return escapeHTML(unicode_value.encode(dispcharset))
  else:
    return unicode_value.encode(dispcharset)

def iso2display(dispcharset,data):
  if type(data) is types.StringType:
    return pyweblib.forms.escapeHTML(unicode(data,'iso-8859-1').encode(dispcharset))
  elif type(data) is types.ListType:
    l = []
    for i in data:
      l.append(utf2display(dispcharset,i))
    return l
  elif data is None:
    return ''
  else:
    raise TypeError, "expected string or list"

def input2utf(inputcharset,s):
  if type(s)==types.UnicodeType:
    return s.encode('utf-8')
  else:
    return unicode(s,inputcharset).encode('utf-8')

########################################################################
# Initialize some constants
########################################################################

startUpTime = time.time()

# Initialize web session object
session = pyweblib.session.WebSession(
  expireDeactivate=w2lapp.cnf.misc.session_relogin,
  expireRemove=w2lapp.cnf.misc.session_remove,
  crossCheckVars = w2lapp.cnf.misc.session_checkvars,
)

class CleanUpThread(pyweblib.session.CleanUpThread):
  """
  Thread class for clean-up thread
  
  Mainly it overrides pyweblib.session.CleanUpThread.run()
  to call ldapSession.unbind().
  """

  def run(self):
    """Thread function for cleaning up session database"""
    while not self._stop_event.isSet():
      try:
        current_time = time.time()
        sessiondict_keys = [
          id
          for id in self._sessionInstance.sessiondict.keys()
          if not (id is None or id.startswith('__'))
        ]
        for session_id in sessiondict_keys:
          try:
            session_timestamp,ldapSession = self._sessionInstance.sessiondict[session_id]
          except KeyError:
            # Avoid race condition. The session might have been
            # deleted in the meantime. But make sure everything is deleted.
            self._sessionInstance.deleteSession(session_id)
          else:
            # Check expiration time
            if (session_timestamp+self._sessionInstance.expireDeactivate<current_time):
              if ldapSession=='_created_':
                # Completely hanging sessions are removed immediately
                self._sessionInstance.deleteSession(session_id)
                self._removed+=1
              else:
                # Disconnect from LDAP server
                ldapSession.unbind()
            if session_timestamp+self._sessionInstance.expireRemove<current_time:
              # Remove expired session
              self._sessionInstance.deleteSession(session_id)
              self._removed+=1
      except:
        # Catch all exceptions to avoid thread being killed.
        if __debug__:
          traceback.print_exc()
        pass

      # Sleeping until next turn
      self._stop_event.wait(self._interval)

    return # CleanUpThread.run()


# Mapping from sndhdr.what() result to MIME-type
sndhdr_mimetype = {
  'au':'audio/basic',
  'wav':'audio/wav',
  'aiff':'audio/x-aiff',
  'aifc':'audio/x-aiff',
}
# Mapping from imghdr.what() result to MIME-type
imghdr_mimetype = {
  'bmp':'image/bmp',
  'gif':'image/gif',
  'jpeg':'image/jpeg',
  'pbm':'image/x-portable-bitmap',
  'pgm':'image/x-portable-graymap',
  'png':'image/png',
  'ppm':'image/x-portable-pixmap',
  'rast':'image/x-cmu-raster',
  'rgb':'image/x-rgb',
  'tiff':'image/tiff',
  'xbm':'image/x-xbitmap',
}

ldap_binaryattr = msbase.CaseinsensitiveStringKeyDict(w2lapp.cnf.misc.ldap_binaryattr)
ldap_binaryattrkeys = ldap_binaryattr.keys()

# Define a attribute name <-> user friendly attribute name mapping
ldap_knownattr = msbase.CaseinsensitiveStringKeyDict(w2lapp.cnf.misc.ldap_knownattr)
for i in ldap_binaryattrkeys:
  ldap_knownattr[unicode(i)] = ldap_binaryattr[i][0]

ldap_known_objectclasses = ldapoc.ldap_oc.keys()
ldap_known_objectclasses.sort()

class ErrorExitClass:
  """Base class for web2ldap application exceptions"""
  def __init__(self,ls,dn,Msg):
    self.ls = ls
    self.dn = dn
    self.Msg = Msg

def log_exception(errf,sid,command,form,ls,env):
  """
  Write an exception with environment vars, LDAP connection data
  and Python traceback to errf file object.
  """
  # Get exception instance and traceback info
  exc_obj,exc_value,exc_traceback = sys.exc_info()
  # Signals are raised again to trigger handling in main process
  if isinstance(exc_value,KeyboardInterrupt):
    raise
  if isinstance(exc_value,IOError) and (exc_value.errno==32):
    pass
  else:
    logentry = ['%s\nUnhandled error at %s' % (
        '-'*60,
        time.strftime(
          '%Y-%m-%dT%H:%M:%SZ',time.gmtime(time.time())
        ),
      )]
    logentry.append('LDAPSession instance: %s' % repr(ls))
    # Log all environment vars
    env_vars = env.keys();env_vars.sort()
    for v in env_vars:
      logentry.append('%s: "%s"' % (v,env[v]))
    logentry.append(''.join(traceback.format_exception(exc_obj,exc_value,exc_traceback,20)))
    # Write the log entry to errf file object
    errf.write(os.linesep.join(logentry))
  # Avoid memory leaks
  exc_obj=None;exc_value=None;exc_traceback=None
  del exc_obj;del exc_value;del exc_traceback
