#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-

# Ajout d'un whos et d'un tracage aux mails d'arpwatch
# Auteurs : Stéphane Glondu, Cyril Cohen, Daniel STAN, Valentin Samir, Vincent Le Gallic
# Licence : GPLv2

from __future__ import print_function

import sys, os, re
import socket

import common

# Keep away from here les modules qui font des connexions ldap sans le dire (!)
import gestion.config
from gestion.iptools import AddrInNets
from utils.sendmail import sendmail 

# On récupère les destinataires dans les arguments (très ad hoc)
recipients = [ mail for mail in sys.argv[1:] if '@' in mail ]
if not recipients:
    print("Pas de mail fourni: affichage sur stdout.",
          file=sys.stderr)

find_mac = re.compile(r'[0-9A-Fa-f]{1,2}(?::[0-9A-Fa-f]{1,2}){5}')
find_ip = re.compile(r'[0-9]{1,3}(?:\.[0-9]{1,3}){3}')
#: Regexp pour matcher l'interface.
#: Ne matche pas toutes les interface (si un jour eth1 poppe),
#: Mais de toutes façons on ne drope que des interfaces qu'on a réussi à identifier
find_iface = re.compile(r'eth0(?:\.[0-9]+)?')
arpwatched_nets = sum([gestion.config.NETs[nom] for nom in ['all', 'adm', 'accueil', 'isolement', 'personnel-ens', 'evenementiel']], [])

#: VLANS dont on ignore les "new station"/"new activity" *si elles ont une ip correspondant au vlan*
ignored_vlans = ['accueil', 'wifi']

def get_machine(unformated_mac):
    """Renvoie les informations sur la machine à partir de sa mac"""
    # Ceci est importé ici car cela génère une connexion ldap (sic)
    from gestion.tools.locate_mac import trace_machine, format_mac, info_machine
    mac = format_mac(unformated_mac)
    return u"\n" + info_machine(mac) + u"\n" + trace_machine(mac)

def get_subject(headers_list):
    for line in headers_list:
        if line.lower().startswith('subject:'):
            return line[9:].strip()
    return None

def drop_report(subject, ip):
    """Détermine à partir du ``subject`` du mail
       si il n'est pas nécessaire d'envoyer une notification pour cet évènement.
       
       Renvoie ``True`` si il faut le dropper.
       """
    # On récupère l'interface et l'ip dans le sujet
    ifaces = find_iface.findall(subject)
    iface = ifaces[0] if ifaces else None
    if not iface is None:
        # On détermine le vlan
        vlans = re.findall(r"\.([^\.]*)$", iface)
        try:
            vlan = int(vlans[0]) if vlans else None
        except ValueError:
            vlan = None
    ips = find_ip.findall(subject)
    ip = ips[0] if ips else None
    #print("%r, %r" % (ip, iface))
    #print(arpwatched_nets)
    if u"new station" in subject or "new activity" in subject:
            if not vlan is None:
                vlannames = [k for (k,v) in gestion.config.vlans.iteritems() if v == vlan]
                #print("vlannames : %r" % vlannames)
                if vlannames and vlannames[0] in ignored_vlans:
                    #print("%r in %r ?" % (ip, gestion.config.NETs[vlannames[0]]))
                    if AddrInNets(ip, gestion.config.NETs[vlannames[0]]):
                        # On ignore les new station dont l'IP est sur le bon vlan
                        return True
    return False

def report(texte, fallback=False):
    """Envoi d'un rapport"""
    textes = texte.splitlines(True)
    try:
        i = textes.index('\n')
    except ValueError:
        print("wrong formatted mail", file=sys.stderr)
        return

    headers = textes[:i]
    textes = textes[(i+1):]

    # Extrait le sujet
    subject = get_subject(headers)

    # Cherche les IP dans le corps
    try:
        ip = set(find_ip.findall(texte)).pop()
    except KeyError:
        ip = None
    # On complète le message seulement en cas de flip flop
    if u'flip flop' in subject and ip is not None and AddrInNets(ip, arpwatched_nets):
        try:
            macs = find_mac.findall(texte)
            for mac in macs:
                textes.append(get_machine(mac))
        except Exception as exc:
            # En cas d'exception, on envoie le traceback
            import traceback
            textes.append(u'\n')
            textes.append(u''.join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)))
    if fallback:
        textes.append(u'\nAttention, arpwatch/sendmail_server.py inactif !\n')
    textes.append(u'\n-- \narpwatch_sendmail.py\n')

    out = ''.join(textes)
    if recipients and not drop_report(subject, ip):
        sendmail(u"arpwatch@crans.org", recipients, subject, out,
            more_headers = {
                'X-Mailer': __file__,
            })
    else:
        print(out)

if __name__ == '__main__':
    data = sys.stdin.read()
    # On essaye d'envoyer le mail à la socket de sendmail_server
    # et au pire, on envoie nous-même le mail
    try:
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.connect(common.SOCKET_FILE)
        s.send(data)
        s.close()
    except socket.error:
        report(data, fallback=True)
