#/**
#@file    PostConfLib.py
#@brief   Librería o clase PostConf
#@class   PostConf
#@brief   Funciones para la postconfiguración de sistemas operativos.
#@warning License: GNU GPLv3+
#*/

import os
import re
import glob
import subprocess

import ogGlobals
import SystemLib
import FileSystemLib
import FileLib
import NetLib
import RegistryLib
import InventoryLib
import BootLib

#/**
#         ogCleanOs int_ndisk int_nfilesys
#@brief   Elimina los archivos que no son necesarios en el sistema operativo.
#@param   int_ndisk      nº de orden del disco
#@param   int_nfilesys   nº de orden del sistema de archivos
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@exception OG_ERR_NOTFOUND  Disco o particion no corresponden con un dispositivo.
#@exception OG_ERR_PARTITION Partición desconocida o no accesible.
#@note    Antes incluido en la funcion ogReduceFs
#@return  (nada)
#*/ ##
#ogCleanOs (1, 1)
def ogCleanOs (disk, par):
    t = InventoryLib.ogGetOsType (disk, par)
    if 'Linux' == t:
        BootLib.ogCleanLinuxDevices (disk, par)
        mntdir = FileSystemLib.ogMount (disk, par)
        for t in glob.glob ('{mntdir}/tmp/*'):
            shutil.rmtree (t)
    elif 'Windows' == t:
        for f in 'pagefile.sys', 'hiberfil.sys', 'swapfile.sys':
            p = FileLib.ogGetPath (src=f'{disk} {par}', file=f)
            if p: FileLib.ogDeleteFile (disk=disk, par=par, file=f)



#/**
#         ogInstallMiniSetup int_ndisk int_npartition str_filename [str_admuser str_admpassword bool_autologin [str_autouser str_autopassword] ]
#@brief   Metafunción para instalar archivo que se ejecutará en el arranque de Windows.
#@see     ogInstallFirstBoot ogInstallRunonce
#*/ ##
def ogInstallMiniSetup (disk, par, cmdfile, user=None, pwd=None, autologin=False, userauto=None, pwdauto=None):
    if user:
        ogInstallRunonce (disk, par, cmdfile, user, pwd, autologin, userauto, pwdauto)
    else:
        ogInstallFirstBoot (disk, par, cmdfile)


#/**
#         ogInstallFirstBoot int_ndisk int_npartition str_filename
#@brief   Crea unas claves del registro y el archivo cmd que se ejecutara en el primer arranque estando la maquina en un estado bloqueado
#@param   int_ndisk         nº de orden del disco
#@param   int_npartition    nº de orden de la partición
#@param   str_filename      nombre del archivo .cmd a ejecutar en el arranque
#@return  (nada)
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@note    El archivo estará en system32 y será visible por el sistema.
#*/ ##
#ogInstallFirstBoot ('1', '1', 'filename.cmd')
def ogInstallFirstBoot (disk, par, cmdfile):
    mntdir = FileSystemLib.ogMount (disk, par)
    if not mntdir: return
    for i in ['winnt', 'windows']:
        dir = FileLib.ogGetPath (file=f'{mntdir}/{i}/system32')
        if dir: cmddir = dir
    if not cmddir:
        SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{mntdir}/windows/system32')
        return
    full_cmdfile = f'{cmddir}/{cmdfile}'
    bn = os.path.basename (full_cmdfile)

    # Creamos el archivo cmd y creamos un par de comandos para que una vez acabe la
    # postconfiguracion resetee el mini setup, sino lo haria en cada arranque.
    with open (full_cmdfile, 'w') as fd:
        fd.write (r'REG ADD HKLM\System\Setup /v SystemSetupInProgress /t REG_DWORD /d 0 /f' + '\n')
        fd.write (r'REG ADD HKLM\System\Setup /v CmdLine /t REG_SZ /d "" /f' + '\n')

    # Crear los valores de registro necesarios para que se haga la ejecución del .cmd al aranque.
    RegistryLib.ogSetRegistryValue     (mntdir, 'SYSTEM', r'\Setup\SystemSetupInProgress', 1)
    RegistryLib.ogSetRegistryValue     (mntdir, 'SYSTEM', r'\Setup\SetupType', 4)
    #RegistryLib.ogDeleteRegistryValue (mntdir, 'SYSTEM', r'\Setup\CmdLine')
    RegistryLib.ogAddRegistryValue     (mntdir, 'SYSTEM', r'\Setup\CmdLine')
    RegistryLib.ogSetRegistryValue     (mntdir, 'SYSTEM', r'\Setup\CmdLine', f'cmd.exe /c {bn}')


#/**
#         ogInstallRunonce int_ndisk int_npartition str_filename str_adm_user str_adm_password bool_autologin [str_auto_user str_auto_password]
#@brief   Crea el archivo cmd que se ejecutara en el runonce de un usuario administrador
#@param   int_ndisk         nº de orden del disco
#@param   int_npartition    nº de orden de la partición
#@param   str_filename      nombre del archivo .cmd a ejecutar en el arranque (estara en system32 y sera visible por el sistema)
#@param   str_adm_user      usuario administrador para hacer autologin y ejecutar el runonce
#@param   str_adm_password  password del usuario administrador
#@param   bool_autologin    si despues de la postconfiguracion queremos que la maquina haga autologin (0 o 1)
#@param   str_auto_user     Usuario con el que queremos que haga autologin despues de la postconfiguracion
#@param   str_auto_password Password del usuario que hara autologin
#@return  (nada)
#@exception OG_ERR_FORMAT    Formato incorrecto.
#*/ ##
#ogInstallRunonce ('1', '1', 'filename.cmd', 'administrator', 'passadmin', '1', 'userauto', 'pwdauto')
#ogInstallRunonce ('1', '1', 'filename.cmd', 'administrator', 'passadmin', '0')
def ogInstallRunonce (disk, par, cmdfile, user, pwd, autologin, userauto=None, pwdauto=None):
    mountpoint = FileLib.ogGetPath (src=f'{disk} {par}', file='/')
    for i in ['winnt', 'windows']:
        dir = FileLib.ogGetPath (file=f'{mountpoint}/{i}/system32')
        if dir: cmddir = dir
    if not cmddir:
        SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{mountpoint}/Windows/System32')
        return
    full_cmdfile = f'{cmddir}/{cmdfile}'
    if not autologin:
        # Si no queremos hacer autologin despues de la postconfiguracion lo indicamos en las claves de registro
        with open (full_cmdfile, 'w') as fd:
            fd.write (r'DEL C:\ogboot.*' + '\n')
            fd.write (r'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon /t REG_SZ /d 0 /f' + '\n')
            fd.write (r'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultUserName /t REG_SZ /d "" /f' + '\n')
            fd.write (r'REG DELETE "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultPassword /f' + '\n')
    else:
        # Si queremos hacer autologin despues de la postconfiguracion introducimos la informacion en las claves de registro
        with open (full_cmdfile, 'w') as fd:
            fd.write (r'DEL C:\ogboot.*' + '\n')
            fd.write (r'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon /t REG_SZ /d 1 /f' + '\n')
            fd.write (fr'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultUserName /t REG_SZ /d "{userauto}" /f' + '\n')
            fd.write (fr'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultPassword /t REG_SZ /d "{pwdauto}" /f' + '\n')
    #Creamos las claves de registro necesarias para que meter el cmd en el runonce del usuario y este haga autologin
    RegistryLib.ogAddRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows\CurrentVersion\RunOnce\PostConfiguracion')
    RegistryLib.ogSetRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows\CurrentVersion\RunOnce\PostConfiguracion', rf'C:\windows\system32\{cmdfile}')
    RegistryLib.ogAddRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoAdminLogon')
    RegistryLib.ogSetRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoAdminLogon', 1)
    RegistryLib.ogAddRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName')
    RegistryLib.ogSetRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName', user)
    RegistryLib.ogAddRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName')
    RegistryLib.ogSetRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName', '.\\')
    RegistryLib.ogAddRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword')
    RegistryLib.ogSetRegistryValue    (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultPassword', pwd)
    RegistryLib.ogDeleteRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceAutoLockOnLogon')
    RegistryLib.ogDeleteRegistryValue (mountpoint, 'software', r'\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoLogonCount')

#/**
#         ogAddCmd int_ndisk int_npartition str_filename str_commands
#@brief   Añade comandos al cmd creado por ogInstalMiniSetup
#@param   int_ndisk         nº de orden del disco
#@param   int_npartition    nº de orden de la partición
#@param   str_filename      nombre del fichero cmd (siempre se guardara en windows\system32\para que sea visible por el sistema
#@param   str_commands      comando o comandos que se añadiran al fichero
#@return
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@exception OG_ERR_PARTITION Tipo de partición desconocido o no se puede montar.
#*/ ##
#ogAddCmd ('1', '1', 'filename.cmd', 'command')
def ogAddCmd (disk, par, cmdfile, cmd):
    mountpoint = FileSystemLib.ogMount (disk, par)
    if not mountpoint: return

    full_cmdfile = FileLib.ogGetPath (file=f'{mountpoint}/windows/system32') + '/' + cmdfile
    if not full_cmdfile:
        ogInstallMiniSetup (disk, par, cmdfile)
        SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{mountpoint}/windows/system32/{cmdfile}')
        return

    # Concatenamos el comando en el fichero de comandos
    with open (full_cmdfile, 'a') as fd:
        fd.write (cmd + '\n')


#/**
#         ogDomainScript int_ndisk int_npartition str_domain str_user str_password
#@brief   Crea un script .vbs para unir al dominio una maquina windows y el comando adequado en el archivo cmd creado por ogInstallMiniSetup
#@param   int_ndisk         nº de orden del disco
#@param   int_npartition    nº de orden de la partición
#@param   str_filename      nombre del fichero cmd donde deberemos introducir el comando de ejecutar el script vbs
#@param   str_domain        dominio donde se quiere conectar
#@param   str_user          usuario con privilegios para unir al dominio
#@param   str_password      password del usuario con privilegios
#@return
#@exception OG_ERR_FORMAT    Formato incorrecto.
#*/ ##


### PRUEBAS.

#/**
#         ogConfigureOgagent int_ndisk int_filesys
#@brief   Modifica el fichero de configuración del nuevo agente OGAent para sistemas operativos.
#@param   int_ndisk    nº de orden del disco
#@param   int_filesys  nº de orden del sistema de archivos
#@return  (nada)
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@exception OG_ERR_NOTFOUND  Fichero o dispositivo no encontrado.
#@exception OG_ERR_LOCKED    Sistema de archivos bloqueado.
#*/ ##
def ogConfigureOgagent (disk, par, imgname=''):
    mntdir = FileSystemLib.ogMount (disk, par)
    if not mntdir:
        return

    for agentdir in ['usr/share/OGAgent', 'Program Files/OGAgent', 'Program Files (x86)/OGAgent', 'Applications/OGAgent.app']:
        cfgfile = FileLib.ogGetPath (file=f'{mntdir}/{agentdir}/cfg/ogagent.cfg')
        if cfgfile: break

    if not cfgfile:
        SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'ogagent.cfg')
        return

    ogcore_scheme = 'https'
    ogcore_ip     = NetLib.ogGetServerIp()
    ogcore_port   = NetLib.ogGetServerPort()
    cfgfile_patched = cfgfile + '.patched'
    cur_section = ''
    with open (cfgfile, 'r') as fdin:
        with open (cfgfile_patched, 'w') as fdout:
            while True:
                lineout = linein = fdin.readline()
                if not linein: break
                m = re.match (r'\[(.*)] *$', linein)

                if m:
                    cur_section = m.groups()[0]
                else:
                    if 'opengnsys' == cur_section:
                        if re.match (r'remote *=', linein):
                            lineout = f'remote={ogcore_scheme}://{ogcore_ip}:{ogcore_port}/opengnsys/rest/\n'
                        elif re.match ('imgname *=', linein):
                            lineout = f'imgname={imgname}\n'
                fdout.write (lineout)
    os.rename (cfgfile_patched, cfgfile)


#/**
#         ogInstallLaunchDaemon int_ndisk int_nfilesys str_filename
#@brief   Instala archivo que se ejecutará en el arranque de macOS.
#@param   int_ndisk         nº de orden del disco
#@param   int_nfilesys      nº de orden del sistema de archivos
#@param   str_filename      nombre del script
#return   (nada)
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@exception OG_ERR_NOTFOUND  Fichero o directorio no encontrado.
#@npte    Crea ficheros de configuración /Library/LaunchDaemon/es.opengnsys.Script.plist.
#*/ ##


### PRUEBAS.

#/**
#         ogAddToLaunchDaemon int_ndisk int_nfilesys str_filename str_commands
#@brief   Añade comandos al script creado por ogInstalLaunchDaemon.
#@param   int_ndisk         nº de orden del disco
#@param   int_nfilesys      nº de orden del sistema de archivos
#@param   str_filename      nombre del script (siempre se guardará en /usr/share para que sea visible por el sistema
#@param   str_commands      comando o comandos que se añadiran al fichero
#return   (nada)
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@exception OG_ERR_NOTFOUND  Fichero o directorio no encontrado.
#*/ ##


#/**
#         ogUninstallLinuxClient int_ndisk int_filesys
#@brief   Desinstala el cliente OpenGnSys para sistemas operativos GNU/Linux.
#@param   int_ndisk    nº de orden del disco
#@param   int_filesys  nº de orden del sistema de archivos
#@return  (nada)
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@exception OG_ERR_PARTITION Paritición o sistema de archivos incorrectos.
#@exception OG_ERR_LOCKED    Sistema de archivos bloqueado.
#*/ ##
def ogUninstallLinuxClient (disk, par):
    mntdir = FileSystemLib.ogMount (disk, par)
    if not mntdir:
        return

    for f in [
        f'{mntdir}/usr/sbin/ogAdmLnxClient',
        f'{mntdir}/sbin/ogAdmLnxClient',
        f'{mntdir}/usr/local/sbin/ogAdmLnxClient',
        f'{mntdir}/etc/ogAdmLnxClient.cfg',
        f'{mntdir}/usr/local/etc/ogAdmLnxClient.cfg',
    ]:
        try: os.remove (f)
        except: pass

    for f in [
        f'{mntdir}/etc/rc.local',
        f'{mntdir}/etc/rc.d/rc.local',
        f'{mntdir}/usr/local/etc/rc.local',
        f'{mntdir}/usr/local/etc/rc.d/rc.local',
    ]:
        subprocess.run (['sed', '-i', '-e', '/ogAdmLnxClient/ d', f], stderr=subprocess.DEVNULL)


#/**
#         ogUninstallWindowsClient int_ndisk int_filesys str_filename
#@brief   Desinstala el cliente OpenGnSys para sistemas operativos Windows.
#@param   int_ndisk         nº de orden del disco
#@param   int_npartition    nº de orden de la partición
#@param   str_filename      nombre del fichero cmd donde deberemos introducir el comando de ejecutar el script vbs
#@return  (nada)
#@exception OG_ERR_FORMAT    Formato incorrecto.
#@exception OG_ERR_PARTITION Paritición o sistema de archivos incorrectos.
#@exception OG_ERR_LOCKED    Sistema de archivos bloqueado.
#*/ ##
#ogUninstallWindowsClient ('1', '1', 'filename.cmd')
def ogUninstallWindowsClient (disk, par, cmdfile):
    mntdir = FileSystemLib.ogMount (disk, par)
    if not mntdir: return

    exe1 = FileLib.ogGetPath (file=f'{mntdir}/windows/ogAdmWinClient.exe')
    exe2 = FileLib.ogGetPath (file=f'{mntdir}/winnt/ogAdmWinClient.exe')
    if exe1 or exe2:
        ogAddCmd (disk, par, cmdfile, 'ogAdmWinClient -remove')
        ogAddCmd (disk, par, cmdfile, r'DEL C:\Windows\ogAdmWinClient.exe')
        ogAddCmd (disk, par, cmdfile, r'DEL C:\Winnt\ogAdmWinClient.exe')
