# $Id$
# 
# Copyright (C) 2014  Dragon Research Labs ("DRL")
# Portions copyright (C) 2010--2012  Internet Systems Consortium ("ISC")
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notices and this permission notice appear in all copies.
# 
# THE SOFTWARE IS PROVIDED "AS IS" AND DRL AND ISC DISCLAIM ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL DRL OR
# ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

"""
Take the basic application/x-rpki messages that rpkid and friends
log and translate them into a text version that's easier to search,
without losing any of the original data.  We use MH for the output
format because nmh makes a handy viewer.
"""

import email.mime
import email.mime.application
import email.mime.text
import email.mime.multipart
import email.utils
import email.encoders
import mailbox
import rpki.POW
import lxml.etree
import argparse
import sys
import base64

parser = argparse.ArgumentParser(description = __doc__)
parser.add_argument("-i", "--input", required = True,
                    help = "input Maildir")
parser.add_argument("-m", "--mark", action = "store_true",
                    help = "mark seen messages")
parser.add_argument("-k", "--kill", action = "store_true",
                    help = "kill seen messages")
parser.add_argument("-o", "--output", required = True,
                    help = "output MH folder")
parser.add_argument("-t", "--tag",
                    default = "{http://www.apnic.net/specs/rescerts/up-down/}message",
                    help = "XML namespace tag for an input message")
parser.add_argument("-u", "--unseen", action = "store_true",
                    help = "only process unseen messages")
args = parser.parse_args()

def pprint_cert(b64):
  return rpki.POW.X509.derRead(base64.b64decode(b64)).pprint()
  
def up_down():
  msg["X-RPKI-Up-Down-Type"] = xml.get("type")
  msg["X-RPKI-Up-Down-Sender"] = xml.get("sender")
  msg["X-RPKI-Up-Down-Recipient"] = xml.get("recipient")
  msg["Subject"] = "Up-down %s %s => %s" % (xml.get("type"), xml.get("sender"), xml.get("recipient"))
  for x in xml:
    if x.tag.endswith("class"):
      for y in x:
        if y.tag.endswith("certificate") or y.tag.endswith("issuer"):
          msg.attach(email.mime.text.MIMEText(pprint_cert(y.text)))

def left_right():
  msg["X-RPKI-Left-Right-Type"] = xml.get("type")
  msg["Subject"] = "Left-right %s" % xml.get("type")

def publication():
  msg["X-RPKI-Left-Right-Type"] = xml.get("type")
  msg["Subject"] = "Publication %s" % xml.get("type")

dispatch = { "{http://www.apnic.net/specs/rescerts/up-down/}message" : up_down,
             "{http://www.hactrn.net/uris/rpki/left-right-spec/}msg" : left_right,
             "{http://www.hactrn.net/uris/rpki/publication-spec/}msg" : publication }

def fix_headers():
  if "X-RPKI-PID" in srcmsg or "X-RPKI-Object" in srcmsg:
    msg["X-RPKI-PID"] = srcmsg["X-RPKI-PID"]
    msg["X-RPKI-Object"] = srcmsg["X-RPKI-Object"]
  else:
    words = srcmsg["Subject"].split()
    msg["X-RPKI-PID"] = words[1]
    msg["X-RPKI-Object"] = " ".join(words[4:])
  
destination = None
source = None
try:
  destination = mailbox.MH(args.output, factory = None, create = True)
  source = mailbox.Maildir(args.input, factory = None)

  for srckey, srcmsg in source.iteritems():
    if args.unseen and "S" in srcmsg.get_flags():
      continue
    assert not srcmsg.is_multipart() and srcmsg.get_content_type() == "application/x-rpki"
    payload = srcmsg.get_payload(decode = True)
    cms = rpki.POW.CMS.derRead(payload)
    txt = cms.verify(rpki.POW.X509Store(), None, rpki.POW.CMS_NOCRL | rpki.POW.CMS_NO_SIGNER_CERT_VERIFY | rpki.POW.CMS_NO_ATTR_VERIFY | rpki.POW.CMS_NO_CONTENT_VERIFY)
    xml = lxml.etree.fromstring(txt)
    tag = xml.tag
    if args.tag and tag != args.tag:
      continue
    msg = email.mime.multipart.MIMEMultipart("related")
    msg["X-RPKI-Tag"] = tag
    for i in ("Date", "Message-ID", "X-RPKI-Timestamp"):
      msg[i] = srcmsg[i]
    fix_headers()
    if tag in dispatch:
      dispatch[tag]()
    if "Subject" not in msg:
      msg["Subject"] = srcmsg["Subject"]
    msg.attach(email.mime.text.MIMEText(txt))
    msg.attach(email.mime.application.MIMEApplication(payload, "x-rpki"))
    msg.epilogue = "\n"                 # Force trailing newline
    key = destination.add(msg)
    print "Added", key
    if args.kill:
      del source[srckey]
    elif args.mark:
      srcmsg.set_subdir("cur")
      srcmsg.add_flag("S")
      source[srckey] = srcmsg

finally:
  if destination:
    destination.close()
  if source:
    source.close()