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.
- package/LICENSE +502 -502
- package/NOTICE +8 -8
- package/README.md +40 -11
- package/acbr.config.json +16 -16
- package/acbr.lock.json +0 -0
- package/dist/cli.js +39 -13
- package/dist/cli.js.map +2 -2
- package/dist/contract.json +0 -0
- package/dist/index.cjs +36 -11
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +36 -11
- package/dist/index.js.map +2 -2
- package/dist/runtime.cjs +36 -11
- package/dist/runtime.cjs.map +2 -2
- package/dist/runtime.js +36 -11
- package/dist/runtime.js.map +2 -2
- package/dist/types/generated/acbr/index.d.ts +1 -0
- package/dist/types/generated/acbr/models.d.ts +0 -0
- package/dist/types/src/cli.d.ts +0 -0
- package/dist/types/src/config.d.ts +0 -0
- package/dist/types/src/contract-help.d.ts +0 -0
- package/dist/types/src/diagnostics.d.ts +2 -0
- package/dist/types/src/generator.d.ts +0 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/native-build.d.ts +1 -0
- package/dist/types/src/offline-check.d.ts +0 -0
- package/dist/types/src/offline-fixture.d.ts +0 -0
- package/dist/types/src/runtime.d.ts +1 -0
- package/dist/types/src/source.d.ts +0 -0
- package/dist/types/src/types.d.ts +4 -2
- package/docs/API.md +116 -90
- package/docs/ARQUITETURA.md +19 -15
- package/docs/CI.md +60 -60
- package/docs/CLI.md +1 -1
- package/docs/DEPENDENCIAS-NATIVAS.md +93 -93
- package/docs/VALIDACAO.md +67 -67
- package/docs/examples/recuperar-xml.mjs +54 -0
- package/docs/licenses/fpc/COPYING.FPC +25 -25
- package/docs/licenses/fpc/COPYING.GPL.txt +340 -340
- package/docs/licenses/fpc/COPYING.LGPL.txt +504 -504
- package/docs/licenses/fpc/ORIGIN.json +21 -21
- package/docs/licenses/lazarus/COPYING.LGPL.txt +481 -481
- package/docs/licenses/lazarus/COPYING.modifiedLGPL.txt +26 -26
- package/docs/licenses/lazarus/COPYING.txt +46 -46
- package/docs/licenses/lazarus/ORIGIN.json +1 -1
- package/native/NativeMotor.pas +55 -3
- package/native/NativeProtocol.pas +3 -2
- package/native/NativeRecovery.pas +274 -0
- package/native/generated/AcbrModels.pas +0 -0
- package/package.json +76 -76
- package/scripts/build-native-deps.mjs +237 -237
- 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"}
|
package/native/NativeMotor.pas
CHANGED
|
@@ -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
|
|
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,'&','&',[rfReplaceAll]);
|
|
153
|
+
Result:=StringReplace(Result,'"','"',[rfReplaceAll]);
|
|
154
|
+
Result:=StringReplace(Result,'<','<',[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
|