IMPORT util IMPORT com IMPORT os IMPORT security IMPORT XML CONSTANT dpy_verb=TRUE -- send messages to standard output TYPE reqInfoTyp RECORD method STRING, ctype STRING, # check the Content-Type informat STRING, # short word for Content Type caccept STRING, # check which format the client accepts outformat STRING, # short word for Accept path STRING, query STRING, # the query string items DYNAMIC ARRAY OF RECORD name STRING, value STRING END RECORD END RECORD DEFINE m_reqInfo reqInfoTyp DEFINE doc xml.DomDocument DEFINE url,msg,method,headers,mensaje,filexml,tipoecf,rnc_e,rnc_c,e_ncf,fileacuse,resp, ruta_firmados,fileafirmar_f STRING, tout, n, x,rcode INTEGER, start_time DATETIME YEAR TO SECOND DEFINE d0 DATETIME YEAR TO SECOND DEFINE a om.SaxAttributes DEFINE out om.SaxDocumentHandler, i INT, fechaFirma DATETIME YEAR TO SECOND MAIN DEFINE ret INTEGER, mensaje STRING DEFINE req com.HTTPServiceRequest, err RECORD code INT, DESC STRING END RECORD DEFER INTERRUPT LET filexml = ".\\ArchRecibidos\\" LET fileacuse = ".\\Acuses\\" LET ruta_firmados = fgl_getenv("RUTA_XML_FIRMADOS") # iNICIO SERVICIO SERVER DISPLAY "Iniciando server..." CALL com.WebServiceEngine.Start() DISPLAY "The server is listening." # Creacion del servidor WHILE TRUE TRY LET req = com.WebServiceEngine.getHTTPServiceRequest(30) # ... parse and process the request ... CALL getReqInfo(req) RETURNING method,url DISPLAY "metodo2 ",method," url ",url CASE method WHEN "GET" CALL req.sendTextResponse(200,"","API SERVER para los e-NCF") EXIT WHILE WHEN "POST" IF url = "http://ecf.fulldetord.com/gas/ws/r/fe/aprobacioncomercial/api/ecf" OR url = "https://ecf.fulldetord.com/gas/ws/r/fe/aprobacioncomercial/api/ecf" THEN LET ret = NULL DISPLAY "metodo2 ",method," ",url CALL aprobacion(req) RETURNING ret,mensaje IF ret <> 200 THEN LET ret = 400 # LET mensaje = "Invalido" CALL req.sendTextResponse(ret,"",mensaje) DISPLAY "enviar respuesta ",ret," ",url ELSE LET ret = 200 # LET mensaje="Valido" CALL req.sendTextResponse(ret,"",mensaje) END IF END IF EXIT CASE OTHERWISE LET err.code = -3 LET err.desc = mensaje CALL req.sendTextResponse(400,"OK","Unknown request:\n" || url || "\n" || method) END CASE { CATCH LET ret = status CASE ret WHEN -15565 DISPLAY "Server process has been stopped by Ctrl-C" WHEN -15575 DISPLAY "Server process has been disconnected by the GAS" OTHERWISE DISPLAY "[ERROR] " || ret EXIT WHILE END CASE IF int_flag<>0 THEN LET int_flag = 0 EXIT WHILE END IF } END TRY END WHILE END MAIN FUNCTION getReqInfo(req) DEFINE req com.HTTPServiceRequest DEFINE str, val base.StringTokenizer DEFINE token,mensaje STRING DEFINE i,rcode INT INITIALIZE m_reqInfo TO NULL LET m_reqInfo.ctype = getHeaderByName(req,"Content-Type") IF m_reqInfo.ctype.getIndexOf("/xml",1) THEN LET m_reqInfo.informat = "XML" ELSE LET m_reqInfo.informat = "JSON" END IF LET m_reqInfo.caccept = getHeaderByName(req,"accept") IF m_reqInfo.caccept.getIndexOf("/xml",1) THEN LET m_reqInfo.outformat = "XML" ELSE LET m_reqInfo.outformat = "JSON" END IF LET m_reqInfo.method = req.getMethod() CALL parseUrl(req.getUrl()) RETURNING m_reqInfo.path, m_reqInfo.query LET str = base.StringTokenizer.create(m_reqInfo.query,"&") LET i=1 CALL m_reqInfo.items.clear() WHILE str.hasMoreTokens() LET token = str.nextToken() LET val = base.StringTokenizer.create(token,"=") IF val.hasMoreTokens() THEN LET m_reqInfo.items[i].name = val.nextToken() END IF IF val.hasMoreTokens() THEN LET m_reqInfo.items[i].value = val.nextToken() END IF LET i=i+1 END WHILE RETURN m_reqInfo.method,m_reqInfo.path END FUNCTION FUNCTION parseUrl(url) DEFINE url STRING DEFINE i INT LET i = url.getIndexOf("?",1) IF i = 0 THEN RETURN url, NULL ELSE RETURN url.subString(1,i), url.subString(i+1,url.getLength()) END IF END FUNCTION FUNCTION getHeaderByName(areq,hname) DEFINE areq com.HTTPServiceRequest DEFINE hname STRING DEFINE aname STRING DEFINE iname STRING DEFINE i INT DEFINE n INT LET aname = hname.toLowerCase() LET n = areq.getRequestHeaderCount() FOR i=1 TO n LET iname = areq.getRequestHeaderName(i) IF aname.equals(iname.toLowerCase()) THEN RETURN areq.getRequestHeaderValue(i) END IF END FOR RETURN NULL END FUNCTION PUBLIC FUNCTION recepcion() DEFINE req com.HttpServiceRequest CALL read_request(req) RETURNING rcode,resp IF rcode <> "200" THEN CALL req.sendTextResponse(501,NULL,"No pudimos procesar el file") ELSE LET doc = xml.DomDocument.Create() CALL doc.setFeature("format-pretty-print",TRUE) CALL doc.load(fileafirmar_f) CALL req.sendXmlResponse(200,NULL,doc) # CALL req.sendTextResponse(200,NULL,resp) END IF END FUNCTION FUNCTION check_utf8() IF ORD("€") == 8364 AND LENGTH("€") == 1 THEN RETURN 0, NULL ELSE RETURN -1, "Application locale must be UTF-8, with char length semantics" END IF END FUNCTION FUNCTION show_verb(msg) DEFINE msg STRING IF dpy_verb THEN DISPLAY msg END IF # IF log_verb THEN # CALL errorlog(msg CLIPPED) # END IF END FUNCTION FUNCTION getxmldata() DEFINE i, l INTEGER DEFINE r om.XmlReader DEFINE e,tok STRING, pfechafirmadigital VARCHAR(50), facturainterna INT DEFINE a om.SaxAttributes DISPLAY "filexml: ",filexml LET r = om.XmlReader.createFileReader(filexml) LET l = 0 LET e = r.read() WHILE e IS NOT NULL CASE e WHEN "StartDocument" # DISPLAY "StartDocument:" WHEN "StartElement" LET l=l+1 #DISPLAY l SPACES, "StartElement:", r.getTagName() LET a = r.getAttributes() FOR i=1 to a.getLength() IF a.getName(i) = "SignatureValue" THEN # DISPLAY l SPACES," ", # a.getName(i)," = ", # a.getValueByIndex(i) END IF END FOR WHEN "Characters" IF r.getTagName() = "TipoeCF" THEN LET tipoecf = r.getCharacters() EXIT CASE END IF IF r.getTagName() = "FechaHoraFirma" THEN LET pfechafirmadigital = r.getCharacters() EXIT CASE END IF IF r.getTagName() = "RNCEmisor" THEN LET rnc_e = r.getCharacters() EXIT CASE END IF IF r.getTagName() = "eNCF" THEN LET e_ncf = r.getCharacters() EXIT CASE END IF IF r.getTagName() = "RNCComprador" THEN LET rnc_c = r.getCharacters() EXIT CASE END IF WHEN "SkippedEntity" # DISPLAY "Entity:'",r.skippedEntity(),"'" WHEN "EndElement" # DISPLAY l SPACES, "EndElement:", r.getTagName() LET l=l-1 WHEN "EndDocument" # DISPLAY "EndDocument:" OTHERWISE # DISPLAY "Invalid event: ",e END CASE LET e=r.read() END WHILE # RETURN phash,pfechafirmadigital,facturainterna END FUNCTION FUNCTION read_request(req) # DEFINE doc xml.DomDocument DEFINE req com.HttpServiceRequest DEFINE cmd,recibido,tmpfile STRING, p RECORD oper STRING, data RECORD usuario STRING, clave STRING, codigo STRING END RECORD END RECORD INITIALIZE cmd TO NULL LET cmd = req.readFileRequest() DISPLAY "request: ",cmd # LET cmd = "cp ",cmd CLIPPED," /home/fourjs/gas/appdata/services/mbs/bin/tmp/recibido.xml" LET cmd = "copy ",cmd CLIPPED, " C:\\ProgramData\\FourJs\\gas\\3.20.06-201906131721\\services\\ecf\\tmp\\recibido.xml" RUN cmd # LET tmpfile = "/home/fourjs/gas/appdata/services/mbs/bin/tmp/recibido.xml" LET tmpfile = ".\\tmp\\recibido.xml" LET recibido = filexml CLIPPED,"recibido.xml" LET filexml = recibido # TRY LET doc = xml.DomDocument.Create() CALL doc.setFeature("whitespace-in-element-content",FALSE) IF os.Path.exists(tmpfile) THEN CALL doc.load(tmpfile) CALL doc.save(recibido) CALL getxmldata() LET filexml = filexml CLIPPED,rnc_e CLIPPED,e_ncf CLIPPED,".xml" CALL doc.save(filexml) CALL formaracuse() RETURNING filexml CALL firmadocumento(filexml) CALL doc.load(fileafirmar_f) LET cmd = doc.saveToString() RETURN 200,cmd ELSE RETURN 401,"Falla en el proceso del file" END IF # CATCH # CALL show_err(SFMT("Unexpected HTTP request read exception: %1", STATUS)) # RETURN FALSE # END TRY END FUNCTION FUNCTION show_err(msg) DEFINE msg STRING DISPLAY "ERROR:",msg # CALL errorlog(msg CLIPPED) END FUNCTION FUNCTION formaracuse() DEFINE chFechaFirma STRING LET fileacuse = fileacuse CLIPPED,"acuse",rnc_c CLIPPED,".xml" LET out=om.XmlWriter.createFileWriter(fileacuse) LET a=om.SaxAttributes.create() CALL out.startDocument() CALL a.addAttribute("xmlns:xs","http://www.w3.org/2001/XMLSchema") CALL out.startElement("ARECF",a) CALL a.clear() CALL out.startElement("DetalleAcusedeRecibo",a) CALL out.startElement("Version",a) CALL out.characters("1.0") CALL out.endElement("Version") CALL out.startElement("RNCEmisor",a) CALL out.characters(rnc_e) CALL out.endElement("RNCEmisor") CALL out.startElement("RNCComprador",a) CALL out.characters(rnc_c) CALL out.endElement("RNCComprador") CALL out.startElement("eNCF",a) CALL out.characters(e_ncf) CALL out.endElement("eNCF") CALL out.startElement("Estado",a) CALL out.characters("0") CALL out.endElement("Estado") LET chFechaFirma = util.Datetime.format(CURRENT, "%d-%m-%Y %H:%M:%S" ) DISPLAY "FECHA ",chFechaFirma CALL out.startElement("FechaHoraAcuseRecibo",a) CALL out.characters(chFechaFirma) CALL out.endElement("FechaHoraAcuseRecibo") CALL out.endElement("DetalleAcusedeRecibo") CALL out.endElement("ARECF") CALL out.endDocument() RETURN fileacuse END FUNCTION FUNCTION limpiatmp() DISPLAY "BORRANDO XML FILES..." RUN 'rm /tmp/*.xml' END FUNCTION FUNCTION firmadocumento(fileafirmar) DEFINE doc xml.DomDocument DEFINE doc2 xml.DomDocument DEFINE root xml.DomNode DEFINE node xml.DomNode DEFINE signNode xml.DomNode DEFINE sig xml.Signature DEFINE key xml.CryptoKey DEFINE cert xml.CryptoX509 DEFINE index INTEGER, fileafirmar STRING, codsecure VARCHAR(6) # Create DomDocument object LET doc = xml.DomDocument.Create() # Notice that whitespaces are significant in cryptography, # therefore it is recommended to remove unnecessary ones CALL doc.setFeature("whitespace-in-element-content",FALSE) #DISPLAY "file ",fileafirmar LET filexml = fileafirmar CALL doc.load(fileafirmar) TRY # Create rsa key LET key = xml.CryptoKey.Create("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256") CALL key.loadPEM("onlyRSA.pem") # Create an X509 certificate with the constructor of the CryptoX509 class. LET cert = xml.CryptoX509.Create() CALL cert.setFeature("X509Certificate",TRUE) # Load the X509 certificate associated to the RSA or DSA private key into the CryptoKey object. # CALL cert.loadPEM("cert509.crt") CALL cert.loadPEM("onlyCert.pem") # Create signature object with the key to use LET sig = xml.Signature.Create() # Assign the CryptoKey object to the Signature object. CALL sig.setKey(KEY) # Assign the CryptoX509 object to the Signature object. CALL sig.setCertificate(cert) # Set XML node to be signed. In our case, the entire document # LET index = sig.createReference("", # "http://www.w3.org/2001/04/xmlenc#sha256") LET index = sig.createReference('""', "http://www.w3.org/2001/04/xmlenc#sha256") # Add enveloped method to not take the XML signature node into account # when computing the entire document. CALL sig.appendReferenceTransformation(index, "http://www.w3.org/2000/09/xmldsig#enveloped-signature",doc.getDocumentElement()) # Set canonicalization method on the XML fragment to be signed. # Compute enveloped signature CALL sig.compute(doc) # Retrieve signature document LET doc2=sig.getDocument() # Append the signature node to the original document to get # a valid enveloped signature # Notice that the enveloped signature can be added anywhere in the # original document LET signNode = doc2.getDocumentElement() # Get Signature node # Import it into the original document LET node = doc.importNode(signNode,true) # Retrieve the original document root node LET root = doc.getDocumentElement() # Append the signature node as last child of the original document CALL root.appendChild(node) # Save document with enveloped signature back to disk CALL doc.setFeature("format-pretty-print",TRUE) LET fileafirmar = ruta_firmados CLIPPED,"\\",os.Path.baseName(fileafirmar) CALL doc.save(fileafirmar) LET fileafirmar_f = fileafirmar CATCH DISPLAY "Unable to create an enveloped signature :",status," ",sqlca.sqlerrm END TRY END FUNCTION