########################################################################
# 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,\
       msbase, cgiforms, ldap, httphelper, w2lcore, w2lcnf, web2ldapcnf, w2lgui, ldapbase
from msbase import listremove,intersection,union

##############################################################################
# Read existing entry and output as HTML od vCard
##############################################################################

def w2l_Read(
  outf,
  command,
  form,
  ls,
  wanted_attrs=None,
  read_attrmode = 'view',
  read_attrmimetype = None,
):


  # Retrieve all attribute types
  try:
    result_dnlist = ls.l.search_s(
      ls.dn.encode('utf-8'),
      ldap.SCOPE_BASE,
      "(objectclass=*)",
      None,
      w2lcnf.GetParam(ls,'search_attrsonly',1)
    )
  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)))
    all_attrs = result_dnlist[0][1].keys()

  ldap_hiddenattrs = w2lcnf.GetParam(ls,'hiddenattrs',[])
  # Attribute herausloeschen, welche unsichtbar bleiben sollen
  listremove(all_attrs,ldap_hiddenattrs,ignorecase=1)

  # Wurde eine Attributwunschliste angegeben?
  if not wanted_attrs:
    if 'read_attr' in form.inputkeys:
      wanted_attrs = [form.field['read_attr'][0].content]
    else:
      wanted_attrs = all_attrs

  # Falls nicht nur ein Attributinhalt gelesen werden soll,
  # so werden alle Binaerattribute, fuer die ein
  # MIME-Typ-Mapping angegeben wurde, rausgestrichen
  read_attrs = wanted_attrs[:]
  if len(read_attrs)>1:
    listremove(read_attrs,w2lcore.ldap_binaryattrkeys,ignorecase=1)

  # Lesen ausführen
  try:
    result_dnlist = ls.l.search_s(
      ls.dn.encode('utf-8'),
      ldap.SCOPE_BASE,
      "(objectclass=*)",
      ldapbase.encode_unicode_list(read_attrs,'utf-8'),
      0
    )
  except ldap.NO_SUCH_OBJECT:
    raise w2lcore.ErrorExitClass(ls,'Noch such object.')

  entry = msbase.CaseinsensitiveStringKeyDict(result_dnlist[0][1],'&nbsp;')

  # Ausgabe der gelesenen Eintragsdaten

  accept_languages = string.split(form.env.get('HTTP_ACCEPT_LANGUAGE',''),',')

  if command=='read':

    if (len(read_attrs)==1) and \
       (string.lower(read_attrs[0]) in w2lcore.ldap_binaryattrkeys):

      # Send a single binary attribute with appropriate MIME-type

      import w2lviewer

      if 'read_attrindex' in form.inputkeys:
        read_attrindex = string.atoi(form.field['read_attrindex'][0].content)
	if read_attrindex >= len(entry[read_attrs[0]]):
          raise w2lcore.ErrorExitClass(ls,'Illegal index for multi-valued binary attribute.')
      else:
        read_attrindex = None

      if 'read_attrmode' in form.inputkeys:
        read_attrmode = form.field['read_attrmode'][0].content

      if (read_attrmode=='view') and \
         (w2lviewer.viewer_func.has_key(read_attrs[0])):
        # Nice displaying of binary attribute
	w2lgui.PrintHeader(outf,w2lcore.utf2display(form.accept_charset,ls.dn),form.accept_charset)
	w2lgui.MainMenuTable(outf,form,ls)
        outf.write('<div id=MessageDiv>\n')
        w2lviewer.viewer_func[read_attrs[0]](
	  outf,
	  command,
	  form,
	  ls,
	  read_attrs[0],
	  entry,
	  read_attrindex
	)
	outf.write('</div>\n')
	w2lgui.PrintFooter(outf)

      else:
        # Raw download with (hopefully) appropriate MIME-type
        if 'read_attrmimetype' in form.inputkeys:
          read_attrmimetype = form.field['read_attrmimetype'][0].content
        w2lviewer.DisplayBinaryAttribute(
	  outf,
	  command,
	  form,
	  ls,
	  read_attrs[0],
	  entry,
	  read_attrindex,
          read_attrmimetype
	)

    else:

      # Ausgabe des ganzen Eintrags mit mehreren Attributen
      w2lgui.PrintHeader(outf,w2lcore.utf2display(form.accept_charset,ls.dn),form.accept_charset)
      w2lgui.MainMenuTable(outf,form,ls)
      w2lgui.ContextMenuSingleEntry(outf,form,ls)

      objectclasses = entry.get('objectclass',entry.get('objectClass',[]))

      read_template_dict = msbase.CaseinsensitiveStringKeyDict(w2lcnf.GetParam(ls,'read_template',{}))

      template_oc = intersection(map(string.lower,objectclasses),map(string.lower,read_template_dict.keys()),ignorecase=1)

      outf.write('<div id=MessageDiv>\n')

      displayed_attrs=[]

      if template_oc:

	# Templates defined => display the entry with the help of the template
	displayentry = msbase.DefaultDict('&nbsp;')
	attr_list=entry.keys()
	for attr in attr_list:
          itemlist = []
          for item in entry[attr]:
            itemlist.append(w2lgui.DataStr(form,ls,item))
	  displayentry[attr] = string.join(itemlist,', ')

        accept_languages = map(string.strip,string.split(form.env.get('HTTP_ACCEPT_LANGUAGE',''),','))

	for oc in template_oc:

	  read_template_filename = w2lgui.GetVariantFilename(read_template_dict[oc],accept_languages)
          if read_template_filename:
	    try:
  	      template_str = open(read_template_filename,'r').read()
	    except IOError:
	      outf.write('<p>I/O error during reading template file for object class <strong>%s</strong>.</p>' % oc)
            else:
  	      outf.write(template_str % displayentry)
              displayed_attrs = msbase.union(displayed_attrs,msbase.GrabKeys(template_str)())
	  else:
            outf.write('<p>Template file for object class <strong>%s</strong> not found.</p>' % oc)

      # Remove attributes already displayed with HTML templates
      wanted_attrs = msbase.listremove(wanted_attrs,displayed_attrs,ignorecase=1)

      if wanted_attrs:

	# No templates defined => display the entry in a raw way
	from ldapoc import ldap_oc
	required_attrs = []
	allowed_attrs = []
	for oc in objectclasses:
	  oc_def = ldap_oc.get(oc,{})
	  required_attrs = union(required_attrs,oc_def.get('requires',[]))
	  allowed_attrs = union(allowed_attrs,oc_def.get('allows',[]))
	required_attrs = intersection(map(string.lower,required_attrs),wanted_attrs,ignorecase=1)
	allowed_attrs = intersection(map(string.lower,allowed_attrs),wanted_attrs,ignorecase=1)
	nomatching_attrs = wanted_attrs[:]
	listremove(nomatching_attrs,required_attrs,ignorecase=1)
	listremove(nomatching_attrs,allowed_attrs,ignorecase=1)
	listremove(required_attrs,['objectclass'],ignorecase=1)
	listremove(allowed_attrs,['objectclass'],ignorecase=1)
	listremove(nomatching_attrs,['objectclass'],ignorecase=1)

	def PrintAttrRows(outf,form,ls,entry,attrs,read_attrs,Comment=''):
          if attrs:
            attrs.sort()
            if Comment:
              outf.write('<tr><th colspan=2 align=left><strong>%s</strong></th></tr>\n' % (Comment))
	    for attr in attrs:
	      if not (string.lower(attr) in w2lcore.ldap_binaryattrkeys):
        	datalist = entry.get(attr,entry.get(string.lower(attr),[]))
        	if datalist:
		  outf.write('<tr><td width="30%%" rowspan=%d>%s</td>' % (len(datalist),w2lgui.AttrNameStr(attr)))
        	  # Ausgabe des Attribut-Inhalts
		  outf.write('<td align=left>%s</td></tr>\n' % w2lgui.DataStr(form,ls,datalist[0],commandbutton=1))
		  for data in datalist[1:]:
		    outf.write('<tr><td align=left>%s</td></tr>\n' % w2lgui.DataStr(form,ls,data,commandbutton=1))
        	else:
		  outf.write('<tr><td width="30%%">%s</td><td align=left>&nbsp;</td></tr>\n' % (w2lgui.AttrNameStr(attr)))
	      else:
		outf.write('<tr><td width="30%%">%s</td><td align=left>%s</td></tr>\n' % (
		  w2lgui.AttrNameStr(attr),
		  w2lgui.CommandButton(
		    form,'read','View/Load',
		    ls.who,
		    ls.cred,
		    ls.host,
		    ls.dn,
		    extrastr='<input type=hidden name="read_attr" value="%s"><input type=hidden name="read_attrindex" value="0">' % attr
		  )
		))

	# Eigentliche Ausgabe des gelesenen Eintrags
	outf.write('<table id=readtable summary="Content of LDAP entry" width="100%%" border rules=rows noshade cellpadding="2%%" cellspacing=0>')

	PrintAttrRows(outf,form,ls,entry,['objectclass'],read_attrs,'Object classes')
	PrintAttrRows(outf,form,ls,entry,required_attrs,all_attrs,'Required Attributes')
	PrintAttrRows(outf,form,ls,entry,allowed_attrs,all_attrs,'Allowed Attributes')
	PrintAttrRows(outf,form,ls,entry,nomatching_attrs,all_attrs,'Attributes not matching schema')

	outf.write('</table>')

      outf.write('</div>\n')

      w2lgui.PrintFooter(outf)


  elif command=='vcard':

    objectclasses = entry.get('objectclass',entry.get('objectClass',[]))

    vcard_template_dict = msbase.CaseinsensitiveStringKeyDict(
      w2lcnf.GetParam(ls,'vcard_template',{})
    )
    template_oc = intersection(map(string.lower,objectclasses),map(string.lower,vcard_template_dict.keys()),ignorecase=1)

    if template_oc:

      from charset import utf2iso

      # Templates defined => display the entry with the help of a template
      displayentry = msbase.CaseinsensitiveStringKeyDict(default='')
      displayentry['dn'] = utf2iso(ls.dn)
      attr_list=entry.keys()
      for attr in attr_list:
        itemlist = []
        for item in entry[attr]:
          itemlist.append(string.join(string.split(utf2iso(item),'$'),'\, '))
	displayentry[attr] = string.join(itemlist,';')

      accept_languages = map(string.strip,string.split(form.env.get('HTTP_ACCEPT_LANGUAGE',''),','))

      oc = template_oc[0]
      vcard_template_filename = w2lgui.GetVariantFilename(vcard_template_dict[oc],accept_languages)
      if vcard_template_filename:
	try:
  	  template_str = open(vcard_template_filename,'r').read()
	except IOError:
	  raise w2lcore.ErrorExitClass(ls,'I/O error during reading template file for object class <strong>%s</strong>.' % oc)
        else:
          httphelper.SendHeader(outf,'text/x-vcard',charset='ISO-8559-1')
  	  outf.write(template_str % displayentry)
      else:
        raise w2lcore.ErrorExitClass(ls,'vCard template file for object class <strong>%s</strong> not found.' % (oc))

    else:
      raise w2lcore.ErrorExitClass(ls,'No template file defined for object class(es) <strong>%s</strong>.' % (string.join(objectclasses,', ')))

