Files
MBS/PROYECTO/otrodir/apiclient.4gl
T

479 lines
16 KiB
Plaintext

OPTIONS SHORT CIRCUIT
IMPORT util
IMPORT com
FUNCTION apiclient(pemail)
DEFINE data_apilayer RECORD
email,did_you_mean,USER,domain,syntax_valid STRING,
is_disposable,is_role_account,is_catch_all,is_deliverable,can_connect_smtp,
is_inbox_full,is_disabled,mx_records,FREE,score BOOLEAN
END RECORD
DEFINE data RECORD
data RECORD
status ,
result,
deprecation_notice string,
score smallint,
email string,
regexp,
gibberish,
disposable,
webmail,
mx_records,
smtp_server,
smtp_check,
accept_all,
block BOOLEAN
END RECORD,
source DYNAMIC ARRAY OF STRING,
meta RECORD
param STRING
END RECORD
END RECORD
DEFINE data_ipq RECORD
MESSAGE STRING,
success,
valid,
disposable BOOLEAN,
smtp_score ,
overall_score DEC(10,2),
first_name string,
generic,
common,
dns_valid,
honeypot BOOLEAN,
deliverability STRING,
frequent_complainer BOOLEAN,
spam_trap_score STRING,
catch_all,
timed_out,
suspect,
recent_abuse BOOLEAN,
fraud_score DEC(10,2),
suggested_domain string,
leaked BOOLEAN
END RECORD
DEFINE estado,purl,cmd,res, msg,s,pemail,tiposervice STRING,
ret INT
DEFINE dominio, detalle, api_url, api_key STRING,
dns_estado SMALLINT,
hunter_ok BOOLEAN
# LA API PARA VERIFICAR ESTA OFFLINE 29/05/2025
{ LET estado = 'VALIDO'
LET ret = 200
RETURN estado,ret
}
# SE DEVUELVE SI EL EMAIL ESTA EN BLANCO
LET pemail = pemail.trim()
IF pemail IS NULL OR pemail.getLength() = 0 THEN
LET ret = 400
LET estado = 'DEBES DIGITAR EL EMAIL POR FAVOR'
RETURN estado,ret
END IF
DISPLAY "llego: ",pemail
# VALIDACION PROPIA 14/10/2026: FORMATO + DOMINIO EN DNS (NO DEPENDE DE LA API)
IF NOT email_formato_valido(pemail) THEN
INSERT INTO envia_email_log (email,estado,fecha,programa_envio,us_crea,score)
VALUES (pemail,'formato_invalido',getdate(),'mt101_local',suser_Sname(),0)
LET ret = 400
LET estado = 'EL FORMATO DEL EMAIL NO ES VALIDO'
RETURN estado,ret
END IF
LET dominio = pemail.subString(pemail.getIndexOf("@",1) + 1, pemail.getLength())
LET dns_estado = email_dominio_dns(dominio)
DISPLAY "dominio: ",dominio," dns: ",dns_estado
IF dns_estado = 0 THEN
INSERT INTO envia_email_log (email,estado,fecha,programa_envio,us_crea,score)
VALUES (pemail,'dominio_inexistente',getdate(),'mt101_dns',suser_Sname(),0)
LET ret = 400
LET estado = 'EL DOMINIO DEL EMAIL NO EXISTE O NO RECIBE CORREO'
RETURN estado,ret
END IF
# ELECCION DE SERVICIOS, POR EL MOMENTO ES IPQUALITY
# URL Y API KEY SE LEEN DE LA TABLA APIS (YA NO VAN EN EL FUENTE)
SELECT a.IDENTIFICACION, a.url, a.apikey INTO tiposervice, api_url, api_key FROM APIS a
WHERE a.estado = 'ONLINE'
IF STATUS = NOTFOUND THEN
RETURN estado,ret
END IF
LET tiposervice = tiposervice.trim()
LET api_url = api_url.trim()
LET api_key = api_key.trim()
# LET tiposervice = 'ipq1'
IF tiposervice = 'HUNTER' THEN
LET purl = api_url,"?email=",pemail CLIPPED,"&api_key=",api_key
CALL ipquality_send_command(purl, NULL) RETURNING ret,res
DISPLAY "response api: ",res," longitud: ",res.getLength()
# HUNTER PUEDE NO RESPONDER (CAIDO, 202 EN PROCESO, 222/451 SMTP):
# EN ESE CASO DECIDE LA VALIDACION DNS PROPIA
LET hunter_ok = FALSE
IF ret == 0 AND res.getLength() > 0 THEN
TRY
CALL util.JSON.parse(res,data)
LET hunter_ok = TRUE
CATCH
LET hunter_ok = FALSE
END TRY
END IF
IF NOT hunter_ok THEN
INSERT INTO envia_email_log (email,display_name,estado,fecha,programa_envio,us_crea,score)
VALUES (pemail,SFMT('hunter http=%1',ret),'hunter_sin_respuesta',getdate(),'mt101_dns',suser_Sname(),0)
CALL email_resultado_dns(dns_estado) RETURNING estado,ret
RETURN estado,ret
END IF
DISPLAY "DATA EMAIL: ",pemail CLIPPED, " score: ",data.data.score
LET detalle = SFMT('mx=%1 smtp=%2 check=%3 regexp=%4 disp=%5 dns=%6',
data.data.mx_records, data.data.smtp_server, data.data.smtp_check,
data.data.regexp, data.data.disposable, dns_estado)
INSERT INTO envia_email_log (email,display_name,estado,fecha,programa_envio,us_crea,score)
VALUES (pemail,detalle,data.data.status,getdate(),'mt101_hunter',suser_Sname(),data.data.score)
# DESECHABLES (MAILINATOR, ETC.) SE RECHAZAN SIEMPRE
IF data.data.disposable THEN
LET estado = 'NO SE PUEDE ENTREGAR MENSAJE A ESTA DIRECCION EMAIL'
LET ret = 400
RETURN estado,ret
END IF
IF (data.data.status = 'valid' OR data.data.status = 'accept_all' OR
data.data.status = 'unknown') AND data.data.score > 29 THEN
LET estado = 'VALIDO'
LET ret = 200
RETURN estado,ret
END IF
# HUNTER LO RECHAZA: SOLO SE RESPETA SI LLEGO AL SERVIDOR DE CORREO
# Y ESTE RECHAZO EL BUZON (EL BUZON NO EXISTE)
IF data.data.mx_records AND data.data.smtp_server AND NOT data.data.smtp_check THEN
LET estado = 'NO SE PUEDE ENTREGAR MENSAJE A ESTA DIRECCION EMAIL'
LET ret = 400
RETURN estado,ret
END IF
# HUNTER NO VE EL DOMINIO O NO PUDO CONECTAR (DOMINIOS .DO PEQUENOS):
# SI EL DNS CONFIRMA QUE EL DOMINIO RECIBE CORREO, SE ACEPTA
CALL email_resultado_dns(dns_estado) RETURNING estado,ret
IF ret = 200 THEN
INSERT INTO envia_email_log (email,display_name,estado,fecha,programa_envio,us_crea,score)
VALUES (pemail,detalle,'valid_dns',getdate(),'mt101_dns',suser_Sname(),0)
END IF
RETURN estado,ret
END IF
IF tiposervice = 'IPQUALITY' THEN
# EN APIS LA URL DE IPQUALITY YA TRAE LA KEY: .../email/<key>/
LET purl = api_url,pemail CLIPPED
CALL ipquality_send_command(purl, api_key) RETURNING ret,res
IF ret ==0 THEN
LET s = util.JSON.stringify(res)
CALL util.JSON.parse(res,data_ipq)
IF data_ipq.valid THEN
LET estado = 'VALIDO'
LET ret = 200
ELSE
LET ret = 400
LET estado = 'NO SE PUEDE ENTREGAR MENSAJE A ESTA DIRECCION EMAIL'
END IF
END IF
END IF
IF tiposervice = 'APILAYER' THEN
# DISPLAY " ESTOY EN APILAYER"
LET purl= api_url,pemail CLIPPED
CALL apilayer_send_command(purl, api_key) RETURNING ret, res
# DISPLAY "res ",res," ",ret
IF ret ==0 THEN
LET s = util.JSON.stringify(res)
CALL util.JSON.parse(res,data_apilayer)
IF data_apilayer.can_connect_smtp THEN
LET estado = 'VALIDO'
LET ret = 200
ELSE
LET ret = 400
LET estado = 'NO SE PUEDE ENTREGAR MENSAJE A ESTA DIRECCION EMAIL'
END IF
# DISPLAY "sintax ",data.syntax_valid," deliv ",data.is_deliverable
ELSE
IF ret = 429 THEN
LET ret = 200
LET estado ='VALIDO POR EXESO DE REQUEST'
ELSE
LET ret = 400
LET estado = 'NO FUE POSIBLE LA CONEXION, '
END IF
END IF
END IF
# LET ret = 200
DISPLAY "en return ",estado," ",ret
RETURN estado,ret
END FUNCTION
#
## Communications library using com (http or https)
#
FUNCTION apilayer_send_command(url, papikey)
DEFINE url, papikey STRING
DEFINE result, tmp STRING
DEFINE http_req com.HTTPRequest
DEFINE http_resp com.HTTPResponse
TRY
UPDATE apis SET cuenta = cuenta + 1 WHERE estado = 'ONLINE'
LET http_req = com.HTTPRequest.Create(url)
CALL http_req.setHeader("apikey",papikey)
CALL http_req.doRequest()
LET http_resp=http_req.getResponse()
IF http_resp.getStatusCode() != 200 THEN
RETURN http_resp.getStatusCode(), http_resp.getStatusDescription()
END IF
LET tmp = http_resp.getStatusDescription()
IF (tmp == "OK" || tmp == "no error" ) THEN
RETURN -1, SFMT("HTTP request status description: %1 ",tmp)
END IF
LET result = http_resp.getTextResponse()
CATCH
RETURN -2, SFMT("HTTP request error: STATUS=%1 (%2)",STATUS,SQLCA.sqlerrd[2])
END TRY
RETURN 0, result
END FUNCTION
FUNCTION ipquality_send_command(url, papikey)
DEFINE url, papikey STRING
DEFINE result, tmp STRING
DEFINE http_req com.HTTPRequest
DEFINE http_resp com.HTTPResponse
# NO SE MUESTRA LA URL: LLEVA LA API KEY
TRY
LET http_req = com.HTTPRequest.Create(url)
IF papikey IS NOT NULL THEN
CALL http_req.setHeader("apikey",papikey)
END IF
CALL http_req.doRequest()
LET http_resp=http_req.getResponse()
IF http_resp.getStatusCode() != 200 THEN
RETURN http_resp.getStatusCode(), http_resp.getStatusDescription()
END IF
LET tmp = http_resp.getStatusDescription()
IF (tmp == "OK" || tmp == "no error" ) THEN
RETURN -1, SFMT("HTTP request status description: %1 ",tmp)
END IF
LET result = http_resp.getTextResponse()
CATCH
RETURN -2, SFMT("HTTP request error: STATUS=%1 (%2)",STATUS,SQLCA.sqlerrd[2])
END TRY
RETURN 0, result
END FUNCTION
#
## VALIDACION PROPIA DEL EMAIL (14/10/2026)
## Formato basico + el dominio debe existir y recibir correo (registro MX, o A
## como MX implicito segun RFC 5321). Se consulta el DNS por HTTPS (dns.google)
## porque BDL no tiene resolvedor DNS.
#
FUNCTION email_formato_valido(pemail)
DEFINE pemail, usuario, dominio, etiqueta, c, permitidos STRING
DEFINE i, arroba, inicio INT
LET permitidos = "._%+-!#$&'*/=?^`{|}~"
IF pemail IS NULL OR pemail.getLength() > 254 THEN
RETURN FALSE
END IF
# UNA SOLA ARROBA, CON USUARIO Y DOMINIO
LET arroba = pemail.getIndexOf("@",1)
IF arroba <= 1 OR arroba = pemail.getLength() THEN
RETURN FALSE
END IF
IF pemail.getIndexOf("@",arroba + 1) > 0 THEN
RETURN FALSE
END IF
LET usuario = pemail.subString(1, arroba - 1)
LET dominio = pemail.subString(arroba + 1, pemail.getLength())
# USUARIO: MAX 64, SIN PUNTO AL INICIO/FINAL NI DOBLE PUNTO
IF usuario.getLength() > 64 THEN
RETURN FALSE
END IF
IF usuario.getCharAt(1) = "." OR usuario.getCharAt(usuario.getLength()) = "."
OR usuario.getIndexOf("..",1) > 0 THEN
RETURN FALSE
END IF
FOR i = 1 TO usuario.getLength()
LET c = usuario.getCharAt(i)
IF NOT (c MATCHES "[a-zA-Z0-9]" OR permitidos.getIndexOf(c,1) > 0) THEN
RETURN FALSE
END IF
END FOR
# DOMINIO: AL MENOS UN PUNTO, ETIQUETAS DE LETRAS/NUMEROS/GUION,
# SIN GUION AL INICIO/FINAL, Y EXTENSION (TLD) DE 2+ LETRAS
IF dominio.getLength() > 253 OR dominio.getIndexOf(".",1) = 0 THEN
RETURN FALSE
END IF
LET inicio = 1
FOR i = 1 TO dominio.getLength() + 1
IF i > dominio.getLength() OR dominio.getCharAt(i) = "." THEN
LET etiqueta = dominio.subString(inicio, i - 1)
IF etiqueta.getLength() = 0 OR etiqueta.getLength() > 63 THEN
RETURN FALSE
END IF
IF etiqueta.getCharAt(1) = "-" OR etiqueta.getCharAt(etiqueta.getLength()) = "-" THEN
RETURN FALSE
END IF
LET inicio = i + 1
ELSE
LET c = dominio.getCharAt(i)
IF NOT (c MATCHES "[a-zA-Z0-9]" OR c = "-") THEN
RETURN FALSE
END IF
END IF
END FOR
# ETIQUETA FINAL = TLD
IF etiqueta.getLength() < 2 THEN
RETURN FALSE
END IF
FOR i = 1 TO etiqueta.getLength()
IF NOT (etiqueta.getCharAt(i) MATCHES "[a-zA-Z]") THEN
RETURN FALSE
END IF
END FOR
RETURN TRUE
END FUNCTION
# 1 = EL DOMINIO RECIBE CORREO, 0 = NO EXISTE / SIN MX NI A, -1 = NO SE PUDO CONSULTAR
FUNCTION email_dominio_dns(pdominio)
DEFINE pdominio STRING
DEFINE r SMALLINT
LET r = email_dns_consulta(pdominio, "MX")
IF r <> 0 THEN
RETURN r
END IF
# SIN MX: EL REGISTRO A FUNCIONA COMO MX IMPLICITO
RETURN email_dns_consulta(pdominio, "A")
END FUNCTION
FUNCTION email_dns_consulta(pdominio, ptipo)
DEFINE pdominio, ptipo, res, dato STRING
DEFINE http_req com.HTTPRequest
DEFINE http_resp com.HTTPResponse
DEFINE obj, rr util.JSONObject
DEFINE answers util.JSONArray
DEFINE i, dns_status, tipo_rr, tipo_buscado INT
IF ptipo = "MX" THEN
LET tipo_buscado = 15
ELSE
LET tipo_buscado = 1
END IF
TRY
LET http_req = com.HTTPRequest.Create(
SFMT("https://dns.google/resolve?name=%1&type=%2", pdominio.toLowerCase(), ptipo))
CALL http_req.setTimeOut(10)
CALL http_req.setHeader("Accept","application/dns-json")
CALL http_req.doRequest()
LET http_resp = http_req.getResponse()
IF http_resp.getStatusCode() != 200 THEN
RETURN -1
END IF
LET res = http_resp.getTextResponse()
LET obj = util.JSONObject.parse(res)
LET dns_status = obj.get("Status")
CATCH
DISPLAY "email_dns_consulta error: ",STATUS
RETURN -1
END TRY
# 3 = NXDOMAIN (EL DOMINIO NO EXISTE); OTRO <> 0 = FALLA DEL DNS
IF dns_status = 3 THEN
RETURN 0
END IF
IF dns_status <> 0 THEN
RETURN -1
END IF
IF NOT obj.has("Answer") THEN
RETURN 0
END IF
IF obj.getType("Answer") <> "ARRAY" THEN
RETURN 0
END IF
LET answers = obj.get("Answer")
FOR i = 1 TO answers.getLength()
LET rr = answers.get(i)
LET tipo_rr = rr.get("type")
LET dato = rr.get("data")
IF tipo_rr = tipo_buscado THEN
# "0 ." ES UN NULL MX (RFC 7505): EL DOMINIO DECLARA QUE NO RECIBE CORREO
IF tipo_buscado = 15 AND dato.trim() MATCHES "* ." THEN
CONTINUE FOR
END IF
RETURN 1
END IF
END FOR
RETURN 0
END FUNCTION
FUNCTION email_resultado_dns(dns_estado)
DEFINE dns_estado SMALLINT
CASE dns_estado
WHEN 1
RETURN 'VALIDO', 200
WHEN 0
RETURN 'EL DOMINIO DEL EMAIL NO EXISTE O NO RECIBE CORREO', 400
OTHERWISE
RETURN 'NO FUE POSIBLE VERIFICAR EL EMAIL, INTENTE DE NUEVO', 400
END CASE
END FUNCTION