#!/usr/bin/python
"""
Monitoring plugin to check local unread email presence

Author : Benjamin Renard <brenard@easter-eggs.com>
Date : Mon, 20 Jan 2020 12:59:14 +0100
Source : https://gitlab.easter-eggs.com/check_local_unread_emails
"""

import logging
import os
import sys

## Configuration
desc = "Monitoring tool to check local unread email presence"

default_spool_dir_path = '/var/mail'

## Arguments
try:
    import argparse

    parser = argparse.ArgumentParser(
        description=desc
    )

    parser.add_argument(
        '-D',
        dest='spool_dir_path',
        type=str,
        help='Spool directory path (Default: %s)' % default_spool_dir_path,
        default=default_spool_dir_path
    )

    parser.add_argument(
        '-x', '--exclude',
        action='append',
        type=str,
        help='Exclude one (or more) mailboxes from monitoring'
    )

    parser.add_argument(
        '-d', '--debug',
        action='store_true',
        help='Show debug messages'
    )

    parser.add_argument(
        '-v', '--verbose',
        action='store_true',
        help='Show verbose messages'
    )

    parser.add_argument(
        '-w', '--warning',
        action='store_true',
        help='Show warning messages'
    )

    parser.add_argument(
        '-l', '--logfile',
        action='store',
        type=str,
        help='Log file path'
    )

    parser.add_argument(
        '-C', '--console',
        action='store_true',
        help='Also log on console (even if log file is provided)'
    )

    options = parser.parse_args()
except ImportError:
    import optparse

    parser = optparse.OptionParser(
        description=desc
    )

    parser.add_option(
        '-D',
        dest='spool_dir_path',
        type='string',
        help='Spool directory path (Default: %s)' % default_spool_dir_path,
        default=default_spool_dir_path
    )

    parser.add_option(
        '-x', '--exclude',
        dest='exclude',
        action='append',
        type='string',
        help='Exclude one (or more) mailboxes from monitoring'
    )

    parser.add_option(
        '-d', '--debug',
        action='store_true',
        help='Show debug messages'
    )

    parser.add_option(
        '-v', '--verbose',
        action='store_true',
        help='Show verbose messages'
    )

    parser.add_option(
        '-w', '--warning',
        action='store_true',
        help='Show warning messages'
    )

    parser.add_option(
        '-l', '--logfile',
        action='store',
        type='string',
        help='Log file path'
    )

    parser.add_option(
        '-C', '--console',
        action='store_true',
        help='Also log on console (even if log file is provided)'
    )

    (options, args) = parser.parse_args()

# Initialize log
log = logging.getLogger()
logformat = logging.Formatter("%(asctime)s - " + os.path.basename(sys.argv[0]) + " - %(levelname)s : %(message)s")

if options.debug:
    log.setLevel(logging.DEBUG)
elif options.verbose:
    log.setLevel(logging.INFO)
elif options.warning:
    log.setLevel(logging.WARNING)
else:
    log.setLevel(logging.FATAL)

if options.logfile:
    logfile = logging.FileHandler(options.logfile)
    logfile.setFormatter(logformat)
    log.addHandler(logfile)

if not options.logfile or options.console:
    logconsole = logging.StreamHandler()
    logconsole.setFormatter(logformat)
    log.addHandler(logconsole)

if not os.path.isdir(options.spool_dir_path):
    print('UNKNOWN - Spool directory %s not found (or not a directory)' % options.spool_dir_path)
    sys.exit(3)

unread_msg_users = dict()

def handle_msg_info():
    """ Handle message info after detecting a new one (or at the end) """
    global total_msg, read_msg, unread_msg, unread_messages, current_message, status
    if not current_message:
        return
    log.debug('Handle msg "%s" info (Status = %s)', current_message, status)
    if status is None:
        # Status not defined : it's a new message
        unread_msg += 1
        unread_messages.append(current_message)
    elif 'D' in status:
        # Message is deleted : do not count it
        pass
    elif 'R' in  status:
        read_msg += 1
    else:
        unread_msg += 1
        unread_messages.append(current_message)
    total_msg += 1

errors = []
log.debug('List mbox files from %s directory', options.spool_dir_path)
for mbox_file in os.listdir(options.spool_dir_path):
    mbox_path = os.path.join(options.spool_dir_path, mbox_file)
    if not os.path.isfile(mbox_path):
        continue
    if options.exclude and mbox_file in options.exclude:
        log.debug('Mailbox %s is excluded', mbox_file)
        continue
    log.debug("Handling %s mbox file", mbox_file)
    try:
        total_msg = 0
        read_msg = 0
        unread_msg = 0
        unread_messages = []

        current_message = False
        in_headers = True
        status = None
        mbox_fd = open(mbox_path, 'r')
        for line in mbox_fd.readlines():
            if line.startswith('From '):
                handle_msg_info()
                current_message = line.strip()
                in_headers = True
                status = None
            elif current_message and in_headers and line.strip() == "":
                log.debug("End of headers detected")
                in_headers = False
            elif current_message and in_headers and status is None and line.startswith('Status: '):
                status = line[8:].strip()
        mbox_fd.close()
        handle_msg_info()
        if unread_msg:
            unread_msg_users[mbox_file] = dict(
                total=total_msg,
                read=read_msg,
                unread=unread_msg,
                unread_messages=unread_messages
            )
        log.debug('Mbox file %s contains %d email(s) including %d unread and %d read messages', mbox_file, total_msg, unread_msg, read_msg)
    except IOError:
        log.warning('Fail to read MBOX file %s', mbox_file, exc_info=True)
        errors.append(mbox_file)
if errors:
    print("UNKNOWN - Error occured reading MBOX file(s) %s" % ', '.join(errors))
elif unread_msg_users:
    print("WARNING - Local unread email found for the following(s) user(s): %s\n" % ', '.join(['%s (%s unread messages on %s)' % (user, details['unread'], details['total']) for user, details in unread_msg_users.items()]))

    for user, details in unread_msg_users.items():
        msg = "Unread email of user %s " % user
        if len(details['unread_messages']) > 10:
            msg += "(last 10 messages) :\n"
            msg += "\n".join(details['unread_messages'][-10:])
        else:
            msg += ":\n" + "\n".join(details['unread_messages'])
        print(msg + "\n")
else:
    print("OK - No local unread email found")
    sys.exit(0)

if not errors:
    sys.exit(1)
sys.exit(3)
