diff --git a/surveillance/comptes_inactifs.py b/surveillance/comptes_inactifs.py index 140dbc9f..2e437152 100755 --- a/surveillance/comptes_inactifs.py +++ b/surveillance/comptes_inactifs.py @@ -2,7 +2,7 @@ # -*- coding: iso-8859-15 -*- """ -Repère les comptes inactifs en parsant les logs de derniere connexion +Repère les comptes inactifs en parsant les logs de dernière connexion de sshd et dovecot. """ @@ -13,19 +13,37 @@ de sshd et dovecot. import sys, os, sre, time, cPickle from time import mktime, time, localtime, strptime, strftime from socket import gethostname +from smtplib import SMTP host = gethostname() mail_address = u'disconnect@crans.org' mail_sender = u"Comptes inactifs " +template_path = '/usr/scripts/templates/comptes_inactifs.%d' sys.path.append('/usr/scripts/gestion') from affich_tools import tableau -from email_tools import send_email +from email_tools import send_email, parse_mail_template from ldap_crans import crans_ldap from config import ann_scol db = crans_ldap() +def nb_mails_non_lus(login): + """ + Renvoie le nombre de mails non lus de login, ou None si impossible à + déterminer. + """ + try: + maildir = '/var/mail/%s/new' % login + if os.path.isdir(maildir): + return len(os.listdir(maildir)) + else: + return 0 + except: + # Arrive quand le script n'a pas les bons droits pour lire /var/mail + return None + + class ComptesInactifs: re = sre.compile(r'^(\w+\s+\d+\s+\d+:\d+:\d+).*(?:' r'dovecot.*Login: user=<|' @@ -49,7 +67,7 @@ class ComptesInactifs: f.write('%s %d\n' % (host, os.getpid())) f.close() - # A partir de la, on a le lock + # À partir de là, on a le lock if os.path.isfile(filename): self.dic = cPickle.load(file(filename)) else: @@ -62,22 +80,22 @@ class ComptesInactifs: def update(self, login, timestamp): """ - Met a jour l'entree correspondant au login donnee, ainsi que - l'entree '!', qui correspond a la plus vieille entree. + Met à jour l'entrée correspondant au login donné, ainsi que + l'entrée '!', qui correspond à la plus vieille entrée. """ dic = self.dic timestamp = int(timestamp) - # Mise a jour de l'entree la plus vieille + # Mise à jour de l'entrée la plus vieille if not dic.has_key('!') or timestamp < dic['!']: dic['!'] = timestamp - # Mise a jour de l'entree correspondant au login + # Mise à jour de l'entrée correspondant au login if not dic.has_key(login) or timestamp > dic[login]: dic[login] = timestamp def update_from_syslog(self, loglines): - """ Met a jour le dico avec les lignes de syslog donnees """ + """ Met à jour le dico avec les lignes de syslog données """ annee = localtime(time())[0] now = time() + 600 nombre = 0 @@ -95,9 +113,9 @@ class ComptesInactifs: print '%d ligne(s) pertinente(s)' % nombre def do_log(self): - """ Lit des lignes de log sur l'entree std et met a jour le dico """ + """ Lit des lignes de log sur l'entrée std et met à jour le dico """ self.update_from_syslog(sys.stdin) - print 'Lecture des logs terminee' + print 'Lecture des logs terminée' def do_dump(self): """ Affiche le contenu du dico """ @@ -110,18 +128,28 @@ class ComptesInactifs: def get_idle_accounts(self, since=32*24*3600): """ - Renvoie la liste de ceux qui ne se sont pas connectes depuis - since secondes, par defaut un mois (32 jours, pour etre sur). + Renvoie la liste des couples (login, objet Adherent) de ceux qui + ne se sont pas connectés depuis since secondes, par défaut un mois + (32 jours, pour etre sûr). """ oldest = self.dic.get('!', int(time())) limit = int(time()) - since - liste = [a - for a in os.listdir('/home') - if os.path.isdir('/var/mail/' + a) - and self.dic.get(a, oldest) < limit] + liste = [] + for x in os.listdir('/home'): + if os.path.isdir('/var/mail/' + x) and self.dic.get(x, oldest) < limit: + a = db.search('uid=%s' % x)['adherent'] + if a: + liste.append((x, a[0])) + else: + print 'uid=%s introuvable' % x + liste.sort() return liste def do_summary(self): + """ + Envoie à disconnect un résume des comptes inactifs depuis plus d'un + mois. + """ modele = u"""*Membres inscrits ne s'étant pas connectés depuis plus d'un mois* %(inscrits)s @@ -141,32 +169,18 @@ L'analyse des logs remonte au %(oldest)s. -- comptes_inactifs.py """ - liste = [] - for x in self.get_idle_accounts(): - a = db.search('uid=%s' % x)['adherent'] - if a: - liste.append((x, a[0])) - else: - print 'uid=%s introuvable' % x - liste.sort() - inscrits = [] anciens = [] - for (x, a) in liste: + for (x, a) in self.get_idle_accounts(): date = self.dic.get(x) if date: date = strftime(u'%d/%m/%Y %H:%M', localtime(date)) else: date = u'Jamais' forward = os.path.isfile('/home/%s/.forward' % x) and u'X' or u'' - try: - maildir = '/var/mail/%s/new' % x - mail = os.path.isdir(maildir) and os.listdir(maildir) and u'X' or u'' - except: - # Arrive quand le script n'a pas les bons droits pour lire - # /var/mail - mail = u'?' + mail = nb_mails_non_lus(x) + mail = mail == None and u'?' or mail > 0 and u'X' or u' ' ligne = (a.id(), x, a.Nom(), date, forward, mail) if ann_scol in a.paiement(): inscrits.append(ligne) @@ -189,6 +203,22 @@ comptes_inactifs.py u'Comptes inactifs', modele % locals()) + def do_spam(self): + """ Envoie un mail explicatif aux possesseurs de compte inactif """ + # Nombre de personnes concernées, en expansant de droite à gauche : + # inscrit/ancien, avec/sans procmail, avec/sans mail non lu + # Voir aussi template_path + stats = [0, 0, 0, 0, 0, 0, 0, 0] + + # On factorise la connexion + smtp = SMTP() + smtp.connect() + + for (x, a) in self.get_idle_accounts(): + pass + + smtp.quit() + if __name__ == '__main__': args = sys.argv[1:] @@ -199,7 +229,7 @@ if __name__ == '__main__': commande = args[0] if commande not in ('log', 'dump', 'summary'): - sys.stderr.write("Commande incorrecte : %s\n" % args[1]) + sys.stderr.write("Commande incorrecte : %s\n" % commande) sys.exit(1) ci_db = ComptesInactifs('/usr/scripts/var/comptes_inactifs.dict')