########################################################################
# 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
########################################################################

import sys, string, re, \
       cgiforms, ldap, w2lcore, w2lcnf, web2ldapcnf, msbase, w2lgui
from ldapbase import ParentDN
from msbase import union,listremove

########################################################################
# Input form for add and modify
########################################################################

def w2l_AddModifyForm(outf,command,form,ls):

  def ObjectClassForm(outf,form,command,ls,ldap_rdn):

    outf.write('<div id=InputDiv>')

    if command=='addform':
      outf.write('<h1>Add new entry</h1>\nChoose object class(es) for new entry.')
    elif command=='modifyform':
      outf.write('<h1>Modify entry</h1>\nYou may change the object class(es) for the entry.')

    if ldap_rdn:
      ldap_rdn_input = '<input type=hidden name="ldap_rdn" value="%s">' % w2lcore.utf2display(form.accept_charset,ldap_rdn)
    else:
      ldap_rdn_input = ''

    outf.write(
      w2lgui.CommandButton(
	form,command,'Input form',
	ls.who,
	ls.cred,
	ls.host,
        ls.dn,
	extrastr='%s\n<p>%s</p>\n' % (
          ldap_rdn_input,
          form.field['ldap_oc'][0].inputfield()
        )
      )    
    )

  def PrintInputTable(outf,form,attrs,tableheader='',entry={}):
    outf.write('<tr><th colspan=2>%s</th></tr>' % tableheader)
    for attr in attrs:
      datalist = entry.get(string.lower(attr),entry.get(attr,['']))
      for data in datalist:
	outf.write("""<tr>
      <td><label for="%s">%s</label><input type=hidden name="input_attr" value="%s"></td>
      <td><input name="input_data" id="%s" value="%s" size=50></td>
    </tr>
  """ % (attr,w2lgui.AttrNameStr(attr),attr,attr,w2lcore.utf2display(form.accept_charset,data)))

  from ldapoc import ldap_oc
  known_objectclasses = ldap_oc.keys()
  known_objectclasses.sort()


  entry ={}

  if command=='addform':
    if 'ldap_rdn' in form.inputkeys:
      ldap_rdn = form.field['ldap_rdn'][0].content
    else:
      ldap_rdn = ''
    new_rdn_attr,objectclasses,dit_immutable = w2lcnf.GetParam(ls,'dit',{}).get(ls.dn,('',[],0))
    form.field['ldap_oc'][0].setdefault(objectclasses)
  elif command=='modifyform':
    ldap_rdn = ''
    new_rdn_attr,objectclasses,dit_immutable = w2lcnf.GetParam(ls,'dit',{}).get(ParentDN(ls.dn),('',[],0))

  if not dit_immutable:
    if not 'ldap_oc' in form.inputkeys:
      # User did not specify ldap_oc
      # => output a form for selecting object class(es)
      if command=='modifyform':
        # read existing object classes
	result_dnlist = ls.l.search_s(
          ls.dn.encode('utf-8'),
          ldap.SCOPE_BASE,
          "(objectclass=*)",
          ['objectclass'],
          0
        )
	entry = result_dnlist[0][1]
	existing_objectclasses = entry.get('objectclass',entry.get('objectClass',[]))
        # Add content of current objectClass attribute to
	# select list for choosing objectClass'es
        form.field['ldap_oc'][0].options = union(
          form.field['ldap_oc'][0].options,
          existing_objectclasses,
          ignorecase=0
        )
	form.field['ldap_oc'][0].options.sort()
	form.field['ldap_oc'][0].setdefault(
          map(
            lambda i:unicode(i,'utf-8'),
            existing_objectclasses
          )
        )

      w2lgui.PrintHeader(outf,'',form.accept_charset)
      w2lgui.MainMenuTable(outf,form,ls)
      ObjectClassForm(outf,form,command,ls,ldap_rdn)
      w2lgui.PrintFooter(outf)
      return

    else:
      # Read objectclass(es) from input form
      objectclasses = form.field['ldap_oc'][0].content

  if command=='modifyform':

    # Retrieve attribute types of the entry first
    try:
      # Get all attribute names of entries
      existing_attrs = ls.l.search_s(
        ls.dn.encode('utf-8'),
        ldap.SCOPE_BASE,
        "(objectclass=*)",
        None,
        w2lcnf.GetParam(ls,'search_attrsonly',1)
      )[0][1].keys()
      # Falls nicht nur ein Attributinhalt gelesen werden soll,
      # so werden alle Binaerattribute, fuer die ein
      # MIME-Typ-Mapping angegeben wurde, rausgestrichen
      read_attrs = existing_attrs[:]
      listremove(read_attrs,w2lcore.ldap_binaryattrkeys,1)
      # Read all non-binary attributes of entry
      result_dnlist = ls.l.search_s(
        ls.dn.encode('utf-8'),
        ldap.SCOPE_BASE,
        "(objectclass=*)",
        read_attrs,
        0
      )
      entry = result_dnlist[0][1]
    except ldap.NO_SUCH_OBJECT:
      raise w2lcore.ErrorExitClass(ls,'No entry <STRONG>%s</STRONG>.' % (w2lcore.utf2display(form.accept_charset,ls.dn)))

  ActionStr = '%s%s' % (string.upper(command[0]),command[1:-4])

  required_attrs = []
  allowed_attrs = []
  # Make a case-ignoring string-keyed copy of ldap_oc
  ldap_oc_ci = msbase.CaseinsensitiveStringKeyDict(ldap_oc)

  for oc in objectclasses:
    oc_def = ldap_oc_ci.get(oc,{})
    required_attrs = union(required_attrs,oc_def.get('requires',[]))
    allowed_attrs = union(allowed_attrs,oc_def.get('allows',[]))

  donottouch_attrs = ['objectclass']+w2lcnf.GetParam(ls,'hiddenattrs',[])
  listremove(required_attrs,donottouch_attrs,1)
  listremove(required_attrs,w2lcore.ldap_binaryattrkeys,1)
  listremove(allowed_attrs,donottouch_attrs,1)
  listremove(allowed_attrs,w2lcore.ldap_binaryattrkeys,1)
  listremove(allowed_attrs,required_attrs,1)

  w2lgui.PrintHeader(outf,'',form.accept_charset)
  w2lgui.MainMenuTable(outf,form,ls)
  outf.write("""<div id=InputDiv>
<h1>%s entry</h1>
<form action="%s/%s" method="%s" enctype="application/x-www-form-urlencoded" accept-charset="%s">
""" % (ActionStr,form.script_name,command[:-4],form.request_method,form.accept_charset))

  if command=='addform':
    if ldap_rdn or (not new_rdn_attr or not dit_immutable):
      outf.write('<p>RDN for new entry below <STRONG>%s</STRONG>:<br><INPUT NAME="ldap_rdn" value="%s" SIZE=50></P>' % (w2lcore.utf2display(form.accept_charset,ls.dn),w2lcore.utf2display(form.accept_charset,ldap_rdn)))
  outf.write('<input type=hidden name="dn" SIZE=50 value="%s">' % (w2lcore.utf2display(form.accept_charset,ls.dn)))

  # Input form according to chosen object class(es)
  outf.write("""
  <input type=hidden name="who"  value="%s">
  <input type=hidden name="cred" value="%s">
  <input type=hidden name="host" value="%s">
  <input type="submit" value="%s"> <input type="reset" value="Reset">
""" % ( \
    w2lcore.utf2display(form.accept_charset,ls.who),
    w2lcore.utf2display(form.accept_charset,ls.cred),
    ls.host,
    ActionStr,
  ))

  # Print chosen objectclass(es) as hidden input
  for oc in objectclasses:
    outf.write('<input type=hidden name="ldap_oc" value="%s">' % (oc))

  outf.write("""
<table id=InputTable summary="Input form for LDAP entry data" border rules=rows cellspacing="2%%" cellpadding="2%%">
<tr>
  <td>%s</td>
  <td>%s</td>
</tr>
"""% ( \
    w2lgui.AttrNameStr('objectclass'),
    string.join(objectclasses,'<br>')
  ))

  PrintInputTable(outf,form,required_attrs,'Required attributes',entry)
  PrintInputTable(outf,form,allowed_attrs,'Allowed attributes',entry)
  if command=='modifyform':
    # Print attributes which does not appear in the schema but
    # do exist in the entry
    listremove(read_attrs,['objectclass','modifytimestamp','creatorsname','createtimestamp','modifiersname','userpassword'],1)
    listremove(read_attrs,required_attrs,1)
    listremove(read_attrs,allowed_attrs,1)
    if read_attrs:
      PrintInputTable(outf,form,read_attrs,'Existing attributes not matching schema',entry)

  outf.write("""
<tr>
  <td align=left valign=top>
    <p><em>Raw LDIF data</EM></p>
    Notes:<br>
    <ul>
      <li>Lines containing "dn:" will be ignored</LI>
      <li>Only the first entry (until first empty line) will be accepted</LI>
      <li>NON-ASCII characters are not translated</LI>
      <li>maximum length is set to %d bytes
    </ul>
  </td>
  <td>
    <textarea name="input_ldif" value="" wrap=off rows=10 cols=70></textarea>
  </td>
</tr>
</table>
</form>
</div>
""" % (web2ldapcnf.ldif_maxbytes))

  w2lgui.PrintFooter(outf)

