########################################################################
# web2ldap
# (c) by Michael Stroeder <michael@stroeder.com>
# Distributed under GNU Public License (GPL)
# web-based LDAP Client, see http://www.web2ldap.de for details
########################################################################

Version = '0.8.1'

import sys, os, string, types, time, re, \
       cgiforms, msbase, ldap, ldapbase, cgissl, cgiforms, web2ldapcnf, w2lcnf, msgzip

from ldapbase import ParentDN,ParentDNList,is_dn,normalize_dn,dn_pattern

def utf2display(dispcharset,data):
  if type(data)==types.UnicodeType:
    return cgiforms.escapeHTML(data.encode(dispcharset))
  elif type(data) is types.StringType:
    if data[0:6]=='{T.61}':
      return cgiforms.escapeHTML(unicode(data[6:],'iso-8859-1').encode(dispcharset))
    else:
      return cgiforms.escapeHTML(unicode(data,'utf-8').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 iso2display(dispcharset,data):
  if type(data) is types.StringType:
    return cgiforms.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 used throughout the whole script
########################################################################

host_pattern = '([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z]+[a-zA-Z0-9-]*(\.[a-zA-Z]+[a-zA-Z0-9-]*)*)(:[0-9]*|)'

ldap_binaryattr = msbase.CaseinsensitiveStringKeyDict(web2ldapcnf.ldap_binaryattr)
ldap_binaryattrkeys = map(lambda i,u=unicode:u(i,'iso-8859-1'),ldap_binaryattr.keys())

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

search_options = (
  (r'(%s=*%s*)','contains'),
  (r'(!(%s=*%s*))',"doesn't contain"),
  (r'(%s=%s)','is'),
  (r'(!(%s=%s))','is not'),
  (r'(%s=%s*)','begins with'),
  (r'(%s=*%s)','ends with'),
  (r'(%s~=%s)','sounds like'),
  (r'(%s>=%s)','greater equal than'),
  (r'(%s<=%s)','lesser equal than')
)

class ErrorExitClass:

  def __init__(self,ls,Msg):
    self.ls = ls
    self.Msg = Msg

def LDAPSessionParams(form,ls):
  """Pull the LDAP session parameters out of current form input"""
  for param in ['who','cred','host','dn']:
    if param in form.inputkeys:
      setattr(ls,param,form.field[param][0].content)
  return ls

def LDAPConnect(ls):
  """Connect and bind to LDAP host"""
  # Try connecting to LDAP host
  ls.open(ls.host)
  # Switch off chasing referrals and reconnecting
  ls.l.options=0
  if not ls.who:
    who = ''
  elif ldapbase.is_dn(ls.who):
    who = ls.who
  else:
    ls.l.bind_s('','',ldap.AUTH_SIMPLE)
    if ls.dn:
      search_root = ldapbase.match_dn(
        ls.dn,
        ldapbase.GetNamingContexts(ls.l)
      )
    else:
      search_root = ''
    who = ldapbase.SmartLogin(
      ls.l,
      ls.who,
      search_root,
      w2lcnf.GetParam(ls,'binddnsearch',''),
      attrnamesonly=w2lcnf.GetParam(ls,'search_attrsonly',1),
    )
    ls.who=who
  if ls.cred:
    cred=ls.cred
  else:
    cred=''
  ls.l.bind_s(who,cred,ldap.AUTH_SIMPLE)
  return ls


# Check if current security level is sufficient for command
def CheckSecurityLevel(command,ls):
  sec_valid_dn = w2lcnf.GetParam(ls,'sec_valid_dn','')
  sec_valid_idn = w2lcnf.GetParam(ls,'sec_valid_idn','')
  sec_ssllevel = cgissl.SecLevel(
    web2ldapcnf.sec_sslacceptedciphers,
    sec_valid_dn,
    sec_valid_idn
  )
  # Ist der Sicherheitslevel ausreichend fuer das geforderte Kommando?
  sec_reqlevel = w2lcnf.GetParam(ls,'sec_reqlevel',2)
  if sec_ssllevel < sec_reqlevel:
    raise ErrorExitClass(ls,'Security level %d required for executing command %s on host %s! The security level of your access was only %d.' % (sec_reqlevel,command,ls.host,sec_ssllevel)
    )
  return
