########################################################################
# 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, os, string, charset, utctime, httphelper, msbase, cgihelper, \
       web2ldapcnf, w2lgui, w2lcore

def DisplayBinaryAttribute(
  outf,
  command,
  form,
  ls,
  attr,
  entry,
  index=None,
  mimetype=None
):
  """Display a binary attribute."""
  browsertype,browserversion = cgihelper.BrowserType(
    form.env.get('HTTP_USER_AGENT','')
  )
  ldap_browsermimetypes = msbase.CaseinsensitiveStringKeyDict(web2ldapcnf.ldap_browsermimetypes)
  if mimetype==None:
    if ldap_browsermimetypes.has_key(attr) and \
       ldap_browsermimetypes[attr].has_key(browsertype):
      mimetype = ldap_browsermimetypes[attr][browsertype]
    else:
      mimetype = w2lcore.ldap_binaryattr.get(
          attr,
          ('Octet stream','application/octet-stream')
      )[1]
  httphelper.SendHeader(
    outf,
    mimetype,
    charset='US-ASCII',
    contentlength=str(len(entry[attr][index]))
  )
  outf.write(entry[attr][index])

viewer_func = msbase.CaseinsensitiveStringKeyDict(default=DisplayBinaryAttribute)

try:
  # Modules from standard lib
  import base64,md5,sha
  # ASN.1 parser from Pisces
  from pisces import asn1
  # my own mspki modules
  from mspki import x509v3,asn1helper,util

except ImportError:
  pass

else:

  class CRLDisplayer(x509v3.CRL):

    def htmlShortView(self,form,ls,ldap_attrtype,ldap_attrindex):
      """Display a CRL in HTML only with subject/issuer info"""

    def htmlDetailView(self,outf,form,ls,ldap_attrtype,ldap_attrindex):
      """Display a CRL in HTML with all details"""
      outf.write("""<hr width="100%%">
      <table><tr><td>%s</td><td>%s</td></tr></table>
      """ % (
	  w2lgui.CommandButton(
	    form,'read','Install',
	    ls.who,ls.cred,ls.host,ls.dn,
	    extrastr="""
	    <input type=hidden name="read_attr" value="%s">
	    <input type=hidden name="read_attrmode" value="load">
	    <input type=hidden name="read_attrindex" value="%d">""" % (
              ldap_attrtype.encode(),ldap_attrindex
            )
	  ),
	  w2lgui.CommandButton(
	    form,'read','Save to disk',
	    ls.who,ls.cred,ls.host,ls.dn,
	    extrastr="""
	    <input type=hidden name="read_attr" value="%s">
	    <input type=hidden name="read_attrmode" value="load">
	    <input type=hidden name="read_attrmimetype" value="application/octet-stream">
	    <input type=hidden name="read_attrindex" value="%d">""" % (
              ldap_attrtype.encode(),ldap_attrindex
            )
	  ),
        )
      )
      # Get OID dictionary
      try:
        oids = asn1helper.ParseCfg(web2ldapcnf.misc.dumpasn1cfg)
      except IOError:
        oids = None

      revokedCertificates = self.revokedCertificates()
      if revokedCertificates:
        revokedCertificates_str = """<table>
        <tr><th>Serial Number</th><th>Revocation date</th>
        %s
        </table>
        """ % string.join(
          map(
            lambda x:
              '<tr><td>%d</td><td>%s</td></tr>\n' % (
                x[0],x[1]
              ),
            revokedCertificates
          ),
          '\n'
        )
      else:
        revokedCertificates_str = '<p>No revoked certificates.</p>'

      # Get the extensions as string-keyed dict but with
      # numeric string representation of OIDs
      extensions = self.crlExtensions()
      if extensions:
        extensions_html_list = []
        for e in extensions:
          extensions_html_list.append(
            '<dt>%s (%s)</dt><dd>%s</dd>' % (
                e.extnValue.__class__.__name__,
                str(e.extnId),
                x509v3.htmlize(e.extnValue)
            )
          )
      else:
        extensions_html_list = ['No extensions.']
      outf.write("""
    <table>
    <tr>
      <td>
	<dl>
          <dt><strong>This CRL was issued by:</strong></dt>
          <dd>%s</dd>
	</dl>
      </td>
    </tr>
    <tr>
      <td colspan="2">
	<dl>
          <dt><strong>CRL Version:</strong><dt>
          <dd>%d</dd>
          <dt><strong>This CRL is valid from %s until %s.</strong></dt>
          <dt><strong>Signature Algorithm:</strong></dt>
          <dd>%s</dd>
	</dl>
	<strong>Revoked certificates:</strong><br />
        %s
	<strong>CRL extensions:</strong><br />
	<dl>
          %s
	</dl>
      </td>
    </tr>
  </table>
  """ % (
	self.issuer().__html__(oids,form.accept_charset),
	self.version(),
	self.thisUpdate(),
	self.nextUpdate(),
	asn1helper.GetOIDDescription(self.signatureAlgorithm(),oids),
        revokedCertificates_str,
	string.join(extensions_html_list,'\n'),
      ))


  class X509CertificateDisplayer(x509v3.Certificate):

    def htmlShortView(self,form,ls,ldap_attrtype,ldap_attrindex):
      """Display a X.509 certificate in HTML only with subject/issuer info"""

    def htmlDetailView(self,outf,form,ls,ldap_attrtype,ldap_attrindex):
      """Display a X.509 certificate in HTML with all details"""
      outf.write("""<hr width="100%%">
      <table><tr><td>%s</td><td>%s</td></tr></table>
      """ % (
	  w2lgui.CommandButton(
	    form,'read','Install',
	    ls.who,ls.cred,ls.host,ls.dn,
	    extrastr="""
	    <input type=hidden name="read_attr" value="%s">
	    <input type=hidden name="read_attrmode" value="load">
	    <input type=hidden name="read_attrindex" value="%d">""" % (
              ldap_attrtype.encode(),ldap_attrindex
            )
	  ),
	  w2lgui.CommandButton(
	    form,'read','Save to disk',
	    ls.who,ls.cred,ls.host,ls.dn,
	    extrastr="""
	    <input type=hidden name="read_attr" value="%s">
	    <input type=hidden name="read_attrmode" value="load">
	    <input type=hidden name="read_attrmimetype" value="application/octet-stream">
	    <input type=hidden name="read_attrindex" value="%d">""" % (
              ldap_attrtype.encode(),ldap_attrindex
            )
	  ),
        )
      )
      # Get OID dictionary
      try:
        oids = asn1helper.ParseCfg(web2ldapcnf.misc.dumpasn1cfg)
      except IOError:
        oids = None

      # strings containing UTCTime of begin and end of validity period
      notBefore,notAfter=self.validity()

      # Get the extensions as string-keyed dict but with
      # numeric string representation of OIDs
      extensions = self.extensions()
      nsBaseUrl=''
      if extensions:
        extensions_html_list = []
        for e in extensions:
          if e.extnValue.__class__.__name__ == 'nsBaseUrl':
            nsBaseUrl = str(e.extnValue)
          if e.extnValue.__class__.__name__ in ['nsCaRevocationUrl','nsRevocationUrl','nsRenewalUrl','nsCaPolicyUrl']:
            extensions_html_list.append(
              '<dt>%s (%s)</dt><dd>%s</dd>' % (
                  e.extnValue.__class__.__name__,
                  str(e.extnId),
                  e.extnValue.__html__(nsBaseUrl,hex(self.serialNumber())[2:])
              )
            )
          else:
            extensions_html_list.append(
              '<dt>%s (%s)</dt><dd>%s</dd>' % (
                  e.extnValue.__class__.__name__,
                  str(e.extnId),
                  x509v3.htmlize(e.extnValue)
              )
            )
      else:
        extensions_html_list = ['No extensions.']

      outf.write("""
    <table>
    <tr>
      <td width="50%%">
	<dl>
          <dt><strong>This certificate belongs to:</strong></dt>
          <dd>%s</dd>
	</dl>
      </td>
      <td>
	<dl>
          <dt><strong>This certificate was issued by:</strong></dt>
          <dd>%s</dd>
	</dl>
      </td>
    </tr>
    <tr>
      <td colspan="2">
	<dl>
          <dt><strong>Certificate Version:</strong><dt>
          <dd>%d</dd>
          <dt><strong>Serial Number:</strong><dt>
          <dd>%s</dd>
          <dt><strong>This certificate is valid from %s until %s.</strong></dt>
          <dt><strong>MD5 Fingerprint:</strong></dt>
          <dd>%s</dd>
          <dt><strong>SHA-1 Fingerprint:</strong></dt>
          <dd>%s</dd>
          <dt><strong>Signature Algorithm:</strong></dt>
          <dd>%s</dd>
	</dl>
	<strong>X.509v3 certificate extensions:</strong>
	<dl>
          %s
	</dl>
      </td>
    </tr>
  </table>
  """ % (
	self.subject().__html__(oids,form.accept_charset),
	self.issuer().__html__(oids,form.accept_charset),
	self.version(),
	self.serialNumber(),
	notBefore,
	notAfter,
	self.MD5Fingerprint(),
	self.SHA1Fingerprint(),
	asn1helper.GetOIDDescription(self.signatureAlgorithm(),oids),
	string.join(extensions_html_list,'\n'),
      ))



  def DisplayX509Certificate_der(outf,command,form,ls,attr,entry,index=None):
    """Display a DER-encoded X.509 certificate attribute"""
    outf.write('<h1>%s</h1>' % (w2lgui.AttrNameStr(attr)))
    for index in range(len(entry[attr])):
      X509CertificateDisplayer(
        entry[attr][index],'der').htmlDetailView(
          outf,
	  form,
	  ls,
	  attr,
	  index,
      )
    return None
  

  def DisplayX509Certificate_base64(outf,command,form,ls,attr,entry,index=None):
    """Display a base64-encoded X.509 certificate attribute"""
    outf.write('<h1>%s</h1>' % (w2lgui.AttrNameStr(attr)))
    for index in range(len(entry[attr])):
      if util.is_base64(entry[attr][index]):
	inform='base64'
      else:
	inform='der'
      X509CertificateDisplayer(
        entry[attr][index],inform).htmlDetailView(
          outf,
	  form,
	  ls,
	  attr,
	  index,
      )
    return None


  def DisplayCRL_der(outf,command,form,ls,attr,entry,index=None):
    """Display a DER-encoded CRL attribute"""
    outf.write('<h1>%s</h1>' % (w2lgui.AttrNameStr(attr)))
    for index in range(len(entry[attr])):
      CRLDisplayer(
        entry[attr][index],'der').htmlDetailView(
          outf,
	  form,
	  ls,
	  attr,
	  index,
      )
    return None
  

  def DisplayCRL_base64(outf,command,form,ls,attr,entry,index=None):
    """Display a base64-encoded CRL attribute"""
    outf.write('<h1>%s</h1>' % (w2lgui.AttrNameStr(attr)))
    for index in range(len(entry[attr])):
      if util.is_base64(entry[attr][index]):
	inform='base64'
      else:
	inform='der'
      CRLDisplayer(
        entry[attr][index],inform).htmlDetailView(
          outf,
	  form,
	  ls,
	  attr,
	  index,
      )
    return None

  # register viewer functions

  viewer_func['userCertificate;binary'] = \
  viewer_func['cACertificate;binary'] = DisplayX509Certificate_der
  viewer_func['entrustPolicyCertificate'] = DisplayX509Certificate_der
  viewer_func['attributeCertificate'] = DisplayX509Certificate_der

  viewer_func['cACertificate'] = \
  viewer_func['userCertificate'] = \
  viewer_func['pem_x509'] = DisplayX509Certificate_base64

  viewer_func['certificateRevocationList;binary'] = DisplayCRL_der
  viewer_func['certificateRevocationList'] = DisplayCRL_base64
  viewer_func['authorityRevocationList;binary'] = DisplayCRL_der
  viewer_func['authorityRevocationList'] = DisplayCRL_base64
