cosmemilton-acbr-node 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +502 -502
  2. package/NOTICE +8 -8
  3. package/README.md +40 -11
  4. package/acbr.config.json +16 -16
  5. package/acbr.lock.json +0 -0
  6. package/dist/cli.js +39 -13
  7. package/dist/cli.js.map +2 -2
  8. package/dist/contract.json +0 -0
  9. package/dist/index.cjs +36 -11
  10. package/dist/index.cjs.map +2 -2
  11. package/dist/index.js +36 -11
  12. package/dist/index.js.map +2 -2
  13. package/dist/runtime.cjs +36 -11
  14. package/dist/runtime.cjs.map +2 -2
  15. package/dist/runtime.js +36 -11
  16. package/dist/runtime.js.map +2 -2
  17. package/dist/types/generated/acbr/index.d.ts +1 -0
  18. package/dist/types/generated/acbr/models.d.ts +0 -0
  19. package/dist/types/src/cli.d.ts +0 -0
  20. package/dist/types/src/config.d.ts +0 -0
  21. package/dist/types/src/contract-help.d.ts +0 -0
  22. package/dist/types/src/diagnostics.d.ts +2 -0
  23. package/dist/types/src/generator.d.ts +0 -0
  24. package/dist/types/src/index.d.ts +1 -0
  25. package/dist/types/src/native-build.d.ts +1 -0
  26. package/dist/types/src/offline-check.d.ts +0 -0
  27. package/dist/types/src/offline-fixture.d.ts +0 -0
  28. package/dist/types/src/runtime.d.ts +1 -0
  29. package/dist/types/src/source.d.ts +0 -0
  30. package/dist/types/src/types.d.ts +4 -2
  31. package/docs/API.md +116 -90
  32. package/docs/ARQUITETURA.md +19 -15
  33. package/docs/CI.md +60 -60
  34. package/docs/CLI.md +1 -1
  35. package/docs/DEPENDENCIAS-NATIVAS.md +93 -93
  36. package/docs/VALIDACAO.md +67 -67
  37. package/docs/examples/recuperar-xml.mjs +54 -0
  38. package/docs/licenses/fpc/COPYING.FPC +25 -25
  39. package/docs/licenses/fpc/COPYING.GPL.txt +340 -340
  40. package/docs/licenses/fpc/COPYING.LGPL.txt +504 -504
  41. package/docs/licenses/fpc/ORIGIN.json +21 -21
  42. package/docs/licenses/lazarus/COPYING.LGPL.txt +481 -481
  43. package/docs/licenses/lazarus/COPYING.modifiedLGPL.txt +26 -26
  44. package/docs/licenses/lazarus/COPYING.txt +46 -46
  45. package/docs/licenses/lazarus/ORIGIN.json +1 -1
  46. package/native/NativeMotor.pas +55 -3
  47. package/native/NativeProtocol.pas +3 -2
  48. package/native/NativeRecovery.pas +274 -0
  49. package/native/generated/AcbrModels.pas +0 -0
  50. package/package.json +76 -76
  51. package/scripts/build-native-deps.mjs +237 -237
  52. package/tools/extract-model.lpr +119 -119
@@ -1,26 +1,26 @@
1
- This is the file COPYING.modifiedLGPL, it applies to several units in the
2
- Lazarus sources distributed by members of the Lazarus Development Team.
3
- All files contains headers showing the appropriate license. See there if this
4
- modification can be applied.
5
-
6
- These files are distributed under the Library GNU General Public License
7
- (see the file COPYING.LGPL) with the following modification:
8
-
9
- As a special exception, the copyright holders of this library give you
10
- permission to link this library with independent modules to produce an
11
- executable, regardless of the license terms of these independent modules,
12
- and to copy and distribute the resulting executable under terms of your choice,
13
- provided that you also meet, for each linked independent module, the terms
14
- and conditions of the license of that module. An independent module is a
15
- module which is not derived from or based on this library. If you modify this
16
- library, you may extend this exception to your version of the library, but
17
- you are not obligated to do so. If you do not wish to do so, delete this
18
- exception statement from your version.
19
-
20
-
21
- If you didn't receive a copy of the file COPYING.LGPL, contact:
22
- Free Software Foundation, Inc.,
23
- 675 Mass Ave
24
- Cambridge, MA 02139
25
- USA
26
-
1
+ This is the file COPYING.modifiedLGPL, it applies to several units in the
2
+ Lazarus sources distributed by members of the Lazarus Development Team.
3
+ All files contains headers showing the appropriate license. See there if this
4
+ modification can be applied.
5
+
6
+ These files are distributed under the Library GNU General Public License
7
+ (see the file COPYING.LGPL) with the following modification:
8
+
9
+ As a special exception, the copyright holders of this library give you
10
+ permission to link this library with independent modules to produce an
11
+ executable, regardless of the license terms of these independent modules,
12
+ and to copy and distribute the resulting executable under terms of your choice,
13
+ provided that you also meet, for each linked independent module, the terms
14
+ and conditions of the license of that module. An independent module is a
15
+ module which is not derived from or based on this library. If you modify this
16
+ library, you may extend this exception to your version of the library, but
17
+ you are not obligated to do so. If you do not wish to do so, delete this
18
+ exception statement from your version.
19
+
20
+
21
+ If you didn't receive a copy of the file COPYING.LGPL, contact:
22
+ Free Software Foundation, Inc.,
23
+ 675 Mass Ave
24
+ Cambridge, MA 02139
25
+ USA
26
+
@@ -1,46 +1,46 @@
1
- This is the file COPYING. It gives an overview of the licenses used in the
2
- Lazarus sources.
3
-
4
- The Lazarus sources consists of several parts and each part has its own
5
- license. Three main licenses are in use:
6
- * the GNU General Public License, version 2 (GPLv2)
7
- * a modified LGPL (as described in COPYING.modifiedLGPL)
8
- * the MPL
9
- In general, each file contains a header, describing the license of the file.
10
-
11
- The license directory tree:
12
-
13
- <lazarus>/
14
- |
15
- +- ide (GPLv2)
16
- |
17
- +- designer (GPLv2)
18
- |
19
- +- debugger (GPLv2)
20
- |
21
- +- examples (GPLv2)
22
- |
23
- +- packager (GPLv2)
24
- |
25
- +- tools (GPLv2)
26
- |
27
- +- lcl (modified LGPL)
28
- | With the exception of the customdrawn/android interface,
29
- | where some files are licensed under the
30
- | Apache Licence, Version 2.
31
- |
32
- +- components/
33
- |
34
- +- codetools (GPLv2)
35
- |
36
- +- synedit (MPL - Mozilla public license)
37
- |
38
- +- xxx There are various packages under various licenses. Mostly the
39
- modified LGPL. See the license in the package files for details.
40
- Some are not free for commercial use.
41
-
42
- The IDE files are the files in the ide, designer, packager and debugger
43
- directory. They are under the GPLv2, with the following exceptions:
44
- transfermacros.pp, wordcompletion.pp, patheditordlg.pas, outputfilter.pas,
45
- inputfiledialog.pas, findreplacedialog.pp, findinfilesdlg.pas
46
- These files are under the modified LGPL as described in COPYING.modifiedLGPL.
1
+ This is the file COPYING. It gives an overview of the licenses used in the
2
+ Lazarus sources.
3
+
4
+ The Lazarus sources consists of several parts and each part has its own
5
+ license. Three main licenses are in use:
6
+ * the GNU General Public License, version 2 (GPLv2)
7
+ * a modified LGPL (as described in COPYING.modifiedLGPL)
8
+ * the MPL
9
+ In general, each file contains a header, describing the license of the file.
10
+
11
+ The license directory tree:
12
+
13
+ <lazarus>/
14
+ |
15
+ +- ide (GPLv2)
16
+ |
17
+ +- designer (GPLv2)
18
+ |
19
+ +- debugger (GPLv2)
20
+ |
21
+ +- examples (GPLv2)
22
+ |
23
+ +- packager (GPLv2)
24
+ |
25
+ +- tools (GPLv2)
26
+ |
27
+ +- lcl (modified LGPL)
28
+ | With the exception of the customdrawn/android interface,
29
+ | where some files are licensed under the
30
+ | Apache Licence, Version 2.
31
+ |
32
+ +- components/
33
+ |
34
+ +- codetools (GPLv2)
35
+ |
36
+ +- synedit (MPL - Mozilla public license)
37
+ |
38
+ +- xxx There are various packages under various licenses. Mostly the
39
+ modified LGPL. See the license in the package files for details.
40
+ Some are not free for commercial use.
41
+
42
+ The IDE files are the files in the ide, designer, packager and debugger
43
+ directory. They are under the GPLv2, with the following exceptions:
44
+ transfermacros.pp, wordcompletion.pp, patheditordlg.pas, outputfilter.pas,
45
+ inputfiledialog.pas, findreplacedialog.pp, findinfilesdlg.pas
46
+ These files are under the modified LGPL as described in COPYING.modifiedLGPL.
@@ -1 +1 @@
1
- {"version":"3.0","url":"https://gitlab.com/freepascal.org/lazarus/lazarus/-/tree/lazarus_3_0"}
1
+ {"version":"3.0","url":"https://gitlab.com/freepascal.org/lazarus/lazarus/-/tree/lazarus_3_0"}
@@ -6,7 +6,7 @@ procedure Executar(Q,R:TJSONObject);
6
6
  implementation
7
7
  uses Classes,SysUtils,DateUtils,base64,ACBrNFe,ACBrDFeSSL,ACBrNFeNotasFiscais,
8
8
  ACBrDFe.Conversao,pcnConversao,pcnConversaoNFe,ACBrUtil.Strings,
9
- AcbrModels,NativeProtocol,NativeTLS,OpenSSLExt,ACBrDFeComum.RetEnvio,ACBrNFe.RetInut;
9
+ AcbrModels,NativeProtocol,NativeTLS,NativeRecovery,OpenSSLExt,ACBrDFeComum.RetEnvio,ACBrNFe.RetInut;
10
10
  procedure Estado(R:TJSONObject;const S:string);
11
11
  begin R.Strings['estado']:=S;R.Booleans['sucesso']:=not(S='ERRO')and not(S='REJEITADO')and not(S='PENDENTE')and not(S='INDETERMINADO')and not(S='DENEGADO');end;
12
12
  procedure ChaveValida(const Key:string;C:TJSONObject;M:TACBrNFe);
@@ -66,10 +66,26 @@ begin
66
66
  end;
67
67
  end;
68
68
  procedure CarregarXML(M:TACBrNFe;Q,C:TJSONObject;out XML:string);
69
+ var OriginalKey,OriginalDocument,OriginalDigest:string;
69
70
  begin
70
71
  XML:=Decodificar(Texto(Q,'xmlBase64'),4*1024*1024);
71
72
  if(Pos('<!DOCTYPE',UpperCase(XML))>0)or(Pos('<!ENTITY',UpperCase(XML))>0)or(Pos(#0,XML)>0)or(Pos('<nfeProc',XML)>0)or(Pos('<enviNFe',XML)>0)then Falhar('XML_INVALIDO','Informe um XML NFe sem envelope, DTD ou entidades externas.');
72
- if not M.NotasFiscais.LoadFromString(XML,False)or(M.NotasFiscais.Count<>1)then Falhar('XML_INVALIDO','XML deve conter uma única NF-e 4.00.');
73
+ if Texto(Q,'comando')='recuperarXml'then begin
74
+ { Collection.LoadFromString in the pinned source searches literal NFe tags.
75
+ Its individual DOM reader supports namespaces; validate the exact root
76
+ first, then load it directly without rewriting any signed source bytes. }
77
+ try
78
+ OriginalKey:=ChaveAlvoRecuperacao(XML);
79
+ InspecionarAlvoRecuperacao(XML,OriginalKey,OriginalDocument,OriginalDigest);
80
+ except
81
+ on E:EFiscal do begin
82
+ if E.Codigo='RETORNO_INVALIDO'then Falhar('XML_INVALIDO','XML deve conter uma unica NFe assinada sem envelope.');
83
+ raise;
84
+ end;
85
+ end;
86
+ if not M.NotasFiscais.Add.LerXML(XML)or(M.NotasFiscais.Count<>1)then Falhar('XML_INVALIDO','XML deve conter uma unica NF-e 4.00.');
87
+ if M.NotasFiscais.Items[0].NumID<>OriginalKey then Falhar('IDENTIDADE_DIVERGENTE','Leitura da NFe diverge da identidade original.');
88
+ end else if not M.NotasFiscais.LoadFromString(XML,False)or(M.NotasFiscais.Count<>1)then Falhar('XML_INVALIDO','XML deve conter uma única NF-e 4.00.');
73
89
  Identidade(M,C);ChaveValida(M.NotasFiscais.Items[0].NumID,C,M);
74
90
  with M.NotasFiscais.Items[0].NFe.Ide do if(StrToIntDef(Copy(M.NotasFiscais.Items[0].NumID,23,3),-1)<>serie)or(StrToIntDef(Copy(M.NotasFiscais.Items[0].NumID,26,9),-1)<>nNF)or(Copy(M.NotasFiscais.Items[0].NumID,35,1)<>'1')or(StrToIntDef(Copy(M.NotasFiscais.Items[0].NumID,36,8),-1)<>cNF)then Falhar('IDENTIDADE_DIVERGENTE','Chave fiscal divergente dos dados do documento.');
75
91
  if(Texto(Q,'chave',False)<>'')and(Texto(Q,'chave')<>M.NotasFiscais.Items[0].NumID)then Falhar('IDENTIDADE_DIVERGENTE','Chave do XML divergente.');
@@ -78,6 +94,14 @@ procedure Schema(M:TACBrNFe);
78
94
  begin
79
95
  try M.NotasFiscais.Validar;except Falhar('XML_SCHEMA_INVALIDO','XML nao atende aos schemas fiscais.');end;
80
96
  end;
97
+ procedure SchemaIntegral(M:TACBrNFe;const XML,Base,Name:string);
98
+ var Err:string;
99
+ begin
100
+ if not FileExists(Base+'schemas/'+Name)then Falhar('SCHEMAS_AUSENTES','Schema de recuperacao indisponivel.');
101
+ try
102
+ if not M.SSL.Validar(XML,Base+'schemas/'+Name,Err)then Falhar('XML_SCHEMA_INVALIDO','XML nao atende ao schema de recuperacao.');
103
+ except on E:EFiscal do raise;on E:Exception do Falhar('XML_SCHEMA_INVALIDO','XML nao atende ao schema de recuperacao.');end;
104
+ end;
81
105
  procedure ProtocoloValido(const P:string);
82
106
  var I:Integer;
83
107
  begin
@@ -133,7 +157,7 @@ begin
133
157
  Estado(R,S);
134
158
  end;
135
159
  procedure Executar(Q,R:TJSONObject);
136
- var M:TACBrNFe;C,Inut:TJSONObject;Base,Cmd,XML,Key,Receipt,Err,J:string;Sending:Boolean;I,Found:Integer;
160
+ var M:TACBrNFe;C,Inut:TJSONObject;Base,Cmd,XML,Key,Receipt,Err,J,Documento,Digest,ConsultaXML,ProtXML,Prot,Motivo:string;Sending:Boolean;I,Found,Code:Integer;
137
161
  procedure InicioEnvio;
138
162
  begin Sending:=True;R.Booleans['envioIniciado']:=True;MarcadorEnvio(Texto(Q,'id'));end;
139
163
  begin
@@ -153,6 +177,34 @@ begin
153
177
  Identidade(M,C);XML:=M.NotasFiscais.Items[0].GerarXML;
154
178
  if(XML='')or(Length(XML)>4*1024*1024)then Falhar('XML_INVALIDO','XML fiscal vazio ou acima do limite.');
155
179
  ChaveValida(M.NotasFiscais.Items[0].NumID,C,M);DefinirTexto(R,'chave',M.NotasFiscais.Items[0].NumID);DefinirTexto(R,'xmlBase64',EncodeStringBase64(XML));Estado(R,'XML_GERADO');
180
+ end else if Cmd='recuperarXml'then begin
181
+ CarregarXML(M,Q,C,XML);Key:=M.NotasFiscais.Items[0].NumID;
182
+ InspecionarAlvoRecuperacao(XML,Key,Documento,Digest);
183
+ SchemaIntegral(M,XML,Base,'nfe_v4.00.xsd');
184
+ if not M.SSL.VerificarAssinatura(XML,Err,'infNFe')then Falhar('ASSINATURA_INVALIDA','Assinatura fiscal invalida.');
185
+ { All input proof is complete and its raw source/digest are preserved above.
186
+ Clear the temporary model before the key-only query: the pinned ACBr
187
+ loaded-note path rewrites XMLOriginal with a literal-root writer and an
188
+ apostrophe replacement. Recovery independently proves the real RetWS. }
189
+ M.NotasFiscais.Clear;
190
+ M.WebServices.Consulta.NFeChave:=Key;M.WebServices.Consulta.ExtrairEventos:=False;
191
+ M.WebServices.Consulta.Executar;
192
+ ConsultaXML:=M.WebServices.Consulta.RetWS;
193
+ InspecionarConsultaRecuperacao(ConsultaXML,Key,Digest,Inteiro(C,'ambiente'),M.Configuracoes.WebServices.UFCodigo,Code,Motivo,Prot,ProtXML);
194
+ SchemaIntegral(M,ConsultaXML,Base,'retConsSitNFe_v4.00.xsd');
195
+ if Code<>100 then begin
196
+ R.Add('cStat',Code);DefinirTexto(R,'xMotivo',Motivo);DefinirTexto(R,'chave',Key);
197
+ DefinirTexto(R,'codigo','RECUPERACAO_NAO_COMPROVADA');Estado(R,'CONSULTADO');R.Booleans['sucesso']:=False;
198
+ end else begin
199
+ { Only the authentic query protocol and untouched original are used;
200
+ protocol scalars and a previous emission response are never composed. }
201
+ XML:=ComporRecuperacao(Documento,ProtXML);
202
+ ConferirRecuperacao(XML,Documento,ProtXML);
203
+ SchemaIntegral(M,XML,Base,'procNFe_v4.00.xsd');
204
+ if not M.SSL.VerificarAssinatura(XML,Err,'infNFe')then Falhar('ASSINATURA_INVALIDA','Assinatura fiscal invalida no XML recuperado.');
205
+ DefinirTexto(R,'chave',Key);DefinirTexto(R,'protocolo',Prot);R.Add('cStat',100);DefinirTexto(R,'xMotivo',Motivo);
206
+ DefinirTexto(R,'xmlBase64',EncodeStringBase64(XML));Estado(R,'XML_RECUPERADO');
207
+ end;
156
208
  end else if(Cmd='validar')or(Cmd='assinar')or(Cmd='transmitir')then begin
157
209
  CarregarXML(M,Q,C,XML);Key:=M.NotasFiscais.Items[0].NumID;DefinirTexto(R,'chave',Key);
158
210
  if Cmd='assinar'then begin
@@ -47,7 +47,7 @@ begin
47
47
  OnlyKeys(O,'|versao|id|comando|sourceRevision|contractHash|config|documento|xmlBase64|chave|recibo|protocolo|justificativa|correcao|sequenciaEvento|inutilizacao|');
48
48
  if(Inteiro(O,'versao')<>1)or(Inteiro(O,'sourceRevision')<>SourceRevision)or(Texto(O,'contractHash')<>ContractHash)then Falhar('CONTRATO_INCOMPATIVEL','Contrato ou revisão ACBr incompatível.');
49
49
  S:=Texto(O,'id');if Length(S)>100 then Falhar('REQUISICAO_INVALIDA','Identificador inválido.');for I:=1 to Length(S)do if not(S[I]in['A'..'Z','a'..'z','0'..'9','_','-'])then Falhar('REQUISICAO_INVALIDA','Identificador inválido.');
50
- Cmd:=Texto(O,'comando');if Pos('|'+Cmd+'|','|gerarXml|validar|assinar|transmitir|consultar|cancelar|cartaCorrecao|inutilizar|')=0 then Falhar('REQUISICAO_INVALIDA','Comando fiscal inválido.');
50
+ Cmd:=Texto(O,'comando');if Pos('|'+Cmd+'|','|gerarXml|validar|assinar|transmitir|consultar|recuperarXml|cancelar|cartaCorrecao|inutilizar|')=0 then Falhar('REQUISICAO_INVALIDA','Comando fiscal inválido.');
51
51
  C:=Objeto(O,'config');OnlyKeys(C,'|cnpj|uf|modelo|ambiente|certificado|csc|');
52
52
  S:=Texto(C,'cnpj');if Length(S)<>14 then Falhar('REQUISICAO_INVALIDA','CNPJ inválido.');for I:=1 to Length(S)do if not(S[I]in['0'..'9'])then Falhar('REQUISICAO_INVALIDA','CNPJ inválido.');
53
53
  if not(Inteiro(C,'modelo')in[55,65])or not(Inteiro(C,'ambiente')in[1,2])then Falhar('REQUISICAO_INVALIDA','Modelo ou ambiente inválido.');
@@ -56,7 +56,8 @@ begin
56
56
  if C.Find('csc')<>nil then begin Cert:=Objeto(C,'csc');OnlyKeys(Cert,'|id|valor|');S:=Texto(Cert,'id');if Length(S)>6 then Falhar('REQUISICAO_INVALIDA','CSC inválido.');for I:=1 to Length(S)do if not(S[I]in['0'..'9'])then Falhar('REQUISICAO_INVALIDA','CSC inválido.');if Length(Texto(Cert,'valor'))>256 then Falhar('REQUISICAO_INVALIDA','CSC inválido.');end;
57
57
  if(Cmd='cartaCorrecao')and(Inteiro(C,'modelo')<>55)then Falhar('REQUISICAO_INVALIDA','Carta de correção restrita à NF-e.');
58
58
  if(Cmd='gerarXml')and(O.Find('documento')=nil)then Falhar('REQUISICAO_INVALIDA','Documento obrigatório para gerar XML.');
59
- if(Pos('|'+Cmd+'|','|validar|assinar|transmitir|')>0)and(O.Find('xmlBase64')=nil)then Falhar('REQUISICAO_INVALIDA','XML obrigatório.');
59
+ if(Pos('|'+Cmd+'|','|validar|assinar|transmitir|recuperarXml|')>0)and(O.Find('xmlBase64')=nil)then Falhar('REQUISICAO_INVALIDA','XML obrigatório.');
60
+ if(Cmd='recuperarXml')and((O.Find('chave')<>nil)or(O.Find('recibo')<>nil)or(O.Find('protocolo')<>nil)or(O.Find('documento')<>nil)or(O.Find('inutilizacao')<>nil)or(O.Find('justificativa')<>nil)or(O.Find('correcao')<>nil)or(O.Find('sequenciaEvento')<>nil))then Falhar('REQUISICAO_INVALIDA','Recuperacao recebe somente o XML original assinado.');
60
61
  end;
61
62
  procedure MarcadorEnvio(const Id:string);
62
63
  var O:TJSONObject;
@@ -0,0 +1,274 @@
1
+ unit NativeRecovery;
2
+ {$mode objfpc}{$H+}{$codepage utf8}
3
+ interface
4
+
5
+ function ChaveAlvoRecuperacao(const XML:string):string;
6
+ procedure InspecionarAlvoRecuperacao(const XML,Key:string;out Documento,Digest:string);
7
+ procedure InspecionarConsultaRecuperacao(const XML,Key,Digest:string;
8
+ Ambiente,UFCodigo:Integer;out Code:Integer;out Motivo,Protocolo,XMLProtocolo:string);
9
+ function ComporRecuperacao(const Documento,XMLProtocolo:string):string;
10
+ procedure ConferirRecuperacao(const XML,Documento,XMLProtocolo:string);
11
+
12
+ implementation
13
+ uses Classes,SysUtils,DOM,XMLRead,NativeErrors;
14
+
15
+ const NFeNS='http://www.portalfiscal.inf.br/nfe';
16
+ DSigNS='http://www.w3.org/2000/09/xmldsig#';
17
+ MaxXMLBytes=4*1024*1024;
18
+
19
+ function Nome(N:TDOMNode):string;
20
+ begin Result:=UTF8Encode(N.LocalName);if Result=''then Result:=UTF8Encode(N.NodeName);end;
21
+
22
+ function Atributo(N:TDOMNode;const Name:string):string;
23
+ var A:TDOMNode;
24
+ begin Result:='';if N.Attributes=nil then Exit;A:=N.Attributes.GetNamedItem(UTF8Decode(Name));if A<>nil then Result:=UTF8Encode(A.NodeValue);end;
25
+
26
+ procedure Invalido;
27
+ begin Falhar('RETORNO_INVALIDO','XML fiscal de recuperacao invalido.');end;
28
+
29
+ procedure Seguro(const XML:string);
30
+ var U:string;
31
+ begin
32
+ U:=UpperCase(XML);
33
+ if(XML='')or(Length(XML)>MaxXMLBytes)or(Pos(#0,XML)>0)or
34
+ (Pos('<!DOCTYPE',U)>0)or(Pos('<!ENTITY',U)>0)then Invalido;
35
+ if UTF8Encode(UTF8Decode(XML))<>XML then Invalido;
36
+ end;
37
+
38
+ function Ler(const XML:string):TXMLDocument;
39
+ var S:TStringStream;Parser:TDOMParser;Source:TXMLInputSource;
40
+ begin
41
+ Seguro(XML);Result:=nil;S:=TStringStream.Create(XML);Parser:=TDOMParser.Create;Source:=TXMLInputSource.Create(S);
42
+ try
43
+ Parser.Options.Namespaces:=True;Parser.Options.DisallowDoctype:=True;
44
+ Parser.Options.ExpandEntities:=False;Parser.Options.MaxChars:=MaxXMLBytes;
45
+ try Parser.Parse(Source,Result);except Result.Free;Result:=nil;Invalido;end;
46
+ finally Source.Free;Parser.Free;S.Free;end;
47
+ if(Result=nil)or(Result.DocumentElement=nil)then begin Result.Free;Invalido;end;
48
+ end;
49
+
50
+ procedure Raiz(D:TXMLDocument;const Name,Version:string);
51
+ begin
52
+ if(Nome(D.DocumentElement)<>Name)or(UTF8Encode(D.DocumentElement.NamespaceURI)<>NFeNS)or
53
+ ((Version<>'')and(Atributo(D.DocumentElement,'versao')<>Version))then Invalido;
54
+ end;
55
+
56
+ function Filho(N:TDOMNode;const Name,NS:string;Required:Boolean=True):TDOMNode;
57
+ var C:TDOMNode;
58
+ begin
59
+ Result:=nil;C:=N.FirstChild;
60
+ while C<>nil do begin
61
+ if(C.NodeType=ELEMENT_NODE)and(Nome(C)=Name)then begin
62
+ if(Result<>nil)or(UTF8Encode(C.NamespaceURI)<>NS)then Invalido;Result:=C;
63
+ end;
64
+ C:=C.NextSibling;
65
+ end;
66
+ if Required and(Result=nil)then Invalido;
67
+ end;
68
+
69
+ function Valor(N:TDOMNode;const Name:string;Required:Boolean=True):string;
70
+ var C,V:TDOMNode;
71
+ begin
72
+ C:=Filho(N,Name,NFeNS,Required);Result:='';
73
+ if C<>nil then begin
74
+ V:=C.FirstChild;while V<>nil do begin if V.NodeType=ELEMENT_NODE then Invalido;V:=V.NextSibling;end;
75
+ Result:=UTF8Encode(C.TextContent);
76
+ if Required and(Result='')then Invalido;
77
+ end;
78
+ end;
79
+
80
+ procedure SemEnvelope(N:TDOMNode;Depth:Integer=0);
81
+ var C:TDOMNode;S:string;
82
+ begin
83
+ if Depth>128 then Invalido;
84
+ C:=N.FirstChild;
85
+ while C<>nil do begin
86
+ if C.NodeType=ELEMENT_NODE then begin
87
+ S:=Nome(C);
88
+ if(S='NFe')or(S='nfeProc')or(S='protNFe')or(S='enviNFe')then Invalido;
89
+ SemEnvelope(C,Depth+1);
90
+ end;
91
+ C:=C.NextSibling;
92
+ end;
93
+ end;
94
+
95
+ function Branco(C:Char):Boolean;
96
+ begin Result:=C in[#9,#10,#13,' '];end;
97
+
98
+ { The DOM establishes well-formedness, cardinality and namespaces. This scanner
99
+ only locates byte ranges in that already parsed document; it never interprets
100
+ fiscal values or rewrites signed content. }
101
+ function Fragmento(const XML,ChildName:string):string;
102
+ var I,J,K,Depth,Start,Last,RootStart,RootEnd,Count:Integer;
103
+ Quote:Char;Tag,Local:string;Closing,Empty:Boolean;
104
+ begin
105
+ I:=1;Depth:=0;Start:=0;Last:=0;RootStart:=0;RootEnd:=0;Count:=0;
106
+ while I<=Length(XML)do begin
107
+ if XML[I]<>'<'then begin Inc(I);Continue;end;
108
+ if Copy(XML,I,4)='<!--'then begin
109
+ J:=I+4;while(J<=Length(XML)-2)and(Copy(XML,J,3)<>'-->')do Inc(J);
110
+ I:=J+3;Continue;
111
+ end;
112
+ if Copy(XML,I,9)='<![CDATA['then begin
113
+ J:=I+9;while(J<=Length(XML)-2)and(Copy(XML,J,3)<>']]>')do Inc(J);
114
+ I:=J+3;Continue;
115
+ end;
116
+ if Copy(XML,I,2)='<?'then begin
117
+ J:=I+2;while(J<=Length(XML)-1)and(Copy(XML,J,2)<>'?>')do Inc(J);
118
+ I:=J+2;Continue;
119
+ end;
120
+ J:=I+1;Quote:=#0;
121
+ while J<=Length(XML)do begin
122
+ if Quote<>#0 then begin if XML[J]=Quote then Quote:=#0;end
123
+ else if XML[J]in['"',#39]then Quote:=XML[J]
124
+ else if XML[J]='>'then Break;
125
+ Inc(J);
126
+ end;
127
+ if J>Length(XML)then Invalido;
128
+ K:=I+1;Closing:=XML[K]='/';if Closing then Inc(K);
129
+ Tag:='';while(K<J)and not Branco(XML[K])and not(XML[K]in['/','>'])do begin Tag:=Tag+XML[K];Inc(K);end;
130
+ Local:=Copy(Tag,Pos(':',Tag)+1,MaxInt);K:=J-1;
131
+ while(K>I)and Branco(XML[K])do Dec(K);Empty:=XML[K]='/';
132
+ if Closing then begin
133
+ Dec(Depth);
134
+ if(ChildName<>'')and(Depth=1)and(Start>0)and(Last=0)then Last:=J;
135
+ if Depth=0 then RootEnd:=J;
136
+ end else begin
137
+ if Depth=0 then begin if RootStart<>0 then Invalido;RootStart:=I;end;
138
+ if(ChildName<>'')and(Depth=1)and(Local=ChildName)then begin
139
+ Inc(Count);if Count<>1 then Invalido;Start:=I;if Empty then Last:=J;
140
+ end;
141
+ if not Empty then Inc(Depth)else if Depth=0 then RootEnd:=J;
142
+ end;
143
+ I:=J+1;
144
+ end;
145
+ if ChildName=''then begin Start:=RootStart;Last:=RootEnd;end;
146
+ if(Start=0)or(Last<Start)then Invalido;
147
+ Result:=Copy(XML,Start,Last-Start+1);
148
+ end;
149
+
150
+ function Escapar(const S:string):string;
151
+ begin
152
+ Result:=StringReplace(S,'&','&amp;',[rfReplaceAll]);
153
+ Result:=StringReplace(Result,'"','&quot;',[rfReplaceAll]);
154
+ Result:=StringReplace(Result,'<','&lt;',[rfReplaceAll]);
155
+ end;
156
+
157
+ function ProtocoloFechado(const XML:string;Root,Prot:TDOMNode):string;
158
+ var I,P:Integer;A:TDOMNode;Name,Extra:string;
159
+ begin
160
+ Result:=Fragmento(XML,'protNFe');Extra:='';
161
+ { A direct child can inherit namespace bindings from retConsSitNFe. Copy only
162
+ those declarations, onto that protocol alone, so the NFe signature context
163
+ cannot acquire new prefix bindings from the consultation. }
164
+ for I:=0 to Root.Attributes.Length-1 do begin
165
+ A:=Root.Attributes.Item[I];Name:=UTF8Encode(A.NodeName);
166
+ if((Name='xmlns')or(Copy(Name,1,6)='xmlns:'))and(Atributo(Prot,Name)='')then
167
+ Extra:=Extra+' '+Name+'="'+Escapar(UTF8Encode(A.NodeValue))+'"';
168
+ end;
169
+ if Extra<>''then begin
170
+ P:=2;while(P<=Length(Result))and not Branco(Result[P])and not(Result[P]in['/','>'])do Inc(P);
171
+ Insert(Extra,Result,P);
172
+ end;
173
+ end;
174
+
175
+ function ChaveAlvoRecuperacao(const XML:string):string;
176
+ var D:TXMLDocument;Info:TDOMNode;Id:string;
177
+ begin
178
+ D:=Ler(XML);
179
+ try
180
+ Raiz(D,'NFe','');SemEnvelope(D.DocumentElement);
181
+ Info:=Filho(D.DocumentElement,'infNFe',NFeNS);Id:=Atributo(Info,'Id');
182
+ if(Length(Id)<>47)or(Copy(Id,1,3)<>'NFe')then Invalido;
183
+ Result:=Copy(Id,4,44);
184
+ finally D.Free;end;
185
+ end;
186
+
187
+ procedure InspecionarAlvoRecuperacao(const XML,Key:string;out Documento,Digest:string);
188
+ var D:TXMLDocument;R,Info,Sig,SignedInfo,Ref,DigestNode,C:TDOMNode;
189
+ begin
190
+ D:=Ler(XML);
191
+ try
192
+ Raiz(D,'NFe','');R:=D.DocumentElement;SemEnvelope(R);
193
+ Info:=Filho(R,'infNFe',NFeNS);if(Atributo(Info,'Id')<>'NFe'+Key)or(Atributo(Info,'versao')<>'4.00')then Invalido;
194
+ Sig:=Filho(R,'Signature',DSigNS,False);
195
+ if Sig=nil then Falhar('XML_NAO_ASSINADO','Recuperacao exige a NFe original assinada.');
196
+ C:=R.FirstChild;while C<>nil do begin
197
+ if(C.NodeType=ELEMENT_NODE)and(C<>Info)and(C<>Sig)and(C<>Filho(R,'infNFeSupl',NFeNS,False))then Invalido;
198
+ C:=C.NextSibling;
199
+ end;
200
+ SignedInfo:=Filho(Sig,'SignedInfo',DSigNS);Ref:=Filho(SignedInfo,'Reference',DSigNS);
201
+ if Atributo(Ref,'URI')<>'#NFe'+Key then Falhar('ASSINATURA_INVALIDA','Assinatura nao referencia a NFe original.');
202
+ DigestNode:=Filho(Ref,'DigestValue',DSigNS);Digest:=UTF8Encode(DigestNode.TextContent);
203
+ if Digest=''then Falhar('ASSINATURA_INVALIDA','Assinatura fiscal sem digest.');
204
+ Documento:=Fragmento(XML,'');
205
+ finally D.Free;end;
206
+ end;
207
+
208
+ procedure InspecionarConsultaRecuperacao(const XML,Key,Digest:string;
209
+ Ambiente,UFCodigo:Integer;out Code:Integer;out Motivo,Protocolo,XMLProtocolo:string);
210
+ var D:TXMLDocument;R,Prot,Info:TDOMNode;ReturnedDigest:string;I:Integer;
211
+ begin
212
+ Protocolo:='';XMLProtocolo:='';D:=Ler(XML);
213
+ try
214
+ Raiz(D,'retConsSitNFe','4.00');R:=D.DocumentElement;
215
+ if(Valor(R,'chNFe')<>Key)or(Valor(R,'tpAmb')<>IntToStr(Ambiente))or(Valor(R,'cUF')<>IntToStr(UFCodigo))then
216
+ Falhar('IDENTIDADE_DIVERGENTE','Consulta retornou outro documento ou ambiente.');
217
+ Code:=StrToIntDef(Valor(R,'cStat'),0);Motivo:=Valor(R,'xMotivo');
218
+ Prot:=Filho(R,'protNFe',NFeNS,False);
219
+ if Code<>100 then Exit;
220
+ if(Filho(R,'retCancNFe',NFeNS,False)<>nil)or(Filho(R,'procEventoNFe',NFeNS,False)<>nil)then
221
+ Falhar('RECUPERACAO_NAO_COMPROVADA','Consulta contem evento que exige analise separada.');
222
+ if Prot=nil then Falhar('RECUPERACAO_NAO_COMPROVADA','Consulta autorizada sem protocolo completo.');
223
+ if Atributo(Prot,'versao')<>'4.00'then Invalido;
224
+ { The pinned signature verifier resolves SignedInfo globally. It cannot
225
+ establish both signatures independently in a two-signature process. Do not
226
+ return a process whose optional protocol signature has not been proved. }
227
+ if Filho(Prot,'Signature',DSigNS,False)<>nil then
228
+ Falhar('RECUPERACAO_NAO_COMPROVADA','Protocolo com assinatura adicional exige verificacao independente.');
229
+ Info:=Filho(Prot,'infProt',NFeNS);
230
+ if(Valor(Info,'chNFe')<>Key)or(Valor(Info,'tpAmb')<>IntToStr(Ambiente))or(Valor(Info,'cStat')<>'100')then
231
+ Falhar('IDENTIDADE_DIVERGENTE','Protocolo retornou outro documento, ambiente ou situacao.');
232
+ Protocolo:=Valor(Info,'nProt',False);
233
+ if Length(Protocolo)<>15 then Falhar('RECUPERACAO_NAO_COMPROVADA','Consulta autorizada sem protocolo completo.');
234
+ for I:=1 to Length(Protocolo)do if not(Protocolo[I]in['0'..'9'])then Invalido;
235
+ ReturnedDigest:=Valor(Info,'digVal',False);
236
+ if(ReturnedDigest='')or(Digest='')then Falhar('RECUPERACAO_NAO_COMPROVADA','Consulta sem digest para comprovar a NFe original.');
237
+ if ReturnedDigest<>Digest then Falhar('IDENTIDADE_DIVERGENTE','Digest da consulta diverge da NFe original assinada.');
238
+ XMLProtocolo:=ProtocoloFechado(XML,R,Prot);
239
+ finally D.Free;end;
240
+ end;
241
+
242
+ function ComporRecuperacao(const Documento,XMLProtocolo:string):string;
243
+ var D:TXMLDocument;QName,Prefix,ProcessName,Declaration:string;P:Integer;
244
+ begin
245
+ { Only a fixed process envelope is written here. Neither the signed NFe nor
246
+ fiscal protocol fields are generated from scalar values. Reuse the NFe's
247
+ namespace prefix: introducing a different binding on an ancestor can change
248
+ inclusive canonicalization even while all signed source bytes stay equal. }
249
+ D:=Ler(Documento);
250
+ try
251
+ Raiz(D,'NFe','');QName:=UTF8Encode(D.DocumentElement.NodeName);P:=Pos(':',QName);
252
+ Prefix:='';if P>0 then Prefix:=Copy(QName,1,P-1);
253
+ ProcessName:='nfeProc';Declaration:='xmlns';
254
+ if Prefix<>''then begin ProcessName:=Prefix+':nfeProc';Declaration:=Declaration+':'+Prefix;end;
255
+ Result:='<?xml version="1.0" encoding="UTF-8"?><'+ProcessName+' '+Declaration+'="'+NFeNS+'" versao="4.00">'+Documento+XMLProtocolo+'</'+ProcessName+'>';
256
+ finally D.Free;end;
257
+ if Length(Result)>MaxXMLBytes then Invalido;
258
+ end;
259
+
260
+ procedure ConferirRecuperacao(const XML,Documento,XMLProtocolo:string);
261
+ var D:TXMLDocument;R,C,N,P:TDOMNode;
262
+ begin
263
+ D:=Ler(XML);
264
+ try
265
+ Raiz(D,'nfeProc','4.00');R:=D.DocumentElement;
266
+ N:=Filho(R,'NFe',NFeNS);P:=Filho(R,'protNFe',NFeNS);
267
+ C:=R.FirstChild;while C<>nil do begin
268
+ if(C.NodeType=ELEMENT_NODE)and(C<>N)and(C<>P)then Invalido;C:=C.NextSibling;
269
+ end;
270
+ if(Fragmento(XML,'NFe')<>Documento)or(Fragmento(XML,'protNFe')<>XMLProtocolo)then
271
+ Falhar('XML_ALTERADO','Recuperacao alterou o documento ou protocolo original.');
272
+ finally D.Free;end;
273
+ end;
274
+ end.
File without changes