########################################################################
# 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, ldapbase, msbase, w2lcore, web2ldapcnf, w2lcnf, w2lgui

##############################################################################
# Modify existing entry
##############################################################################

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

  def ModifyListTable(form,modlist):
    stringlist = []
    stringlist.append('<TABLE BORDER RULES=rows CELLSPACING=0 CELLPADDING=2%%>')
    for i in modlist:
      if i[0] in w2lcore.ldap_binaryattrkeys:
        tablestr = '%s Bytes of binary data' % string.join(map(str,map(len,i[1])),'Bytes, ')
      else:
        tablestr = string.join(w2lcore.utf2display(form.accept_charset,i[1]),'<BR>')
      if i[0]==ldap.MOD_ADD:
        if i[1] in w2lcore.ldap_binaryattrkeys:
	  tablestr = '%d Bytes of binary data' % (len(i[2]))
	else:
	  tablestr = w2lcore.utf2display(form.accept_charset,i[2])
      else:
        if i[1] in w2lcore.ldap_binaryattrkeys:
          tablestr = '%s Bytes of binary data' % string.join(map(str,map(len,i[2])),'Bytes, ')
	else:
  	  tablestr = string.join(w2lcore.utf2display(form.accept_charset,i[2]),'<BR>')
      stringlist.append('<TR><TD>%s</TD><TD>%s</TD><TD>%s</TD></TR>' % (ldapbase.ldap_mod_str[i[0]],w2lgui.AttrNameStr(i[1]),tablestr))
    stringlist.append('</TABLE>')
    return string.join(stringlist,'\n')


  try:
    # Get all existing attribute names of entry
    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()
    msbase.listremove(existing_attrs,w2lcore.ldap_binaryattrkeys+w2lcnf.GetParam(ls,'hiddenattrs',[]),ignorecase=1)
    # Read all non-binary attribute data of entry
    result_dnlist = ls.l.search_s(
      ls.dn.encode('utf-8'),
      ldap.SCOPE_BASE,
      "(objectclass=*)",
      existing_attrs,
      0
    )
  except ldap.NO_SUCH_OBJECT:
    raise w2lcore.ErrorExitClass(ls,'No such object <strong>%s</strong>.' % (w2lcore.utf2display(form.accept_charset,ls.dn)))
  else:
    if not result_dnlist:
      raise w2lcore.ErrorExitClass(ls,'No search results when reading entry <strong>%s</strong>.' % (w2lcore.utf2display(form.accept_charset,ls.dn)))
    old_entry = result_dnlist[0][1]

  new_entry = msbase.CaseinsensitiveStringKeyDict()

  # Input from LDIF text area?
  if 'input_ldif' in form.inputkeys:
    from StringIO import StringIO
    import ldif
    f = StringIO(form.field['input_ldif'][0].content)
    try:
      ldif_data = ldif.ParseLDIF(f,maxentries=1)
    except ValueError:
      raise w2lcore.ErrorExitClass(ls,'Error parsing LDIF data.')
    else:
      if ldif_data:
        new_entry.update(ldif_data[0][1])

  # Input from normal input fields
  if 'input_attr' in form.inputkeys:
    for i in xrange(len(form.field['input_attr'][0].content)):
      attr = form.field['input_attr'][0].content[i]
      if attr:
	data = form.field['input_data'][0].content[i]
	if new_entry.has_key(attr):
	  if data:
	    if new_entry[attr]==['']:
  	      new_entry[attr] = [data]
	    else:
	      new_entry[attr].append(data)
	else:
	  new_entry[attr] = [data]

  old_objectclasses = old_entry.get('objectclass',old_entry.get('objectClass',[]))
  old_objectclasses.sort()

  modlist = []
  if 'ldap_oc' in form.inputkeys:
   # Object class(es) were defined in users input
    new_objectclasses = form.field['ldap_oc'][0].content
    new_objectclasses.sort()
    if map(string.lower,old_objectclasses)!=map(string.lower,new_objectclasses):
      modlist.append(
        (
          ldap.MOD_REPLACE,
          'objectClass',
          ldapbase.encode_unicode_list(new_objectclasses)
        )
      )

  modlist.extend(ldapbase.modify_modifylist(old_entry,new_entry))

  if modlist:
    # Send modify-list to host
    try:
      ls.l.modify_s(
        ls.dn.encode('utf-8'),
        modlist
      )
    except ldap.NO_SUCH_OBJECT:
      raise w2lcore.ErrorExitClass(ls,'No such object %s: The object to be modified does not exist. Wrong DN?' % (w2lgui.DisplayDN(form.accept_charset,ls.dn)))
    except ldap.OBJECT_CLASS_VIOLATION:
      raise w2lcore.ErrorExitClass(ls,'Object class violation: Maybe a required attribute is missing or some of the given attributes are not allowed.')
    else:
      # delete all cache entries referencing to old DN
      ls.l.uncache_entry(ls.dn.encode('utf-8'))

  w2lgui.PrintHeader(outf,'',form.accept_charset)
  w2lgui.MainMenuTable(outf,form,ls)
  w2lgui.ContextMenuSingleEntry(outf,form,ls)
  outf.write('<div id=MessageDiv>')
  if modlist:
    outf.write('<p>Modified entry %s with your input data:</p>\n%s' % (w2lgui.DisplayDN(form.accept_charset,ls.dn),ModifyListTable(form,modlist)))
  else:
    outf.write('No attributes modified of entry %s.' % (w2lgui.DisplayDN(form.accept_charset,ls.dn)))
  outf.write('</div>')
  w2lgui.PrintFooter(outf)

