#!/usr/bin/python2 -u
# arekm@pld-linux.org, 2006-01
# glen@pld-linux.org, 2006-03-14
# glen@pld-linux.org,arekm@pld-linux.org, 2006-10-30 - added ssl support (for gmail.com)
# glen@pld-linux.org 2006-11-03 - made it work with jabber.pld-linux.org again
# glen@pld-linux.org,arekm@pld-linux.org, 2006-11-13 - added config file support
# glen@pld-linux.org, 2006-12-07 - added html messages support (-x), thx goes to to jajcus
# luzik@pld-linux.org, 2007-03 - added digest auth method(jabber.gda.pl)
# arekm@pld-linux.org, 2009-07 - added fallback accounts support
# usage:
#   jabber.alert [-x] [-c config] [-a account_id][,otheraccount_id] [-t timeout ] [-J from_jid -P password] to_jid1 to_jid2 to_jid3

import os
import hashlib
import re
import sys
import getopt
import logging
import socket
import string
import time
import libxml2

from pyxmpp.jid import JID
from pyxmpp.message import Message
from pyxmpp.jabber.client import JabberClient
from pyxmpp.streamtls import TLSSettings

try:
    opts, args = getopt.getopt(sys.argv[1:], "J:P:a:b:c:ds:t:x")
except getopt.GetoptError, e:
    print >> sys.stderr, "%s: %s " % (sys.argv[0], e)
    sys.exit(1)

jids = []
html = False
debug = False
timeout = 20
cfg = "/etc/nagios/jabber-notify.ini"
tjid = None
body = ""
subject = "Nagios alert"
for o, a in opts:
    if o == '-b':
        body += a
    if o == '-c':
        cfg = a
    if o == '-d':
        debug = True
    if o == '-t':
        timeout = float(a)
    if o == '-x':
        html = True
    if o == '-J':
        tjid = a
    if o == '-P':
        jids.append({ 'jid': tjid, 'password': a })
    if o == '-s':
        subject = a
    if o == '-a':
        import ConfigParser

        config = ConfigParser.ConfigParser()
        config.read(cfg)

        for section in a.split(','):
            jids.append({ 'jid': config.get(section, 'jid'), 'password': config.get(section, 'password')})

socket.setdefaulttimeout(timeout)

recpt = args

for section in jids:
    if not section['jid'] or not section['password']:
        print >> sys.stderr, "%s: jid (-J) and password (-P) are required for `%s'" % (sys.argv[0], section)
        sys.exit(1)

if not jids:
    print >> sys.stderr, "%s: no configured jid accounts found" % sys.argv[0]
    sys.exit(1)

if not recpt:
    print >> sys.stderr, "%s: recipient jids are required" % sys.argv[0]
    sys.exit(1)

if debug:
    logger=logging.getLogger()
    logger.addHandler(logging.StreamHandler())
    logger.setLevel(logging.DEBUG)

if not body:
    stdin_body = ""
    for line in sys.stdin.readlines():
        stdin_body += line

    body += stdin_body

if len(body.strip()) == 0:
    body = "(nagios-jabber.alert warning: missing message body)";

message_type = 'chat'

class XMPPStreamError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return self.msg

class Client(JabberClient):
    def session_started(self):
        if (html == True):
            import re
            message = re.sub('<.*?>', '', body)
            doc = libxml2.parseDoc('<body>' + body + '</body>')
            doc_element = doc.getRootElement().children
        else:
            message = body

        for r in recpt:
            jid_r = JID(r)
            msg = Message(to_jid = jid_r, body = message, subject = subject,
                            stanza_type = message_type, thread = "Nagios")

            if (html == True):
                node = msg.add_new_content('http://jabber.org/protocol/xhtml-im', 'html')
                xbody = node.newChild(None, "body", None)
                html_ns = xbody.newNs('http://www.w3.org/1999/xhtml', None)
                xbody.setNs(html_ns)
                xbody.addChildList(doc_element.docCopyNodeList(xbody.doc))

            self.stream.send(msg)
        self.disconnect()

    def stream_state_changed(self,state,arg):
        if debug:
            print "*** State changed: %s %r ***" % (state,arg)

    def stream_error(self,err):
        raise XMPPStreamError(err.get_message())


err = []
for section in jids:
    for attempt in ('first', 'second'):
        jid = JID(section['jid'])
        resource = "Nagios/" + hashlib.md5(''.join(recpt)).hexdigest()[:10]
        if attempt == 'second':
            # if something went wrong the second time around, it's
            # most likely a resource name conflict on login, so let's
            # wait a bit, randomize the resource name and try again
            resource = resource + '/' + repr(os.getpid())
            time.sleep(0.8)
        if not jid.resource:
            jid = JID(jid.node, jid.domain, resource)

        c = Client(jid, section['password'], auth_methods = ['sasl:DIGEST-MD5', 'sasl:PLAIN', 'digest'],
                        tls_settings = TLSSettings(require = False, verify_peer = False))
        try:
            c.connect()
            try:
                c.loop(1)
            except XMPPStreamError, e:
                # Most likely a duplicate stream problem
                # don't log anything, just try again
                c.disconnect()
                continue
            except Exception, e:
                err.append("ERROR1: %s: %s" % (section['jid'], e))
                c.disconnect()
                # don't try another attempt, jump straigt to
                # another section
                break
            c.disconnect()
            # stop after first successful attempt at sending the msg
            sys.exit(0)
        except Exception, e:
            err.append("ERROR2: %s: %s" % (section['jid'], e))

print >> sys.stderr, "\n".join(err)
sys.exit(1)
