#!/usr/bin/env python3
# -*- coding: utf-8 -*-
###########################################################################
# Eole NG - 2007
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  cf /root/LicenceEole.txt
# eole@ac-dijon.fr
#
# fic_modules
#
# mise en place des fichiers sauvegardés sur zephir
# (niveau serveur, module et variante)
#
###########################################################################

import os, sys, shutil

public_dir = '/var/spool/uucppublic'
module_dir = public_dir + '/fichiers_zephir'

from zephir.lib_zephir import zephir_dir, get_file_perms, get_container_file, creole_vars, check_paqfile
from creole.config import eoleroot

def format_rights(rights):
    """prépare la liste des commandes a executer pour appliquer les permissions"""
    dic_rights = {}
    for fic_right, data in list(rights.items()):
        fic_right = fic_right[fic_right.index('/')+1:]
        dic_rights[fic_right] = []
        try:
            mode, user, group, recursive = data
        except:
            print(fic_right, ' : données invalides')
        else:
            if mode != "":
                # application des droits unix
                dic_rights[fic_right].append('chmod %s ' % (mode))
            recurse = ""
            if recursive == True:
                recurse = " -R "
            if user != "":
                # application de l'utilisateur
                dic_rights[fic_right].append('chown%s %s ' % (recurse, user))
            if group != "":
                dic_rights[fic_right].append('chgrp%s %s ' % (recurse, group))
    return dic_rights

def main():
    # traitement des droits sur les templates creole
    if os.path.isdir(os.path.join(public_dir, 'fichiers_perso')):
        for var in (False, True):
            rights = get_file_perms(public_dir, 'fichiers_perso', var)
            # application des droits et utilisateurs
            dic_rights = format_rights(rights)
            cmd_rights = ['cd %s/fichiers_perso;' % (public_dir)]
            for template in list(dic_rights.keys()):
                dest = template + ";"
                cmd_rights.append(dest.join(dic_rights[template]) + dest)
            res = os.system(" ".join(cmd_rights))

    if os.path.isdir(os.path.join(public_dir, 'fichiers_zephir')):
        # droits des fichiers du serveur
        rights = get_file_perms(public_dir, 'fichiers_zephir')
        # droits au niveau variante
        rights_var = get_file_perms(public_dir, 'fichiers_zephir', True)
        for fic_right, perms in list(rights_var.items()):
            rights[fic_right] = perms
        # application des droits et utilisateurs
        dic_rights = format_rights(rights)

        # on traite les fichiers spécifiés dans le module
        # lecture de la liste des fichiers à replacer (fournie avec le module)
        destinations = {}
        # liste des paquetages additionnels (mise en place par maj ?)
        liste_pkg = []
        # liste des fichiers à supprimer (ex : retrait d'un fichier de variante)
        liste_remove = []
        # on récupère le nom de base du module
        try:
            module = creole_vars.get_creole('eole_module','')
            assert module
        except:
            raise Exception('impossible de trouver le nom de module')

        for fic_type in (module, 'acad'):
            if os.path.isfile(os.path.join(zephir_dir, 'zephir_conf', 'fichiers_%s' % fic_type)):
                try:
                    fic_tmp = open(os.path.join(zephir_dir, 'zephir_conf', 'fichiers_%s' % fic_type), 'r')
                    liste = fic_tmp.readlines()
                    fic_tmp.close()
                except IOError:
                    print ("erreur d'ouverture du fichier module")
                else:
                    # on crée un dictionnaire pour chaque fichier
                    # 'nom_fichier':'destination'
                    for ligne in liste:
                        if ligne.startswith('/'):
                            # gestion des conteneurs
                            ligne_dest = get_container_file(ligne)
                            if ligne_dest.endswith('/'):
                                ligne_dest = ligne_dest[:-1]
                            destinations[ligne[ligne.rindex('/')+1:].strip()] = ligne_dest[:ligne_dest.rindex('/')+1]

        # on installe ensuite les fichiers de variante
        # lecture de la liste des fichiers à replacer (fournie avec la variante)
        try:
            fic_tmp = open(module_dir+'/variante/fichiers_variante', 'r')
            data = fic_tmp.read()
            fic_tmp.close()
        except IOError:
            # pas de fichiers spécifiques à la variante
            pass
        else:
            # on sépare la liste des packages et les fichiers classiques
            liste_pkg.extend(data.split('%%')[1].split('\n'))
            liste = data.split('%%')[0].split('\n')
            # mise à jour du dictionnaire
            # préparation de la liste de copie
            for ligne in liste:
                if ligne.startswith('/'):
                    ligne_dest = get_container_file(ligne)
                    if ligne_dest.endswith('/'):
                        ligne_dest = ligne_dest[:-1]
                    fichier = ligne[ligne.rindex('/')+1:].strip()
                    if fichier in destinations:
                        # on vérifie que la destination est la même que celle spécifiée pour le module
                        if ligne_dest[:ligne_dest.rindex('/')+1] == destinations[fichier]:
                            del(destinations[fichier])
                            destinations['variante/'+fichier] = ligne_dest[:ligne_dest.rindex('/')+1]
                    else:
                        destinations['variante/'+fichier] = ligne_dest[:ligne_dest.rindex('/')+1]
        # on refait la même chose avec les fichiers spécifiques pour ce serveur
        # (liste des fichiers créée par l'administrateur de la machine)
        try:
            fic_tmp = open(module_dir+'/fichiers_zephir', 'r')
            data = fic_tmp.read()
            fic_tmp.close()
        except IOError:
            pass
        else:
            # on ajoute les paquetages spécifiques au serveur
            liste_pkg.extend(data.split('%%')[1].split('\n'))
            # on traite les fichiers à sauvegarder
            liste = data.split('%%')[0].split('\n')
            # on met à jour le dictionnaire
            for ligne in liste:
                if ligne.startswith('/'):
                    ligne_dest = get_container_file(ligne)
                    if ligne_dest.endswith('/'):
                        ligne_dest = ligne_dest[:-1]
                    # pour le nom du fichier, on conserve la partie '::conteneur'
                    fichier = ligne[ligne.rindex('/')+1:].strip()
                    # on interdit de modifier la destination des fichiers spécifiés par le module
                    if fichier not in destinations:
                        # si ce fichier existe dans la variante
                        if 'variante/'+fichier in destinations:
                            # avec la meme destination
                            if ligne_dest[:ligne_dest.rindex('/')+1] == destinations['variante/'+fichier]:
                                # on le remplace par celui-ci
                                del(destinations['variante/'+fichier])
                        destinations[fichier] = ligne_dest[:ligne_dest.rindex('/')+1]
        # supression de fichiers si demandé au niveau variante/serveur
        for fic_remove in ('variante/removed', 'removed'):
            try:
                fic_tmp = open(module_dir+'/%s' % fic_remove, 'r')
                data = fic_tmp.read()
                fic_tmp.close()
            except IOError:
                pass
            else:
                liste = data.split('\n')
                # préparation de la liste de supression
                for ligne in liste:
                    ligne=ligne.strip()
                    if ligne.startswith('/'):
                        ligne_dest = get_container_file(ligne)
                        if ligne_dest not in liste_remove and os.path.exists(ligne_dest):
                            liste_remove.append(ligne_dest)
                    elif ligne.startswith('fichiers_perso'):
                        # cas des templates (fichiers_perso): toujours dans le conteneur maître
                        template_file = os.path.join(eoleroot, 'distrib', os.path.basename(ligne))
                        if os.path.exists(template_file):
                            liste_remove.append(template_file)
        # on gère en premier les répertoires, puis les fichiers particuliers
        # pour éviter un écrasement (ex: répertoire des modèles era et modèle d'une variante)
        list_dest = []
        list_files = []
        missing_files = []
        for fichier in list(destinations.keys()):
            if os.path.isdir(os.path.abspath(module_dir+os.sep+fichier)):
                list_dest.append(fichier)
            elif os.path.exists(os.path.abspath(module_dir+os.sep+fichier)):
                list_files.append(fichier)
            else:
                missing_files.append(fichier)
        # log des fichiers absents
        print("fichiers absents : %s" % ",".join(missing_files))
        # liste ordonnée des fichiers
        list_dest.extend(list_files)
        # suppression des fichiers de liste_remove avant de copier les nouveaux
        # fichiers. évite les problèmes en cas de déplacement d'un fichier
        # de variante vers serveur (et inversement)
        for fic_rm in liste_remove:
            if os.path.isdir(fic_rm):
                shutil.rmtree(fic_rm)
            else:
                os.unlink(fic_rm)
        # mise en place des fichiers reçus
        cmd_mv = ['cd %s;' % module_dir]
        cmd_rights = []
        for fichier in list_dest:
            if fichier.strip() != "":
                if '::' in fichier:
                    # fichier à destination d'un conteneur
                    fic_dest = fichier.split('::')[0]
                else:
                    fic_dest = fichier
                if os.path.isdir(os.path.abspath(module_dir+os.sep+fichier)):
                    # on crée la destination si elle n'existe pas
                    if not os.path.isdir(os.path.join(os.path.abspath(destinations[fichier]), fic_dest)):
                        os.makedirs(os.path.join(os.path.abspath(destinations[fichier]), fic_dest))
                    if check_paqfile(os.path.join(os.path.abspath(destinations[fichier]), fic_dest.lstrip(os.sep))):
                        # répertoire de destination référencé par un paquet, on enlève les fichiers gérés par un paquet
                        def walk_dir(dest_dir, dirname, filenames):
                            copy_dir = dirname.replace(module_dir+os.sep, '', 1)
                            # on vérifie quels fichiers sont concernés par un paquet
                            if check_paqfile(os.path.join(dest_dir, copy_dir)):
                                for filename in filenames:
                                    dest_file = os.path.join(dest_dir, copy_dir, filename)
                                    if os.path.isfile(dest_file) and check_paqfile(dest_file):
                                        # suppression du fichier dans le répertoire temporaire
                                        print("fichier ignoré (appartient à un paquet): ", os.path.join(dirname, filename))
                                        os.unlink(os.path.join(dirname, filename))
                        os.walk(os.path.join(module_dir, fichier),
                                walk_dir,
                                os.path.abspath(destinations[fichier]))
                    # copie de tous les fichiers du répertoire temporaire
                    # après avoir vérifié qu'ils n'ont pas tous été ignorés
                    if len(os.listdir(os.path.join(module_dir, fichier))) > 0:
                        cmd_mv.append('/bin/cp -rpf')
                        cmd_mv.append(fichier + "/*")
                        # emplacement de destination du fichier
                        path_dest = os.path.join(os.path.abspath(destinations[fichier]), fic_dest.lstrip(os.sep)) + os.sep +';'
                        cmd_mv.append(path_dest)
                        # application des droits pour le fichier en question
                        if fichier in dic_rights:
                            cmd_rights.append(path_dest.join(dic_rights[fichier]) + path_dest)
                        # on applique les droits spécifiques aux sous-dossiers/fichiers si ils existent
                        fichiers = list(dic_rights.keys())
                        fichiers.sort()
                        for child_fic in fichiers:
                            if child_fic.startswith(fichier) and child_fic != fichier:
                                # gestion conteneur
                                if '::' in fichier:
                                    child_dest = child_fic.replace(fichier, fic_dest, 1)
                                else:
                                    child_dest = child_fic
                                path_child = os.path.abspath(os.path.join(destinations[fichier], child_dest.lstrip('/'))) + ';'
                                cmd_rights.append(path_child.join(dic_rights[child_fic]) + path_child)
                else:
                    if not os.path.isdir(os.path.abspath(os.path.dirname(destinations[fichier]))):
                        os.makedirs(os.path.abspath(os.path.dirname(destinations[fichier])))
                    cmd_mv.append('/bin/mv -f')
                    # nom du fichier à déplacer
                    cmd_mv.append(fichier)
                    # emplacement de destination du fichier
                    fic_dest = fic_dest.replace('variante/', '')
                    path_dest = destinations[fichier]+fic_dest+';'
                    cmd_mv.append(path_dest)
                    # application des droits pour le fichier en question
                    if fichier in dic_rights:
                        cmd_rights.append(path_dest.join(dic_rights[fichier]) + path_dest)

        # si il existe des fichiers, on les déplace
        if len(cmd_mv) > 3:
            cmd_mv.extend(cmd_rights)
            res = os.system(" ".join(cmd_mv))
            if res != 0:
                # on garde une trace du problème dans le rapport
                print("erreur de copie  : " + " ".join(cmd_mv))

        # mise en place de la liste des paquetages additionnels
        try:
            # création du fichier pkg_zephir qui sera utilisé par
            # la mise à jour pour installer des rpms supplémentaires
            fic_pkg = open('/etc/eole/pkg_zephir' , 'w')
        except:
            print("écriture de la liste des paquetages impossible !!!")
        else:
            for ligne in liste_pkg:
                # si la ligne contient un nom de paquetage
                if not (ligne.startswith('#') or ligne == ''):
                    fic_pkg.write(ligne+'\n')
            fic_pkg.close()

    # l'installation des fichiers est terminée
    sys.exit(0)

if __name__ == '__main__':
    main()
