Release 2015.1
[cascardo/rnetclient.git] / decfile.c
1 /*
2  *  Copyright (C) 2012-2014  Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
3  *  Copyright (C) 2014  Alexandre Oliva <lxoliva@fsfla.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #define _GNU_SOURCE
21 #include "decfile.h"
22 #include <string.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <gcrypt.h>
28 #include "pmhash.h"
29 #include "rnet_message.h"
30
31 #ifndef MAX
32 #define MAX(a,b) (a >= b) ? a : b
33 #endif
34
35 struct rnet_decfile {
36         char *filename;
37         FILE *file;
38         char **lines;
39         int lines_len;
40         struct pmhash *header;
41         struct rnet_message *message;
42 };
43
44 /*
45  * line should be an allocated buffer given to append_line
46  * this means, free(line) will be called when decfile is released
47  */
48 static int append_line(struct rnet_decfile *decfile, char *line)
49 {
50         size_t len;
51         char **old_lines;
52         decfile->lines_len += 1;
53         len = sizeof(*decfile->lines) * decfile->lines_len;
54         old_lines = decfile->lines;
55         decfile->lines = realloc(decfile->lines, len);
56         if (!decfile->lines) {
57                 decfile->lines = old_lines;
58                 goto out;
59         }
60         decfile->lines[decfile->lines_len - 1] = line;
61         return 0;
62 out:
63         decfile->lines_len -= 1;
64         return -ENOMEM;
65 }
66
67 static void decfile_release_lines(struct rnet_decfile *decfile)
68 {
69         int i;
70         for (i = 0; i < decfile->lines_len; i++)
71                 free(decfile->lines[i]);
72         free(decfile->lines);
73         decfile->lines = NULL;
74 }
75
76 static char * get_header(struct rnet_decfile *decfile);
77 static int parse_header_2013(struct pmhash *hash, char *buffer);
78 static int parse_header_2014(struct pmhash *hash, char *buffer);
79 static int parse_header_2015(struct pmhash *hash, char *buffer);
80 static int decfile_parse_file(struct rnet_decfile *decfile);
81
82 static int decfile_parse_header(struct rnet_decfile *decfile)
83 {
84         char *buffer = get_header(decfile);
85         if (!buffer)
86                 return -EINVAL;
87         switch (strlen(buffer)) {
88         case RNET_HEADER_SIZE_2013:
89                 return parse_header_2013(decfile->header, buffer);
90         case RNET_HEADER_SIZE_2014:
91                 return parse_header_2014(decfile->header, buffer);
92         case RNET_HEADER_SIZE_2015:
93                 return parse_header_2015(decfile->header, buffer);
94         default:
95                 return -EINVAL;
96         }
97 }
98
99 static int decfile_parse(struct rnet_decfile *decfile)
100 {
101         char *buffer = NULL;
102         size_t len = 0;
103         int r;
104         while ((r = getline(&buffer, &len, decfile->file)) > 0) {
105                 r = append_line(decfile, buffer);
106                 if (r) {
107                         free(buffer);
108                         goto out;
109                 }
110                 buffer = NULL;
111                 len = 0;
112         }
113         if (!(r = decfile_parse_header(decfile)) && !(r = decfile_parse_file(decfile)))
114                 return 0;
115 out:
116         decfile_release_lines(decfile);
117         return r;
118 }
119
120 struct rnet_decfile * rnet_decfile_open(char *filename)
121 {
122         struct rnet_decfile *decfile;
123         int r = -ENOMEM;
124         decfile = malloc(sizeof(*decfile));
125         if (!decfile)
126                 return NULL;
127         decfile->header = pmhash_new();
128         if (!decfile->header)
129                 goto out_header;
130         decfile->message = rnet_message_new();
131         if (!decfile->message)
132                 goto out_message;
133         decfile->filename = strdup(filename);
134         if (!decfile->filename)
135                 goto out_filename;
136         decfile->file = fopen(filename, "r");
137         if (!decfile->file)
138                 goto out_file;
139         decfile->lines_len = 0;
140         decfile->lines = NULL;
141         if ((r = decfile_parse(decfile)))
142                 goto out_parse;
143         return decfile;
144 out_parse:
145         fclose(decfile->file);
146 out_file:
147         free(decfile->filename);
148 out_filename:
149         rnet_message_del(decfile->message);
150 out_message:
151         pmhash_del(decfile->header);
152 out_header:
153         free(decfile);
154         errno = -r;
155         return NULL;
156 }
157
158 void rnet_decfile_close(struct rnet_decfile *decfile)
159 {
160         decfile_release_lines(decfile);
161         fclose(decfile->file);
162         free(decfile->filename);
163         free(decfile);
164 }
165
166 static char * get_header(struct rnet_decfile *decfile)
167 {
168         int i;
169         for (i = 0; i < decfile->lines_len; i++) {
170                 if (!strncmp(decfile->lines[i], "IRPF", 4)) {
171                         return decfile->lines[i];
172                 }
173         }
174         return NULL;
175 }
176
177 static int parse_header_2015(struct pmhash *hash, char *buffer)
178 {
179         int r;
180         char *p = buffer;
181         char *key;
182         char *val;
183         char *tail;
184
185 #define parse(field, sz) \
186         r = -ENOMEM; \
187         val = malloc(sz + 1); \
188         if (!val) \
189                 goto out_val; \
190         val[sz] = 0; \
191         memcpy(val, p, sz); \
192         p += sz; \
193         key = strdup(field); \
194         if (!key) \
195                 goto out_key; \
196         if (pmhash_add(&hash, key, val)) \
197                 goto out_add;
198
199         parse("sistema", 8);
200         parse("exerc", 4);
201         if (strcmp(val, "2015")) {
202                 r = -EINVAL;
203                 goto out_val;
204         }
205         parse("ano", 4);
206         parse("codigo_recnet", 4);
207         parse("in_ret", 1);
208         parse("cpf", 11);
209         parse("filler", 3);
210         parse("tipo_ni", 1);
211         parse("nr_versao", 3);
212         parse("nome", 60);
213         parse("uf", 2);
214         parse("hash", 10);
215
216         if (p - buffer != RNET_HEADER_HEAD_2015) {
217                 fprintf(stderr, "RNET_HEADER_HEAD_2015 in decfile.h needs to be adjusted to %ti\n", p - buffer);
218                 goto out_val;
219         }
220
221         parse("in_cert", 1);
222         parse("dt_nasc", 8);
223         parse("in_comp", 1);
224         parse("in_res", 1);
225         parse("in_gerada", 1);
226         parse("nr_recibo_anterior", 10);
227         parse("in_pgd", 1);
228         parse("so", 14);
229         parse("versao_so", 7);
230         parse("jvm", 9);
231         parse("nr_recibo", 10);
232         parse("municipio", 4);
233         parse("conjuge", 11);
234         parse("obrig", 1);
235         parse("impdevido", 13);
236         parse("nr_recibo", 10);
237         parse("in_seg", 1);
238         parse("imppago", 2);
239         parse("impant", 1);
240         parse("mudend", 1);
241         parse("cep", 8);
242         parse("debito", 1);
243         parse("banco", 3);
244         parse("agencia", 4);
245         parse("filler", 1);
246         parse("data_julgado", 8);
247         parse("imppagar", 13);
248         parse("tribfonte", 1);
249         parse("cpfrra", 11);
250         parse("trib_rra", 1);
251         parse("cpf_rra2", 11);
252         parse("trib_3rra", 1);
253         parse("cpf_rra3", 11);
254         parse("trib_4rra", 1);
255         parse("cpf_rra4", 11);
256         parse("vr_doacao", 13);
257         parse("cnpj1", 14);
258         parse("cnpj2", 14);
259         parse("cnpj3", 14);
260         parse("cnpj4", 14);
261         parse("cpf_dep1", 11);
262         parse("dnas_dep1", 8);
263         parse("cpf_dep2", 11);
264         parse("dnas_dep2", 8);
265         parse("cpf_dep3", 11);
266         parse("dnas_dep3", 8);
267         parse("cpf_dep4", 11);
268         parse("dnas_dep4", 8);
269         parse("cpf_dep5", 11);
270         parse("dnas_dep5", 8);
271         parse("cpf_dep6", 11);
272         parse("dnas_dep6", 8);
273         parse("cnpj_med1", 14);
274         parse("cnpj_med2", 14);
275         parse("cpf_alim", 11);
276         parse("cpf_invent", 11);
277         parse("municipio", 40);
278         parse("contribuinte", 60);
279         parse("filler", 11);
280         parse("mac", 12);
281         parse("data_nao_residente", 8);
282         parse("cpf_procurador", 11);
283         parse("obrigatoriedade", 3);
284         parse("rendtrib", 13);
285         parse("cnpj_prev", 14);
286         parse("cnpj_prev2", 14);
287         parse("vr_totisentos", 13);
288         parse("vr_totexclusivo", 13);
289         parse("vr_totpagamentos", 13);
290         parse("nr_conta", 13);
291         parse("nr_dv_conta", 2);
292         parse("in_dv_conta", 1);
293
294         parse("codnaturezaocup", 2);
295         parse("cpfdomestic@", 11);
296         parse("nitdomestic@", 11);
297         parse("cpfdomestic@2", 11);
298         parse("nitdomestic@2", 11);
299         parse("cpfdomestic@3", 11);
300         parse("nitdomestic@3", 11);
301         parse("deciniciada", 1);
302         parse("utilpgd", 1);
303         parse("utilapp", 1);
304         parse("utilonline", 1);
305         parse("utilrascunho", 1);
306         parse("utilprepreenchida", 1);
307         parse("utilfontes", 1);
308         parse("utilplanosaude", 1);
309         parse("utilrecuperar", 1);
310         parse("dectransmitida", 1);
311         tail = p;
312
313         parse("versaotestpgd", 3);
314         parse("controle", 10);
315
316         if (*p++ != '\r') {
317                 fprintf(stderr,
318                         "missing CR at the %tith header character\n",
319                         p - buffer);
320                 goto out_val;
321         } else if (*p++ != '\n') {
322                 fprintf(stderr,
323                         "missing LF at the %tith header character\n",
324                         p - buffer);
325                 goto out_val;
326         } else if (*p != 0) {
327                 fprintf(stderr,
328                         "missing NUL at the %tith header character\n",
329                         p - buffer);
330                 goto out_val;
331         } else if (p - buffer != RNET_HEADER_SIZE_2015) {
332                 fprintf(stderr, "RNET_HEADER_SIZE_2015 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
333                 goto out_val;
334         } else if (p - tail != RNET_HEADER_TAIL_2015) {
335                 fprintf(stderr, "RNET_HEADER_TAIL_2015 in decfile.h needs to be adjusted to %ti\n", p - tail);
336                 goto out_val;
337         }
338
339         return 0;
340 out_add:
341         free(key);
342 out_key:
343         free(val);
344 out_val:
345         return r;
346 }
347
348 static int parse_header_2014(struct pmhash *hash, char *buffer)
349 {
350         int r;
351         char *p = buffer;
352         char *key;
353         char *val;
354         char *tail;
355
356 #define parse(field, sz) \
357         r = -ENOMEM; \
358         val = malloc(sz + 1); \
359         if (!val) \
360                 goto out_val; \
361         val[sz] = 0; \
362         memcpy(val, p, sz); \
363         p += sz; \
364         key = strdup(field); \
365         if (!key) \
366                 goto out_key; \
367         if (pmhash_add(&hash, key, val)) \
368                 goto out_add;
369
370         parse("sistema", 8);
371         parse("exerc", 4);
372         if (strcmp(val, "2014")) {
373                 r = -EINVAL;
374                 goto out_val;
375         }
376         parse("ano", 4);
377         parse("codigo_recnet", 4);
378         parse("in_ret", 1);
379         parse("cpf", 11);
380         parse("filler", 3);
381         parse("tipo_ni", 1);
382         parse("nr_versao", 3);
383         parse("nome", 60);
384         parse("uf", 2);
385         parse("hash", 10);
386
387         if (p - buffer != RNET_HEADER_HEAD_2014) {
388                 fprintf(stderr, "RNET_HEADER_HEAD_2014 in decfile.h needs to be adjusted to %ti\n", p - buffer);
389                 goto out_val;
390         }
391
392         parse("in_cert", 1);
393         parse("dt_nasc", 8);
394         parse("in_comp", 1);
395         parse("in_res", 1);
396         parse("in_gerada", 1);
397         parse("nr_recibo_anterior", 10);
398         parse("in_pgd", 1);
399         parse("so", 14);
400         parse("versao_so", 7);
401         parse("jvm", 9);
402         parse("nr_recibo", 10);
403         parse("municipio", 4);
404         parse("conjuge", 11);
405         parse("obrig", 1);
406         parse("impdevido", 13);
407         parse("nr_recibo", 10);
408         parse("in_seg", 1);
409         parse("imppago", 2);
410         parse("impant", 1);
411         parse("mudend", 1);
412         parse("cep", 8);
413         parse("debito", 1);
414         parse("banco", 3);
415         parse("agencia", 4);
416         parse("filler", 1);
417         parse("data_julgado", 8);
418         parse("imppagar", 13);
419         parse("tribfonte", 1);
420         parse("cpfrra", 11);
421         parse("trib_rra", 1);
422         parse("cpf_rra2", 11);
423         parse("trib_3rra", 1);
424         parse("cpf_rra3", 11);
425         parse("trib_4rra", 1);
426         parse("cpf_rra4", 11);
427         parse("vr_doacao", 13);
428         parse("cnpj1", 14);
429         parse("cnpj2", 14);
430         parse("cnpj3", 14);
431         parse("cnpj4", 14);
432         parse("cpf_dep1", 11);
433         parse("dnas_dep1", 8);
434         parse("cpf_dep2", 11);
435         parse("dnas_dep2", 8);
436         parse("cpf_dep3", 11);
437         parse("dnas_dep3", 8);
438         parse("cpf_dep4", 11);
439         parse("dnas_dep4", 8);
440         parse("cpf_dep5", 11);
441         parse("dnas_dep5", 8);
442         parse("cpf_dep6", 11);
443         parse("dnas_dep6", 8);
444         parse("cnpj_med1", 14);
445         parse("cnpj_med2", 14);
446         parse("cpf_alim", 11);
447         parse("cpf_invent", 11);
448         parse("municipio", 40);
449         parse("contribuinte", 60);
450         parse("cpf_empregada", 11);
451         parse("hashcode", 12);
452         parse("data_nao_residente", 8);
453         parse("cpf_procurador", 11);
454         parse("obrigatoriedade", 3);
455         parse("rendtrib", 13);
456         parse("cnpj_prev", 14);
457         parse("cnpj_prev2", 14);
458         parse("vr_totisentos", 13);
459         parse("vr_totexclusivo", 13);
460         parse("vr_totpagamentos", 13);
461         parse("nr_conta", 13);
462         parse("nr_dv_conta", 2);
463         parse("in_dv_conta", 1);
464
465         tail = p;
466
467         parse("versaotestpgd", 3);
468         parse("controle", 10);
469
470         if (*p++ != '\r') {
471                 fprintf(stderr,
472                         "missing CR at the %tith header character\n",
473                         p - buffer);
474                 goto out_val;
475         } else if (*p++ != '\n') {
476                 fprintf(stderr,
477                         "missing LF at the %tith header character\n",
478                         p - buffer);
479                 goto out_val;
480         } else if (*p != 0) {
481                 fprintf(stderr,
482                         "missing NUL at the %tith header character\n",
483                         p - buffer);
484                 goto out_val;
485         } else if (p - buffer != RNET_HEADER_SIZE_2014) {
486                 fprintf(stderr, "RNET_HEADER_SIZE_2014 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
487                 goto out_val;
488         } else if (p - tail != RNET_HEADER_TAIL_2014) {
489                 fprintf(stderr, "RNET_HEADER_TAIL_2014 in decfile.h needs to be adjusted to %ti\n", p - tail);
490                 goto out_val;
491         }
492
493         return 0;
494 out_add:
495         free(key);
496 out_key:
497         free(val);
498 out_val:
499         return r;
500 }
501
502 static int parse_header_2013(struct pmhash *hash, char *buffer)
503 {
504         int r;
505         char *p = buffer;
506         char *key;
507         char *val;
508         char *tail;
509
510 #define parse(field, sz) \
511         r = -ENOMEM; \
512         val = malloc(sz + 1); \
513         if (!val) \
514                 goto out_val; \
515         val[sz] = 0; \
516         memcpy(val, p, sz); \
517         p += sz; \
518         key = strdup(field); \
519         if (!key) \
520                 goto out_key; \
521         if (pmhash_add(&hash, key, val)) \
522                 goto out_add;
523
524         parse("sistema", 8);
525         parse("exerc", 4);
526         if (strcmp(val, "2013")) {
527                 r = -EINVAL;
528                 goto out_val;
529         }
530         parse("ano", 4);
531         parse("codigo_recnet", 4);
532         parse("in_ret", 1);
533         parse("cpf", 11);
534         parse("filler", 3);
535         parse("tipo_ni", 1);
536         parse("nr_versao", 3);
537         parse("nome", 60);
538         parse("uf", 2);
539         parse("hash", 10);
540
541         if (p - buffer != RNET_HEADER_HEAD_2013) {
542                 fprintf(stderr, "RNET_HEADER_HEAD_2013 in decfile.h needs to be adjusted to %ti\n", p - buffer);
543                 goto out_val;
544         }
545
546         parse("in_cert", 1);
547         parse("dt_nasc", 8);
548         parse("in_comp", 1);
549         parse("in_res", 1);
550         parse("in_gerada", 1);
551         parse("nr_recibo_anterior", 10);
552         parse("in_pgd", 1);
553         parse("so", 14);
554         parse("versao_so", 7);
555         parse("jvm", 9);
556         parse("nr_recibo", 10);
557         parse("municipio", 4);
558         parse("conjuge", 11);
559         parse("obrig", 1);
560         parse("impdevido", 13);
561         parse("nr_recibo", 10);
562         parse("in_seg", 1);
563         parse("imppago", 2);
564         parse("impant", 1);
565         parse("mudend", 1);
566         parse("cep", 8);
567         parse("debito", 1);
568         parse("banco", 3);
569         parse("agencia", 4);
570         parse("filler", 1);
571         parse("data_julgado", 8);
572         parse("imppagar", 13);
573         parse("tribfonte", 1);
574         parse("cpfrra", 11);
575         parse("trib_rra", 1);
576         parse("cpf_rra2", 11);
577         parse("trib_3rra", 1);
578         parse("cpf_rra3", 11);
579         parse("vr_doacao", 13);
580         parse("cnpj1", 14);
581         parse("cnpj2", 14);
582         parse("cnpj3", 14);
583         parse("cnpj4", 14);
584         parse("cpf_dep1", 11);
585         parse("dnas_dep1", 8);
586         parse("cpf_dep2", 11);
587         parse("dnas_dep2", 8);
588         parse("cpf_dep3", 11);
589         parse("dnas_dep3", 8);
590         parse("cpf_dep4", 11);
591         parse("dnas_dep4", 8);
592         parse("cpf_dep5", 11);
593         parse("dnas_dep5", 8);
594         parse("cpf_dep6", 11);
595         parse("dnas_dep6", 8);
596         parse("cnpj_med1", 14);
597         parse("cnpj_med2", 14);
598         parse("cpf_alim", 11);
599         parse("cpf_invent", 11);
600         parse("municipio", 40);
601         parse("contribuinte", 60);
602         parse("cpf_empregada", 11);
603         parse("hashcode", 12);
604         parse("data_nao_residente", 8);
605         parse("cpf_procurador", 11);
606         parse("obrigatoriedade", 3);
607         parse("rendtrib", 13);
608         parse("cnpj_prev", 14);
609         parse("cnpj_prev2", 14);
610         parse("vr_totisentos", 13);
611         parse("vr_totexclusivo", 13);
612         parse("vr_totpagamentos", 13);
613
614         tail = p;
615
616         parse("versaotestpgd", 3);
617         parse("controle", 10);
618
619         if (*p++ != '\r') {
620                 fprintf(stderr,
621                         "missing CR at the %tith header character\n",
622                         p - buffer);
623                 goto out_val;
624         } else if (*p++ != '\n') {
625                 fprintf(stderr,
626                         "missing LF at the %tith header character\n",
627                         p - buffer);
628                 goto out_val;
629         } else if (*p != 0) {
630                 fprintf(stderr,
631                         "missing NUL at the %tith header character\n",
632                         p - buffer);
633                 goto out_val;
634         } else if (p - buffer != RNET_HEADER_SIZE_2013) {
635                 fprintf(stderr, "RNET_HEADER_SIZE_2013 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
636                 goto out_val;
637         } else if (p - tail != RNET_HEADER_TAIL_2013) {
638                 fprintf(stderr, "RNET_HEADER_TAIL_2013 in decfile.h needs to be adjusted to %ti\n", p - tail);
639                 goto out_val;
640         }
641
642         return 0;
643 out_add:
644         free(key);
645 out_key:
646         free(val);
647 out_val:
648         return r;
649 }
650
651 char *rnet_decfile_get_header_field(struct rnet_decfile *decfile, char *field)
652 {
653         return pmhash_get(decfile->header, field);
654 }
655
656 /* returns true if register is declaration and not a receipt */
657 static int decfile_reg_is_dec(char *line)
658 {
659         if (line[0] >= '0' && line[0] <= '9' &&
660             line[1] >= '0' && line[1] <= '9')
661                 return 1;
662         if (!strncmp(line, "IRPF    ", 8))
663                 return 1;
664         if (!strncmp(line, "T9", 2))
665                 return 1;
666         return 0;
667 }
668
669 /* strip a register from its control number and append it to message */
670 static int append_stripped_reg_ctrl(struct rnet_message **message, char *line)
671 {
672         size_t len;
673         struct rnet_message *msg = *message;
674         int growth;
675         if (!decfile_reg_is_dec(line))
676                 return 0;
677         len = strlen(line);
678         if (len < 12)
679                 return -EINVAL;
680         growth = msg->len + len - 10 - msg->alen;
681         if (growth > 0) {
682                 if (rnet_message_expand(message, growth))
683                         return -ENOMEM;
684                 msg = *message;
685         }
686         memcpy(&msg->buffer[msg->len], line, len - 12);
687         msg->buffer[msg->len + len - 12] = '\r';
688         msg->buffer[msg->len + len - 11] = '\n';
689         msg->len += len - 10;
690         return 0;
691 }
692
693 static int decfile_parse_file(struct rnet_decfile *decfile)
694 {
695         int i;
696         int r;
697         for (i = 0; i < decfile->lines_len; i++) {
698                 r = append_stripped_reg_ctrl(&decfile->message,
699                                                 decfile->lines[i]);
700                 if (r)
701                         return r;
702         }
703         return 0;
704 }
705
706 struct rnet_message * rnet_decfile_get_file(struct rnet_decfile *decfile)
707 {
708         return decfile->message;
709 }
710
711 char * rnet_decfile_get_file_hash(struct rnet_decfile *decfile)
712 {
713         char *hash;
714         size_t len;
715         if (gcry_md_test_algo(GCRY_MD_MD5))
716                 return NULL;
717         len = gcry_md_get_algo_dlen(GCRY_MD_MD5);
718         hash = malloc(len);
719         if (!hash)
720                 return NULL;
721         gcry_md_hash_buffer(GCRY_MD_MD5, hash, decfile->message->buffer,
722                                         decfile->message->len);
723         return hash;
724 }
725
726 char * rnet_decfile_get_header(struct rnet_decfile *decfile)
727 {
728         return get_header(decfile);
729 }