
Ignore-this: c8770ab7e0b8cab7478cd91231c24dd8 darcs-hash:20130126184831-3a55a-be310f9743a6c9298845f6ab2c2dcb96d0765079.gz
322 lines
11 KiB
Python
Executable file
322 lines
11 KiB
Python
Executable file
#! /usr/bin/env python
|
|
# -*- coding:utf-8 -*-
|
|
|
|
"""
|
|
Script de détection de fichiers potentiellement copyrightés dans les
|
|
répertoires des adhérents. Les tests se basent sur le nom et la taille
|
|
des fichiers.
|
|
|
|
Copyright (C) Nicolas Bruot
|
|
Licence : GPL v2
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import re
|
|
import time
|
|
import shutil
|
|
|
|
# Variables générales
|
|
|
|
sendto = 'bureau@lists.crans.org'
|
|
analysed_path = '/home-adh/'
|
|
|
|
old_path = '/usr/scripts/var/copyrighted-content.old'
|
|
new_path = '/usr/scripts/var/copyrighted-content.new'
|
|
diffs_path = '/usr/scripts/var/copyrighted-content.diffs'
|
|
alert_path = '/usr/scripts/var/copyrighted-content.alert'
|
|
|
|
# ======================================================================
|
|
# Variables pour le calcul du score.
|
|
# ==================================
|
|
|
|
# Score minimum pour être enregistré dans le fichier des différences.
|
|
min_score = 10
|
|
|
|
# Score à partir duquel une notification est envoyée immédiatement par
|
|
# email (sans attendre l'exécution avec l'option --report).
|
|
notif_score = 40
|
|
|
|
# Taille en dessous de laquelle aucun test n'est effectué sur le
|
|
# fichier (augmenter pour améliorer la rapidité du script).
|
|
pass_size = 10e3
|
|
|
|
# Taille en dessous de laquelle seuls les filtres regexps_light sont
|
|
# testés.
|
|
light_size = 40e3
|
|
|
|
# Caractères non alphanumériques ("_" inclus)
|
|
blank = '[^a-zA-Z0-9]'
|
|
lblank = '([^a-zA-Z0-9]|^)' # blanc ou début de chaine
|
|
rblank = '([^a-zA-Z0-9]|$)' # blanc ou fin de chaine
|
|
|
|
# regexps, regexps_light : motifs contribuant au score du fichier.
|
|
# Pour qu'un motif contribue au score, il doit être matché et, si le
|
|
# champ min_size existe, respecter la condition de taille minimum du
|
|
# fichier.
|
|
# Si un ou plusieurs motifs avec un champ crit_size sont matchés,
|
|
# size_score est ajouté au score si la taille du fichier dépasse la
|
|
# valeur minimale des crit_size matchés.
|
|
# Seuls les motifs de regexp_light sont testés pour les fichiers d'une
|
|
# taille inférieure à light_size. Pour ce dictionnaire, il ne peut pas
|
|
# y avoir de champs min_size ou crit_size.
|
|
size_score = 10
|
|
regexps_light = {'\.torrent$': {'score':40},
|
|
'\.srt$': {'score':40},
|
|
'key.?gen': {'score':40}}
|
|
regexps = {'\.avi$': {'score':10, 'min_size':1e6, 'crit_size':1e8},
|
|
'\.mkv$': {'score':10, 'min_size':1e6, 'crit_size':1e8},
|
|
'\.mov$': {'score':10, 'min_size':1e6, 'crit_size':1e8},
|
|
'\.mpeg$': {'score':10, 'min_size':1e6, 'crit_size':1e8},
|
|
'\.mp4$': {'score':10, 'min_size':1e6, 'crit_size':1e8},
|
|
'\.mpg$': {'score':10, 'min_size':1e6, 'crit_size':1e8},
|
|
'\.ogm$': {'score':10, 'min_size':1e5, 'crit_size':1e8},
|
|
'\.wmv$': {'score':10, 'min_size':1e5, 'crit_size':1e8},
|
|
'\.flv$': {'score':10, 'min_size':1e6, 'crit_size':1e8},
|
|
'\.mp3$': {'score':10, 'min_size':1e5},
|
|
'\.m4a$': {'score':10, 'min_size':1e5},
|
|
'\.zip$': {'score':5, 'min_size':1e5, 'crit_size':1e8},
|
|
'\.gz$': {'score':3, 'min_size':1e5, 'crit_size':1e8},
|
|
'\.rar$': {'score':5, 'min_size':1e5, 'crit_size':1e8},
|
|
'\.dmg$': {'score':10, 'min_size':1e5, 'crit_size':1e8},
|
|
'\.pdf$': {'score':5, 'min_size':1e5, 'crit_size':2e7},
|
|
'\.ogg$': {'score':10, 'min_size':1e5},
|
|
'\.iso$': {'score':15, 'min_size':1e5, 'crit_size':1e8},
|
|
|
|
# Episodes (S01E01, Episode01...)
|
|
's\d{1,2}e\d': {'score':20, 'min_size':1e8},
|
|
lblank + '\d{1,2}x\d{1,2}' + rblank: {'score':20, 'min_size':1e8},
|
|
'episode.?\d': {'score':20},
|
|
lblank + 's\d{1,2}' + rblank: {'score':20, 'min_size':1e7},
|
|
lblank + 'e\d{1,2}' + rblank: {'score':20, 'min_size':1e7},
|
|
lblank + 'vol.{0,2}\d{1,2}' + rblank: {'score':20},
|
|
|
|
# Crochets []
|
|
'\[(?!pot).*\]': {'score':5},
|
|
|
|
# "01 Toto.mp3"...
|
|
'^\d{1,2}' + blank + '.*\.(mp3|ogg|wmv)$': {'score':10},
|
|
|
|
# Autres mots-clés
|
|
blank + 'fr' + rblank: {'score':10},
|
|
blank + 'eng' + rblank: {'score':10},
|
|
|
|
blank + 'vo' + rblank: {'score':10, 'min_size':1e7},
|
|
blank + 'vost' + rblank: {'score':30},
|
|
blank + 'vostfr' + rblank: {'score':30},
|
|
blank + 'vosten' + rblank: {'score':30},
|
|
|
|
blank * 3: {'score':3},
|
|
|
|
lblank + 'dvd.?(rip|)' + rblank: {'score':20},
|
|
|
|
lblank + 'cd' + rblank: {'score':10, 'min_size':1e8},
|
|
lblank + 'xvid' + rblank: {'score':20},
|
|
lblank + 'hdtv' + rblank: {'score':20},
|
|
lblank + 'hq' + rblank: {'score':20},
|
|
lblank + 'vtv' + rblank: {'score':20},
|
|
lblank + 'tvrip' + rblank: {'score':20},
|
|
lblank + 'dvtv' + rblank: {'score':20},
|
|
lblank + 'bt' + rblank: {'score':20},
|
|
lblank + 'fqm' + rblank: {'score':20},
|
|
lblank + 'web.?rip' + rblank: {'score':20},
|
|
lblank + 'natzox' + rblank: {'score':10},
|
|
lblank + 'sfm' + rblank: {'score':20},
|
|
lblank + 'lbp' + rblank: {'score':20},
|
|
lblank + 'hd' + rblank: {'score':10},
|
|
lblank + 'hdp' + rblank: {'score':10},
|
|
lblank + 'itdk' + rblank: {'score':20},
|
|
lblank + 'ust' + rblank: {'score':20},
|
|
lblank + 'fxg' + rblank: {'score':20},
|
|
|
|
lblank + 'part.?\d': {'score':10, 'min_size':1e7}}
|
|
|
|
# Si aucune expression n'a été matchée, un fichier d'une taille
|
|
# supérieure à suspect_size se verra attribuer un score de
|
|
# suspect_size_score.
|
|
suspect_size = 200e6
|
|
suspect_size_score = 30
|
|
|
|
# ======================================================================
|
|
|
|
# Pour les gros fichiers, on teste l'ensemble des expressions
|
|
# régulières :
|
|
regexps_full = dict(regexps_light, **regexps)
|
|
|
|
# Compilation des expressions régulières :
|
|
for key in regexps_light.keys():
|
|
regexps_light[re.compile(key, re.I).findall] = regexps_light[key]
|
|
del regexps_light[key]
|
|
|
|
for key in regexps_full.keys():
|
|
regexps_full[re.compile(key, re.I).findall] = regexps_full[key]
|
|
del regexps_full[key]
|
|
|
|
|
|
def human_readable(num):
|
|
"""Convertit le nombre en entrée (taille d'un fichier) en une
|
|
chaine "human-readable". Exemple: 12000 => 12K"""
|
|
|
|
for x in ['', 'K', 'M', 'G', 'T']:
|
|
if num < 1000.:
|
|
return '%3.0f%s' % (num, x)
|
|
num /= 1000.
|
|
|
|
|
|
def get_score(file, size):
|
|
"""Calcule le score d'un fichier d'après des expressions
|
|
régulières et des conditions de taille."""
|
|
|
|
score = 0
|
|
|
|
if size < light_size:
|
|
# On ne fait que les tests simples.
|
|
|
|
for key in regexps_light:
|
|
m = key(file) # liste d'expressions matchées
|
|
param = regexps_light[key]
|
|
|
|
score += len(m)*param['score']
|
|
|
|
return score
|
|
|
|
else:
|
|
# On effectue les tests complexes.
|
|
|
|
crit_size = 1e99
|
|
# On parcourt les expressions régulières.
|
|
for key in regexps_full:
|
|
m = key(file) # liste d'expressions matchées
|
|
param = regexps_full[key]
|
|
|
|
# Tests avec min_size.
|
|
if not param.has_key('min_size') or size >= param['min_size']:
|
|
score += len(m)*param['score']
|
|
|
|
# Mise à jour de crit_size.
|
|
if param.has_key('crit_size'):
|
|
crit_size = min(crit_size, param['crit_size'])
|
|
|
|
# Si le fichier est gros par rapport à une taille définie par les
|
|
# expressions matchées, on augmente le score.
|
|
if size >= crit_size:
|
|
score += size_score
|
|
|
|
# Si le score est nul, les fichiers gros sont quand même suspects.
|
|
if score == 0 and size >= suspect_size:
|
|
score = suspect_size_score
|
|
|
|
return score
|
|
|
|
|
|
def analyse():
|
|
"""Liste les fichiers dans le dossier à analyser et ajoute les
|
|
nouveaux fichiers à copyrighted.diffs. Envoie une notification si
|
|
des scores trop élevés sont trouvés."""
|
|
|
|
if os.path.isfile(new_path):
|
|
# Rotation des fichiers
|
|
shutil.move(new_path, old_path)
|
|
# Lecture de old_files
|
|
old_files = open(old_path).read().split('\n')
|
|
else:
|
|
old_files = []
|
|
# Ouverture des autres fichiers
|
|
new_file = open(new_path, 'w')
|
|
diffs_file = open(diffs_path, 'a')
|
|
alert_file = open(alert_path, 'w')
|
|
os.chmod(new_path, 0600)
|
|
os.chmod(diffs_path, 0600)
|
|
os.chmod(alert_path, 0600)
|
|
|
|
# Listage des fichiers
|
|
for root, sub_folders, files in os.walk(analysed_path):
|
|
|
|
for file in files:
|
|
filename = os.path.join(root, file)
|
|
try:
|
|
filesize = os.path.getsize(filename)
|
|
except OSError: # le fichier a pu être supprimé entre temps
|
|
filesize = 0
|
|
|
|
if filesize < pass_size:
|
|
# On va pas tester les fichiers trop petits.
|
|
continue
|
|
|
|
score = get_score(file, filesize)
|
|
|
|
if score >= min_score:
|
|
# On inscrit le fichier.
|
|
new_file.write(filename + '\n')
|
|
# Si c'est un nouveau fichier, on l'ajoute au
|
|
# fichier diffs.
|
|
if not filename in old_files:
|
|
date = time.strftime("%Y-%m-%d %H:%M",
|
|
time.localtime())
|
|
line = (str(score) + '\t' + date +
|
|
'\t' + human_readable(filesize) +
|
|
'\t' + filename + '\n')
|
|
|
|
diffs_file.write(line)
|
|
|
|
# Pour des scores vraiment élevés on va alerter
|
|
# par email immédiatement.
|
|
if score >= notif_score:
|
|
alert_file.write(line)
|
|
|
|
# On ne parcourt pas les dossiers "Mail".
|
|
if len(root.split('/')) == 3 and 'Mail' in sub_folders:
|
|
sub_folders.remove('Mail')
|
|
|
|
new_file.close()
|
|
diffs_file.close()
|
|
alert_file.close()
|
|
|
|
# Envoi d'une notification le cas échéant.
|
|
if os.path.getsize(alert_path) > 0:
|
|
sujet = "Alerte de fichiers potentiellement copyrightes"
|
|
os.system('sort -n -r ' + alert_path + ' | mail -s "' +
|
|
sujet + '" ' + sendto)
|
|
os.remove(alert_path)
|
|
|
|
|
|
def report():
|
|
"""Envoie un email récapitulatif du fichier de
|
|
copyrighted-content.diffs et purge le fichier."""
|
|
|
|
# Envoi de l'email contenant diffs_path
|
|
if os.path.isfile(diffs_path):
|
|
sujet = "Rapport des fichiers potentiellement copyrightes"
|
|
os.system('sort -n -r ' + diffs_path + ' | mail -s "' +
|
|
sujet + '" ' + sendto)
|
|
os.remove(diffs_path)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
help = """Détection de fichiers potentiellement copyrightés.
|
|
usage: fichiers_copyrightes.py [OPTION]
|
|
|
|
OPTION doit être l'une des suivantes :
|
|
|
|
--analyse
|
|
Ajoute dans le fichier copyrighted-content.diffs les nouveaux
|
|
fichiers du répertoire exploré attribués de leur score. Si des
|
|
fichiers de score élevé sont trouvés, un email de notification est
|
|
envoyé.
|
|
|
|
--report
|
|
Envoie un email récapitulatif du fichier copyrighted-content.diffs
|
|
et purge le fichier."""
|
|
|
|
# Gestion des options
|
|
if len(sys.argv) != 2:
|
|
print help
|
|
sys.exit(0)
|
|
elif sys.argv[1] == '--analyse':
|
|
analyse()
|
|
elif sys.argv[1] == '--report':
|
|
report()
|
|
else:
|
|
print help
|
|
sys.exit(0)
|