#!/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__ = "20261002 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":
            import tempfile

            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

    import html
    import smtplib
    import imaplib
    import socket
    import time

    msg_error = None
    try:
        mensaje = MensajeClass()

        file_name = (
            "correo.txt"
            if len(sys.argv) <= 12 or sys.argv[12] == "AAAAAA"
            else sys.argv[12]
        )

        if os.path.exists(file_name):
            with open(file_name, "r", encoding="ISO-8859-15", errors="replace") as infile:
                mensaje.asunto = infile.readline().strip()
                mensaje.msgtexto = infile.read()
        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("mixed")
        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"

        cuerpo_container = MIMEMultipart("related")
        outer.attach(cuerpo_container)

        anchoImagenFirma = sys.argv[10]
        altoImagenFirma = sys.argv[11]
        imagenfirma = sys.argv[9]

        texto_cuerpo = mensaje.msgtexto
        es_html = texto_cuerpo.strip().startswith("<html>")
        tiene_firma = (imagenfirma != "AAAAAA" and os.path.exists(imagenfirma.replace("AAAAAA", " ")))

        if tiene_firma:
            imagenfirma = imagenfirma.replace("AAAAAA", " ")

            if anchoImagenFirma != "AAAAAA" and altoImagenFirma != "AAAAAA":
                img_firma_tag = f'<img src="cid:image" width="{anchoImagenFirma}" height="{altoImagenFirma}">'
            else:
                img_firma_tag = '<img src="cid:image">'

            if not es_html:
                texto_cuerpo_html = html.escape(texto_cuerpo).replace("\n", "<br>")
                texto_final = f"<html><body>{texto_cuerpo_html}<br><br>{img_firma_tag}</body></html>"
            else:
                if "</body>" in texto_cuerpo:
                    texto_final = texto_cuerpo.replace("</body>", f"<br><br>{img_firma_tag}</body>")
                else:
                    texto_final = f"{texto_cuerpo}<br><br>{img_firma_tag}"

            cuerpo_container.attach(MIMEText(texto_final, "html", "utf-8"))

            try:
                with open(imagenfirma, "rb") as fp:
                    part3 = MIMEImage(fp.read())
                part3.add_header("Content-ID", "<image>")
                part3.add_header("Content-Disposition", "inline", filename=os.path.basename(imagenfirma))
                cuerpo_container.attach(part3)
            except IOError:
                print("Error al abrir el archivo de la firma: " + imagenfirma)
        else:
            cuerpo_container.attach(MIMEText(texto_cuerpo, "html" if es_html else "plain", "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

        composed = outer.as_string()
        timeout_smtp = float(sys.argv[18]) if len(sys.argv) > 18 and sys.argv[18] != "AAAAAA" else 60
        s = smtplib.SMTP(config.mailserver, timeout=timeout_smtp)

        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:
                msg_error = "ERROR - Error creando copia en %s:%s.Sent: %s" % (imap_url, imap_port, str(error))

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

    if msg_error:
        raise Exception(msg_error)

    return True



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()
