#!/usr/bin/python
# Fetches mails from inbox of a pop mailbox to gather configuration
# and state information generated by the livedump command. We use
# the mail subject to identify the mails. Mails with a subject
# "state" do contain the nagios checkresults in the mail body and
# mails with subjects starting with the string "config" do contain 
# nagios configuration definitions. All chars after the config string
# in the subject are used to identfy the sending system. This is
# an ident of the system to support multiple sending systems.
#
# Configuration is written to etc/nagios/conf.d/remote-<ident>.cfg.
# The status info is sent in nagios state result file format and
# written to tmp/nagios/checkresults directory in a temporary file
# which is then processed by nagios.

import os
import sys
import poplib
import re
import quopri
import tempfile

omd_root = os.environ.get('OMD_ROOT')
pop_server = "pop.yourmailserver.com"
pop_user   = "your-username@domain.com"
pop_pass   = "secretpassword"
# By default the mail body is treated to be encoded as "quoted-printable".
# In the other case no decoding is not performed.
encoding   = "quoted-printable"
# By default the mails are not decrypted. If you configure a string here
# it is assumed to be the preshared key to decrypt the AES encrypted mails.
encrypt    = None

if encrypt:
    from base64 import b64decode
    from Crypto import Random
    from Crypto.Hash import MD5
    from Crypto.Cipher import AES

M = poplib.POP3_SSL(pop_server)
M.user(pop_user)
M.pass_(pop_pass)

config_ids = []
status_ids = []
numMessages = len(M.list()[1])

status_mails = []
config_mails = []

# This decrypts mail bodies generated with openssl. e.g. with this command:
# openssl aes-256-cbc -a -kfile /path/to/my/preshared-key-file
def decrypt(body):
    encrypted = b64decode(body)
    salt      = encrypted[8:16]
    data      = encrypted[16:]

    # Need 32 bytes for the key and 16 bytes for the IV
    def openssl_kdf(req):
        prev = ''
        while req>0:
            prev = MD5.new(prev+encrypt+salt).digest()
            req -= 16
            yield prev
    mat = ''.join([ x for x in openssl_kdf(32+16) ])
    key = mat[0:32]
    iv  = mat[32:48]

    dec = AES.new(key, AES.MODE_CBC, iv)
    return dec.decrypt(data)

pattern = re.compile(r"^Subject: (.*)")
for i in range(numMessages):
    for header_lines in M.top(i + 1, 0)[1]:
        matches = pattern.match(header_lines)
        if matches:
            subject = matches.group(1).strip()
            if subject == "status":
                status_mails.append(i + 1)
            elif subject.startswith("config"):
                ident = subject[6:].strip().replace("/", "_").replace(".", "_")
                if not ident:
                    ident = "no-ident"
                config_mails.append((i + 1, ident))

# handle config mails. Only last available is interesting.
# Extract mail body and use this as retrieved config.
if config_mails:
    mail_index, ident = config_mails[-1]
    code, mail, num = M.retr(mail_index)
    body_start_index = mail.index('')
    body = '\n'.join(mail[body_start_index:])

    if encrypt:
        body = decrypt(body)

    if encoding == 'quoted-printable':
        body = quopri.decodestring(body)

    file("%s/etc/nagios/conf.d/remote-%s.cfg" %
         (omd_root, ident), "w").write(body)

if status_mails:
    for index in status_mails:
        code, mail, code = M.retr(index)
        body_start_index = mail.index('')
        body = '\n'.join(mail[body_start_index:])

        if encrypt:
            body = decrypt(body)

        if encoding == 'quoted-printable':
            body = quopri.decodestring(body)

        fd, path = tempfile.mkstemp('', 'c', "%s/tmp/nagios/checkresults" % omd_root)
        os.write(fd, body)
        os.close(fd)
        file(path + ".ok", "w")

# alle mails loeschen
try:
    popstate, msglist, code = M.list()
    for entry in msglist:
        msgid, size = entry.split()
        response = M.dele(msgid)
        if not response.startswith('+OK'):
            raise Exception("Error from POP server: [%s]" % response)
            break

except Exception, e:
    sys.stderr.write(msg + "Error deleting message: %s" % e)
    sys.exit(1)

# Close mailbox, commit changes
M.quit()
