#!/usr/bin python3
# *-* coding: utf-8 *-*


from typing import Dict, Any, TYPE_CHECKING
import logging
import sys
import os
import json
import platform

if TYPE_CHECKING:
    import fernet
    import requests


__VERSION__ = "20251129 Python %s" % (platform.python_version())
_enable_debug:bool = False
_pid: int = os.getpid()
_main_conn = None


class ServidorMailClass(object):
    sqlserver = None
    sqlport = None
    sqluser = None
    sqlpassword = None
    sqldbname = None

    mailuser = None
    mailpassword = None
    mailserver = None
    starttls = False
    mailsender = None


class MensajeClass:
    def __init__(self):
        self.msgtexto = None
        self.recipients = []
        self.recipients_cc = []
        self.recipients_bcc = []
        self.codcliente = None
        self.asunto = None
        self.lista_recibos = []


def main():
    if len(sys.argv) == 1:
        sys.exit("No se han indicado parámetros")

    tipo = sys.argv[1]
    ok = True
    
    try:
        if tipo == "firma_pdf":
            ok = firma_pdf(*sys.argv[2:])
        elif tipo == "cliente_web":
            ok = cliente_web()
        elif tipo == "envio_mail":
            ok = envio_mail()
        elif tipo == "cliente_web_certificado":
            ok = cliente_web_certificado()
        elif tipo == "sftp":
            ok = conexion_sftp()
        elif tipo == "qrcode":
            ok = genera_qr()
        elif tipo == "url_to_file":
            ok = url_to_file()
        elif tipo == "md5":
            ok = resolve_md5()
        elif tipo == "scanner":
            
            ok = scanner()
        elif tipo == "ofuscador":
            ok = ofuscar()
        elif tipo == "uuid":
            ok = uuid_gen()
        elif tipo == "timestamp":
            ok = genera_timestamp()
        elif tipo == "version":
            print("Yeboyebo AQ Extensión (%s)" % __VERSION__)

        elif not tipo or tipo == "help":
            msg = (
                "Tipo de ejecución desconocido: "
                + tipo
                + "\n\
            Posibles tipos: \n\
             - cliente_web\n\
             - cliente_web_certificado\n\
             - envio_mail\n\
             - firma_pdf\n\
             - md5\n\
             - qrcode\n\
             - scanner\n\
             - sftp\n\
             - url_to_file\n\
             - ofuscador\n\
             - timestamp\n\
             - uuid\n\
             - version\n\
            \n\n\
            Carpeta temporal : %s\n" % tempfile.gettempdir()
            )
            raise Exception(msg)

        if not ok:
            sys.exit(1)

    except Exception as e:
        sys.exit(e)

def uuid_gen():

    import uuid

    if len(sys.argv) < 3:
        raise Exception("uuid4: no ha indicado la acción")

    mode = sys.argv[2]
    action = sys.argv[3]


    if mode == "uuid4":
        if action == "generate":
            print(uuid.uuid4())
            return True

    elif mode == "check4":
        uuid_to_check = action
        version = uuid.UUID(uuid_to_check).version or 0
        print(version)
        return version == 4       

    print("uuid4: modo: %s acción %s desconocida. Disponibles:" % (mode))
    print("\t- uuid4 generate. Genera un UUID4")
    print("\t- check4 uuid. Comprueba si es un UUID4 válido")

def genera_timestamp():
    import datetime

    candidate = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
    if len(sys.argv) > 2 and sys.argv[2] != "utc":
        candidate = sys.argv[2]
    try:
        date = datetime.datetime.strptime(candidate, "%Y-%m-%dT%H:%M:%S")
        with_tz = True
        for value in sys.argv:
            if value == "utc":
                with_tz = False
                break

        print(date.astimezone().isoformat() if with_tz else date.strftime("%Y-%m-%d %H:%M:%S"))
        return True

    except Exception:
        print("Error: La fecha %s no es válida." % (candidate))
        return False


def ofuscar():

    import base64

    if len(sys.argv) < 3:
        raise Exception("ofuscar: no ha indicado la acción")
    
    if len(sys.argv) < 5:
        raise Exception("No se ha especificado el texto , o la clave")

    accion = sys.argv[2]
    key = base64.b64decode(sys.argv[3]).decode() #en base64 siempre
    texto = sys.argv[4]

    class_cifrado = Cifrado(key)

    if accion == "ofuscar":
        texto =base64.b64decode(texto).decode("utf-8")
        #print("ofuscar: %s, key: %s" % (texto, key))
        print(class_cifrado.ofuscar(texto))
        return True
    elif accion == "desofuscar":
        #print("desofuscar: %s, key:%s" % (texto, key))
        print(class_cifrado.resolver(texto))
        return True
    else:
        print("ofuscar: acción %s desconocida. Disponibles:" % accion)
        print("\t- ofuscar. Ofusca un texto")
        print("\t- desofuscar. Desofusca un texto ofuscado")
    
    return False

def ofuscar_texto():
    if len(sys.argv) < 4:
        raise Exception("ofuscar: no ha indicado el texto a ofuscar")

    texto = sys.argv[3]


    

def scanner():
    """
    scanner:
    scanner url source dpi mode output_file
    """
    import pyinsane2

    if len(sys.argv) < 3:
        raise Exception("scanner: no ha indicado la acción")

    logger = logging.getLogger()
    sh = logging.StreamHandler()
    formatter= logging.Formatter(
            '%(levelname)-6s %(name)-30s %(message)s' 
        )
    sh.setFormatter(formatter)
    logger.setLevel(logging.ERROR)
    logger.addHandler(sh)
    
    accion = sys.argv[2]

    if accion == "list":
        return list_scanners()
    elif accion == "adquire":
        return adquire_scanner()
    elif accion == "sane_daemon":
        return scanner_sane_daemon()
    else:
        print("scanner: acción %s desconocida. Disponibles:" % accion)
        print("\t- list. Devuelve una lista con los escaners disponibles en el sistema")
        print("\t- adquire. Adquire un escaner y lo guarda en un archivo. url, source(default:flatbed), dpi(default:200), mode(default:bw), output_file(default:$TMPDIR/document.pdf)")

def scanner_sane_daemon():
    import pyinsane2.sane.daemon as sane_daemon
    sane_daemon.main_loop(sys.argv[3], sys.argv[4:6])
    return True
    

def list_scanners():
    pyinsane2.init()
    devices = pyinsane2.get_devices()
    for device in devices:
        print(device.name)
    pyinsane2.exit()
    return True


def adquire_scanner():
    
    import tempfile
    
    if len(sys.argv) < 4:
        raise Exception("scanner: Debe indicar la ruta del scanner.")
    
    url = sys.argv[3]
    source = sys.argv[4] if len(sys.argv) > 4 else "flatbed" # flatbed, ADF
    dpi = sys.argv[5] if len(sys.argv) > 5 else 200 # 200
    mode = sys.argv[6] if len(sys.argv) > 6 else "Gray" # "Color", "Gray", "Lineart"
    output_file = sys.argv[7] if len(sys.argv) > 7 else os.path.join(tempfile.gettempdir(), "document.pdf") # default: /tmp/document.pdf

    pyinsane2.init()

    devices = pyinsane2.get_devices()
    device = None
    for device_ in devices:
        if device_.name == url:
            # print("Found scanner %s.Continue." % device_.name)
            device = device_
            break    
    
    if not device:
        raise Exception("No %s scanner found." % (url))
    
    pyinsane2.set_scanner_opt(device, 'source', [source])
    pyinsane2.set_scanner_opt(device, 'resolution', [dpi])
    pyinsane2.set_scanner_opt(device, 'mode', [mode])
    pyinsane2.maximize_scan_area(device)

    tmp_folder = tempfile.gettempdir()
    tmp_file_prefijo = os.path.join(tmp_folder, "scanner")
    result_ok = True
    try:
        print("Scanning ...")
        scan_session = device.scan(multiple=True)
        images_list = []
        while True:
            try:
                scan_session.scan.read()
            except EOFError:
                print("Got page %d." % (len(scan_session.images)))
                img = scan_session.images[-1]
                file_name = "%s_%d.jpeg" % (tmp_file_prefijo, len(scan_session.images))
                images_list.append(file_name)
                img.save(file_name, "jpeg")
    except StopIteration:
        
        if scan_session.images:
            print("%d pages found." % len(scan_session.images))
            img = scan_session.images[0]
            img.save(output_file, "PDF", resolution=100.0, save_all=True, append_images=images_list[1:])
            print("Saved to %s file." % output_file)
            print("Done")
        else:
            print("No pages") 
            result_ok = False
        

    pyinsane2.exit()
    return result_ok



def resolve_md5():
    import hashlib
    
    if len(sys.argv) < 3:
        raise Exception(
            "resolve_md5: Debe indicar un segundo parámetro con el nombre del fichero"
        )

    file_name_or_value = sys.argv[2]
    if os.path.exists(file_name_or_value):
        with open(file_name_or_value, "rb") as f:
            value = hashlib.md5(f.read()).hexdigest()
    else:
        value = hashlib.md5(file_name_or_value.encode()).hexdigest()

    if len(sys.argv) == 4:
        with open(sys.argv[3], "w", encoding="iso-8859-15") as f:
            f.write(value)
    else:
        print(value)
    return True


def write_to_debug(text, forced = False):
    global _enable_debug, _pid

    import tempfile
    import datetime

    if _enable_debug or forced:
        now = datetime.datetime.now()
        current_timestamp = now.strftime("%m/%d/%Y, %H:%M:%S.%f")
        text = "%s : %d - %s" % (current_timestamp, _pid, text)
        temp_file_name = os.path.join(tempfile.gettempdir(), "aqextension_debug.txt")
        try:
            with open(temp_file_name, "a") as f:
                f.write(text + "\n")
        except Exception as e:
            print("Error escribiendo en fichero de depuración %s: %s" % (temp_file_name, e))


def cliente_web() -> bool:
    global _enable_debug, _pid

    import uuid
    import tempfile
    import time

    if len(sys.argv) != 3:
        raise Exception(
            "cliente_web: Debe indicar un segundo parámetro con el tipo de conexión y parámetros de llamada"
        )

    fichero_ordenes = sys.argv[2]
    result = None
    data: str = ""

    while True:
        _tiempo_ini = time.time()
        with open(fichero_ordenes, "r", encoding="ISO-8859-15") as data_file:
            params = json.loads(data_file.read(), strict=False)

        if not "tipo_payload" in params.keys():
            params["tipo_payload"] = "JSON"
        
        if not "return_response" in params.keys():
            params["return_response"] = False
        
        if params["tipo_payload"].upper() == "BINARY":
            params["return_response"] = True

        use_pipe = params["prefix_pipe"] if "prefix_pipe" in params else None

        if "enable_debug" in params:
            _enable_debug = params["enable_debug"]
            if _enable_debug:
                write_to_debug("DEBUG ACTIVADO")

        if "fentrada" in params:
            with open(params["fentrada"], "r") as fichero_entrada:
                entrada = fichero_entrada.read()
        else:
            entrada = ""

        result = llama_webservice(params, entrada)

        use_encode = (
            params["codificacion"] if "codificacion" in params else "ISO-8859-15"
        )

        if "fsalida" in params:
            tmp_file = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
            
            if "only_key" in params:
                if not result or params["only_key"] not in result.keys():
                    raise Exception(
                        "%s no es una clave válida en el resultado: %s"
                        % (params["only_key"], result)
                    )

                data = result[params["only_key"]]
            else:
                if params["return_response"]:
                    with open(tmp_file, "wb") as fichero_salida:
                        fichero_salida.write(result.content)
                else:
                    try:
                        data = json.dumps(result, ensure_ascii=False)
                    except Exception:
                        data = json.dumps(result.json())

                    with open(tmp_file, "wb") as fichero_salida:
                        fichero_salida.write(data.encode(use_encode, errors='replace'))

            os.rename(tmp_file, params["fsalida"])

        elif "codificacion" in params:
            salida_codificada = result
            try:
                print(str(salida_codificada).encode(use_encode))
            except Exception as e:
                print(str(salida_codificada).encode("UTF-8"))
        else:
            print(str(result))

        salir = params["close_when_finish"] if "close_when_finish" in params else True
        if salir:
            write_to_debug("Saliendo")
            break

        fichero_ordenes = None
        t_end = time.time() + 10
        _tiempo_fin = time.time()
        write_to_debug("Tiempo de espera TOTAL: %s" % (_tiempo_fin - _tiempo_ini))
        write_to_debug("Esperando nueva llamada use_pipe: %s" % (use_pipe))

        file_name_pipe = None
        if use_pipe:
            file_name_pipe = os.path.join(tempfile.gettempdir(), "%s" % (use_pipe))
            write_to_debug("Usando pipe %s" % file_name_pipe)

        while time.time() < t_end and not fichero_ordenes:
            try:
                texto_recibido = ""

                if use_pipe:

                    if os.path.exists(file_name_pipe) and os.path.getsize(file_name_pipe) > 0:
                        with open(file_name_pipe, "r") as f:
                            texto_recibido = f.read()
                        write_to_debug("He leido '%s' desde pipe %s" % (texto_recibido, file_name_pipe))
                        while True:
                            try:
                                if os.path.exists(file_name_pipe):
                                    write_to_debug("Borrando %s" % file_name_pipe)                            
                                    os.remove(file_name_pipe)
                                break
                            except Exception:
                                write_to_debug("Error borrando %s: %s" % (file_name_pipe, e))
                                time.sleep(0.01)
                else:
                    texto_recibido = sys.stdin.readline().strip()
                    if not texto_recibido:
                        continue   
                intentos = 0
                if texto_recibido:
                    write_to_debug("He leido argo %s" % texto_recibido)
                    while True:
                        intentos += 1
                        if use_pipe:
                            if (intentos > 10):
                                write_to_debug("Demasiados intentos. Saliendo")
                                break
                        try:
                            if os.path.exists(texto_recibido):
                                write_to_debug(
                                    "Recibido %s. Procesando..." % texto_recibido
                                )
                                fichero_ordenes = texto_recibido
                        except Exception as e:
                            write_to_debug("Error %s" % e)
                            time.sleep(0.01)
                            write_to_debug("Reintentando ...")
                            continue
                        break
                    write_to_debug("Termino de leer texto recibido")
            except Exception as e:
                write_to_debug("Error %s" % e)


        
        write_to_debug(
            "Tiempo de espera entre llamadas: %s" % (time.time() - _tiempo_fin)
        )

    return False if not result else True


def carga_entrada(params: Dict[str, Any], entrada):
    params["formato"] = (
        params["formato"].upper() if "formato" in params.keys() else "JSON"
    )
    return json.loads(entrada) if params["formato"] == "JSON" else entrada


def llama_xmlrpc(params, entrada):
    import xmlrpc.client

    pEntrada = carga_entrada(params, entrada)
    url = params["url"]
    ws = params["ws"]
    lectorXmlRpc = xmlrpc.client.ServerProxy(url)
    web_service = lectorXmlRpc
    attrs = ws.split("/")

    for attr in attrs:
        web_service = getattr(web_service, attr)

    salida = web_service(pEntrada)
    return salida


def llama_test(params, entrada):
    pEntrada = carga_entrada(params, entrada)

    if params["formato"] == "JSON":
        pEntrada["resultado"] = "OK"
        salida = json.dumps(pEntrada)

    elif params["formato"] == "XML":
        salida = "<hola>%s</hola>" % pEntrada

    return salida


def llama_requests(
    params: Dict[str, Any], metodo: str, return_response=False, use_data_as_json=True
) -> Any:
    global _main_conn
    import time
    import requests

    args = {}
    result_text = False
    tiempo1 = time.time()

    

    if "tipo_payload" in params.keys():
        use_data_as_json = params["tipo_payload"].upper() == "JSON"
        result_text = params["tipo_payload"].upper() != "JSON"


    if "return_response" in params.keys():
        return_response = params["return_response"]

    for key in ("params", "headers", "data", "cert", "verify"):
        if key in params.keys():
            if key == "data" and use_data_as_json:
                args[key] = json.dumps(params[key])
            else:
                if key == "headers" and not isinstance(
                    params[key], dict
                ):  # headers tiene que ser un diccionario.
                    headers = params[key].split(";")
                    args[key] = {}
                    for header in headers:
                        split_header = header.split("=")
                        key_header = split_header[0].strip()
                        args[key][key_header] = (
                            "" if len(split_header) == 1 else split_header[1]
                        )
                else:
                    args[key] = params[key]

    if not "headers" in args.keys():
        args["headers"] = {}
    #args["headers"]["Connection"] = "close"

    tiempo2 = time.time()
    write_to_debug("Tiempo carga argumentos: %s" % (tiempo2 - tiempo1))

    if not _main_conn:
        _main_conn = requests.Session()
        if os.name == "nt":
            _main_conn.trust_env = False
            _main_conn.verify = False


        # lanzar consulta:
    result : 'requests.Response' = getattr(_main_conn, metodo.lower())(params["url"], **args)
    tiempo3 = time.time()
    write_to_debug("Tiempo llamada: %s" % (tiempo3 - tiempo2))

    if return_response:
        return result

    if result.status_code != 200:
        raise Exception(result.text)

    return result.text if result_text else result.json()


def llama_webservice(params, entrada):
    if "metodo" in params:
        metodo = params["metodo"].upper()
    else:
        metodo = "REST"

    if metodo == "XMLRPC":
        salida = llama_xmlrpc(params, entrada)

    elif metodo == "TEST":
        salida = llama_test(params, entrada)

    elif metodo in ("GET", "POST", "PATCH", "DELETE", "PUT"):
        salida = llama_requests(params, metodo)

    else:
        salida = "El método de llamada %s no está implementado" % metodo

    return salida


def firma_pdf(
    certificado,
    password,
    fichpdf,
    fichsignedpdf,
    logosign=False,
    coordenadas=[],
    paginafirma=0,
):
    try:
        from cryptography.hazmat.primitives.serialization import pkcs12
        from cryptography.hazmat import backends
        from endesive import pdf
        import datetime

        date = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=2)
        date = date.strftime("D:%Y%m%d%H%M%S+00'00'")
        coordenadas = (
            (470, 0, 570, 100)
            if not coordenadas
            else tuple([int(i) for i in coordenadas.split("-")])
        )
        if logosign and not os.path.exists(logosign):
            raise Exception("No se encuentra %s" % logosign)

        dct = {
            "aligned": 0,
            "sigflags": 3,
            "sigpage": int(paginafirma),
            "auto_sigfield": True,
            # "sigandcertify": False,
            "signaturebox": coordenadas,
            "signform": False,
            "sigfield": "Signature",
            "signature_img_distort": False,  # default True
            "signature_img_centred": False,  # default True
            "contact": "mail@yeboyebo.es",
            "location": "Almansa",
            "signingdate": date.encode(),
            "reason": "Documento firmado digitalmente",
        }

        if not logosign:
            dct["signature"] = (
                "Documento firmado digitalmente"  # Si se especifica iamgen , no se muestra.
            )
        else:
            dct["signature_img"] = logosign

        with open(certificado, "rb") as fp:
            p12 = pkcs12.load_key_and_certificates(
                fp.read(), bytes(password, encoding="utf-8"), backends.default_backend()
            )

        with open(fichpdf, "rb") as fp:
            data_unsigned = fp.read()

        data_signed = pdf.cms.sign(data_unsigned, dct, p12[0], p12[1], p12[2], "sha256")

        with open(fichsignedpdf, "wb") as fp:
            fp.write(data_unsigned)
            fp.write(data_signed)

    except Exception as e:
        print("Error fima_pdf ", e)
        return False

    return True


def envio_mail() -> bool:
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.application import MIMEApplication
    from email.mime.image import MIMEImage
    from email.utils import formatdate

    # ENVIO_MAIL
    import smtplib
    import imaplib
    import time

    try:
        mensaje = MensajeClass()

        file_name = (
            "correo.txt"
            if len(sys.argv) <= 12 or sys.argv[12] == "AAAAAA"
            else sys.argv[12]
        )  # Si no existe sys.argv[12] usamos correo.txt
        if os.path.exists(file_name):
            infile = open(file_name, "r", encoding="ISO-8859-15", errors="replace")
            mensaje.asunto = infile.readline()
            mensaje.msgtexto = infile.read()
            infile.close()
        else:
            raise Exception("No existe el fichero %s" % (file_name))

        mensaje.recipients = sys.argv[2].split(",")

        if len(sys.argv) > 15 and sys.argv[15] != "AAAAAA":
            mensaje.recipients_cc = sys.argv[15].split(",")

        if len(sys.argv) > 16 and sys.argv[16] != "AAAAAA":
            mensaje.recipients_bcc = sys.argv[16].split(",")

        config = ServidorMailClass()
        config.mailuser = sys.argv[3]
        config.mailpassword = sys.argv[4]
        config.mailserver = sys.argv[5] + ":" + sys.argv[6]
        config.starttls = (
            resolve_bool(sys.argv[17])
            if len(sys.argv) > 17 and sys.argv[17] != "AAAAAA"
            else True
        )
        config.mailsender = sys.argv[7]
        outer = MIMEMultipart()
        outer["From"] = config.mailsender
        outer["To"] = ", ".join(mensaje.recipients)
        outer["Cc"] = ", ".join(mensaje.recipients_cc) if mensaje.recipients_cc else ""
        outer["BCC"] = (
            ", ".join(mensaje.recipients_bcc) if mensaje.recipients_bcc else ""
        )
        outer["Subject"] = mensaje.asunto
        
        outer["Date"] = formatdate(localtime=True)

        outer.preamble = "You will not see this in a MIME-aware mail reader.\n"
        # outer.add_header("Content-Type", "text/html")
        anchoImagenFirma = sys.argv[10]
        altoImagenFirma = sys.argv[11]

        outer.attach(
            MIMEText(
                mensaje.msgtexto,
                "html" if mensaje.msgtexto.strip().startswith("<html>") else "plain",
                "utf-8",
            )
        )

        if anchoImagenFirma != "AAAAAA" and altoImagenFirma != "AAAAAA":
            img_firma = (
                '<img src="cid:image" width ="'
                + anchoImagenFirma
                + '" height = "'
                + altoImagenFirma
                + '">'
            )
        else:
            img_firma = '<img src="cid:image">'

        outer.attach(MIMEText(img_firma, "html", "utf-8"))

        f = sys.argv[8]
        if f != "AAAAAA":
            f = f.replace("AAAAAA", " ")
            ff = f.split("|||")
            for fi in ff:
                try:
                    with open(fi, "rb") as fil:
                        part = MIMEApplication(fil.read(), Name=os.path.basename(fi))
                        part["Content-Disposition"] = (
                            'attachment; filename="%s"' % os.path.basename(fi)
                        )
                        outer.attach(part)
                except IOError:
                    print("Error al adjuntar el fichero." + fi)
                    return False

        imagenfirma = sys.argv[9]
        if imagenfirma != "AAAAAA":
            imagenfirma = imagenfirma.replace("AAAAAA", " ")
            # añdimos la imagen
            fp = open(imagenfirma, "rb")
            part3 = MIMEImage(fp.read())
            fp.close()

            part3.add_header("Content-ID", "<image>")
            outer.attach(part3)

        # Now send or store the message
        composed = outer.as_string()
        s = smtplib.SMTP(config.mailserver)
        # with smtplib.SMTP(config.mailserver) as s:
        if config.starttls:
            s.starttls()

        if config.mailuser and config.mailpassword:
            s.login(config.mailuser, config.mailpassword)

        lista_emails = mensaje.recipients + mensaje.recipients_cc + mensaje.recipients_bcc
        s.sendmail(config.mailsender, lista_emails, composed)
        s.quit()

        imap_port = (
            None if len(sys.argv) < 14 or sys.argv[13] == "AAAAAA" else sys.argv[13]
        )
        imap_url = (
            None if len(sys.argv) < 15 or sys.argv[14] == "AAAAAA" else sys.argv[14]
        )

        if imap_port and config.mailuser and config.mailpassword:
            try:
                imap = imaplib.IMAP4_SSL(imap_url, imap_port)
                imap.login(config.mailuser, config.mailpassword)
                imap.append(
                    "Sent",
                    "\\Seen",
                    imaplib.Time2Internaldate(time.time()),
                    composed.encode("utf8"),
                )
                imap.logout()
            except Exception as error:
                print(
                    "ERROR - Error creando copia en %s:%s.Sent: %s"
                    % (imap_url, imap_port, str(error))
                )

        return True
    except smtplib.SMTPConnectError:
        print("ERROR - No se puede conectar al servidor SMTP.")
        return False
    except smtplib.SMTPAuthenticationError:
        print("ERROR - Error de autenticación SMTP.")
        return False
    except smtplib.SMTPSenderRefused:
        print("ERROR - Dirección del remitente rechazada.")
        return False
    except smtplib.SMTPRecipientsRefused:
        print("ERROR - Todas las direcciones de destinatarios se rechazaron.")
        return False
    except smtplib.SMTPServerDisconnected:
        print("ERROR - El servidor se desconecta inesperadamente.")
        return False
    except smtplib.socket.gaierror:
        print(
            "ERROR - Servidor SMTP no encontrado.Verifique el nombre de host de su servidor SMTP."
        )
        return False
    except Exception as e:
        print("Error sending mail ", e)
        return False


def cliente_web_certificado() -> bool:

    import gzip

    tipo = sys.argv[2]

    headers = {}
    body_data = ""

    try:
        if tipo == "batuz":
            if len(sys.argv) != 9:
                print("Invalid arguments size")
                return False

            json_file = os.path.abspath(sys.argv[7])
            if not os.path.exists(json_file):
                print("File not found", json_file)
                return False

            with open(json_file) as file_data:
                json_data = json.load(file_data)

            body_file = os.path.abspath(sys.argv[6])
            if not os.path.exists(body_file):
                print("File not found", body_file)
                return False

            file_ = open(body_file, "r", encoding="UTF-8")
            body_data = gzip.compress(file_.read().encode(), 4)
            file_.close()

            headers = {
                "Accept-Encoding": "gzip",
                "Content-Encoding": "gzip",
                "Content-Type": "application/octet-stream",
                "eus-bizkaia-n3-version": "1.0",
                "eus-bizkaia-n3-content-type": "application/xml",
                "eus-bizkaia-n3-data": json.dumps(json_data).encode(),
                "Content-Length": str(len(body_data)),
            }

        else:
            print("Unknown client", tipo)
            return False

        pem_file = pfx_to_pem(sys.argv[4], sys.argv[5])
        url = sys.argv[3]

        data = {
            "url": url,
            "data": body_data,
            "headers": headers,
            "cert": pem_file,
            "verify": True,
        }
        response = llama_requests(data, "POST", True, False)

        result = str(response.headers) if tipo == "batuz" else response.text
        result_file = os.path.abspath(sys.argv[8])

        file_header = open(result_file, "wb")
        file_header.write(result.encode())
        file_header.close()

        if tipo == "batuz":
            file_result_xml = open("%s.response_content.xml" % result_file, "wb")
            file_result_xml.write(response.content or b"")
            file_result_xml.close()

    except Exception as error:
        print("Error cliente_web_certificado", error)
        return False

    return True


# https://gist.github.com/erikbern/756b1d8df2d1487497d29b90e81f8068#gistcomment-3743732
def pfx_to_pem(pfx_path, pfx_pass):
    """Decrypts the .pfx file to be used with requests."""

    import tempfile
    from cryptography.hazmat import backends
    from cryptography.hazmat.primitives.serialization import (
    Encoding,
    PrivateFormat,
    NoEncryption,
    pkcs12
)

    t_pem = tempfile.NamedTemporaryFile(suffix=".pem", delete=False)
    pem_name = t_pem.name
    f_pem = open(pem_name, "wb")
    pfx = open(pfx_path, "rb").read()

    private_key, main_cert, add_certs = pkcs12.load_key_and_certificates(
        pfx, pfx_pass.encode(), backends.default_backend()
    )
    f_pem.write(
        private_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption())
    )
    f_pem.write(main_cert.public_bytes(Encoding.PEM))

    for ca in add_certs:
        f_pem.write(ca.public_bytes(Encoding.PEM))
    f_pem.close()

    return pem_name


def conexion_sftp() -> bool:
    """Establece una conexión sftp."""

    import pysftp

    username = sys.argv[2]
    password = sys.argv[3]
    hostname = sys.argv[4]
    modo = sys.argv[5]
    puerto = int(sys.argv[6])
    debug = resolve_bool(sys.argv[7] if len(sys.argv) > 7 else "")

    try:
        cnopts = pysftp.CnOpts()
        cnopts.hostkeys = None
        with pysftp.Connection(
            host=hostname,
            username=username,
            password=password,
            port=puerto,
            cnopts=cnopts,
        ) as sftp:
            if modo in ("get_dir", "put_dir"):
                if debug:
                    print("conexion_sftp. mode: %s" % (modo))
                local_dir = sys.argv[7]
                remote_dir = sys.argv[8]
                if modo == "get_dir":
                    sftp.get_d(remote_dir, local_dir)  # remoto -> local
                else:
                    # Check if local_dir is a file
                    if os.path.isfile(local_dir):
                        if debug:
                            print("conexion_sftp. local_dir is a file")
                            print("conexion_sftp. change remote_dir: %s" % (remote_dir))
                        sftp.chdir(remote_dir)
                        if debug:
                            print("conexion_sftp. sending file: %s" % (local_dir))
                        sftp.put(local_dir)
                    else:
                        if debug:
                            print("conexion_sftp. local_dir is a directory")
                        sftp.put_d(local_dir, remote_dir)

                    sftp.cwd(remote_dir)
                    directory_structure = sftp.listdir_attr()  # local -> remoto
                    for attr in directory_structure:
                        print(attr.filename, attr)  # print de propiedades

        return True

    except Exception as error:
        print("Error sftp", error)
        return False

    return True


def resolve_bool(value):
    return str(value).lower() in ["true", "1", "s"]


def genera_qr() -> bool:
    """Genera qrcode y lo guarda como un fichero."""

    import qrcode

    ruta_img = sys.argv[2]
    qr_string = sys.argv[3].replace("þ", " ")
    received_params = json.loads(sys.argv[4]) if len(sys.argv) > 4 else {}

    # received_params opcional "version":9,"error_correction":"ERROR_CORRECT_M","box_size":2,"border":2}'

    params = {
        "version": 9,
        "error_correction": qrcode.constants.ERROR_CORRECT_M,
        "box_size": 2,
        "border": 2,
    }

    for key, value in received_params.items():
        if key not in params.keys():
            print("Error qrcode:El argumento %s no es válido" % (key))
            return False

        if key == "error_correction":
            value = getattr(qrcode.constants, value)
        params[key] = value

    try:
        qr = qrcode.QRCode(**params)
        qr.add_data(qr_string)
        qr.make(fit=True)
        img = qr.make_image(fill_color="black", back_color="white")
        img.save(ruta_img)
    except Exception as error:
        print("Error qrcode:" + error)
        return False

    return True


def url_to_file() -> bool:
    """Imprime una url en una impresora especificada."""

    if len(sys.argv) < 3:
        print("Error url_to_file: No se ha especificado url")
        return False

    url = sys.argv[2]
    folder = sys.argv[3] if len(sys.argv) > 3 else None
    file_name = _url_to_file(url, folder)

    if file_name:
        print(file_name)
        return True

    return False


def _url_to_file(url, folder=None):
    import uuid
    import tempfile
    import requests
    
    list_url = url.split("/")

    file_name = os.path.join(
        folder if folder else tempfile.gettempdir(),
        "%s_%s" % (str(uuid.uuid4()), list_url[-1]),
    )

    result = requests.get(url)

    if result.status_code != 200:
        print("ERROR: La url %s no es válida" % (url))
        return False

    file_ = open(file_name, "wb")
    file_.write(result.content)
    file_.close()

    return file_name

class Cifrado:

    def __init__(self, key: str = None):
        """Init."""

        self.fernet : "fernet.Fernet" = None
        self.key: str = key


    def ofuscar(self, texto: str) -> bool:
        """Save password."""

        import base64

        # save_password("password","CIF_EMPRESA")
        self.init_fernet()
        if len(texto) < 64:
            texto = texto.ljust(64, "\n")
            
        encoded_bytes = self.fernet.encrypt(texto.encode())
        return base64.b64encode(encoded_bytes).decode()

        

    def resolver(self, cipher_text: str) -> str:
        """Resuelve password."""

        import base64

        self.init_fernet()

        cipher_bytes = base64.b64decode(cipher_text.encode())
        text = self.fernet.decrypt(cipher_bytes).decode()
        text = text.rstrip("\n")
        return text
    
    def init_fernet(self) -> None:
        """Initialize fernet."""
        import hashlib
        import fernet
        import base64

        if not self.fernet:
            if not self.key:
                raise Exception("No se ha especificado key")
            key = self.key
            # salt = os.urandom(9)
            salt = b'\xa1\x8c\x8f\xdb\x8fi?_\xde'
            key_encoded = key.encode()

            hmac = hashlib.pbkdf2_hmac("sha256", key_encoded , salt, 10000)
            dict_passwd = {
                # Algorithm:
                # .. pbkdf2: short algorithm name
                # .. sha256: hash function used
                # .. 4: number of zeroes used on iterations
                "algorithm": "pbkdf2-sha256-4",
                "salt": base64.b64encode(salt).decode(),
                "hash": base64.b64encode(hmac).decode(),
            }
            hashed_password = "%(algorithm)s:%(salt)s:%(hash)s" % dict_passwd

            key_salt = hashlib.sha256(hashed_password.encode()).digest()
            new_key = hashlib.pbkdf2_hmac("sha256", key_encoded, key_salt, 10000)
            key64 = base64.urlsafe_b64encode(new_key)
            self.fernet = fernet.Fernet(key64)

if __name__ == "__main__":
    main()
