#!/usr/bin/env python
# -*- mode: python -*-

from __future__ import print_function

import getopt
import email
import email.parser
import os
import pwd
import sys
import time
import traceback

import ldap

from userdir_gpg import gpg_check_email_sig
from userdir_ldap import (
    BaseDn, connectLDAP, PassDir, Ech_MainLog, Ech_ErrorLog, GetUID,
    FormatPGPKey, SplitEmail)
from userdir_exceptions import Error


EX_TEMPFAIL = 75
EX_PERMFAIL = 65       # EX_DATAERR
Debug = None


# Try to extract a key fingerprint from a PGP siged message
def TryGPG(mail):
   try:
       pgp, _ = gpg_check_email_sig(mail, lax_multipart=True)
   except Exception:
       return None

   # Failed to find a matching sig
   if not pgp.ok:
      S = "%s: %s -> PGP Checking failed '%s': %s %s\n" % (Now, MsgID, mail["From"], str(pgp.why), str(pgp.key_info))
      ErrLog.write(S)
      return None

   # Search for the matching key fingerprint
   Attrs = lc.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "keyFingerPrint=" + pgp.key_fpr)
   if len(Attrs) == 0:
      return None
   if len(Attrs) != 1:
      raise Error("Oddly your key fingerprint is assigned to more than one account..")

   return (Attrs[0][1]["uid"][0].decode('ascii'), "PGP", FormatPGPKey(pgp.key_fpr))


# Try to guess the name from the email address
def TryMatcher(mail):
   Sender = mail["From"]
   if Sender is None:
      return None

   # Split up the address and invoke the matcher routine
   UID = GetUID(lc, SplitEmail(Sender))

   if UID[0] is None:
      if UID[1] is None or len(UID[1]) == 0:
         return None

      # Print out an error message
      S = "%s: %s -> Address matching failed '%s'\n" % (Now, MsgID, Sender)
      for x in UID[1]:
         S = S + " " + x + "\n"
      ErrLog.write(S)
      return None

   return (UID[0], "FROM", Sender)


# Process options
(options, arguments) = getopt.getopt(sys.argv[1:], "dr")
for (switch, val) in options:
   if (switch == '-d'):
      Debug = ""

# Open the log files
if Debug is None:
   MainLog = open(Ech_MainLog, "a+", 0)
   ErrLog = open(Ech_ErrorLog, "a+", 0)
else:
   MainLog = open("/dev/stdout", "a")
   ErrLog = open("/dev/stdout", "a")

if sys.version_info[0] < 3:
    stdin = sys.stdin
else:
    stdin = sys.stdin.buffer

# Start of main program
ErrMsg = "Indeterminate Error"
ErrType = EX_TEMPFAIL
Now = time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(time.time()))
MsgID = None
try:
   # Get the email
   ErrType = EX_PERMFAIL
   ErrMsg = "Failed to understand the email or find a signature:"
   mail = email.parser.BytesParser().parse(stdin)
   MsgID = mail["Message-ID"]

   # Connect to the ldap server
   ErrType = EX_TEMPFAIL
   ErrMsg = "An error occurred while performing the LDAP lookup"
   global lc
   lc = connectLDAP()
   if Debug is None:
      F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r")
      AccessPass = F.readline().strip().split(" ")
      lc.simple_bind_s("uid=" + AccessPass[0] + "," + BaseDn, AccessPass[1])
      F.close()
   else:
      lc.simple_bind_s("", "")

   # Try to decode
   ErrType = EX_TEMPFAIL
   ErrMsg = "An error occurred while trying GPG decoding"
   User = TryGPG(mail)
   if User is None:
      ErrMsg = "An error occurred while trying Matcher decoding"
      User = TryMatcher(mail)

   # Get any mailing list information
   List = mail['X-Mailing-List']
   if not List:
      List = "-"

   # Tada, write a log message
   if User is not None:
      Msg = "[%s] \"%s\" \"%s\" \"%s\"" % (Now, User[2], List, MsgID)
      MainLog.write("%s %s %s\n" % (User[0], User[1], Msg))
      Dn = "uid=" + User[0] + "," + BaseDn
      Rec = [(ldap.MOD_REPLACE, "activity-%s" % User[1], Msg)]
      if Debug is None:
         lc.modify_s(Dn, Rec)
      else:
         print(Rec)
   else:
      User = ("-", "UKN", mail["From"])
      Msg = "[%s] \"%s\" \"%s\" \"%s\"" % (Now, User[2], List, MsgID)
      MainLog.write("%s %s %s\n" % (User[0], User[1], Msg))

except Exception:
   # Log an exception..
   exc_info = sys.exc_info()
   S = "%s: %s -> %s\n" % (Now, MsgID, ErrMsg)
   S = S + "==> %s: %s\n" % (exc_info[0], exc_info[1])
   List = traceback.extract_tb(exc_info[2])
   if len(List) > 1:
      for x in List:
         S = S + "   %s %s:%u: %s\n" % (x[2], x[0], x[1], x[3])
   ErrLog.write(S)
   sys.exit(ErrType)

sys.exit(0)
