Fix other warnings.
[cascardo/libreceita.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 decfile_parse_file(struct rnet_decfile *decfile);
80
81 static int decfile_parse_header(struct rnet_decfile *decfile)
82 {
83         char *buffer = get_header(decfile);
84         if (!buffer)
85                 return -EINVAL;
86         switch (strlen(buffer)) {
87         case RNET_HEADER_SIZE_2013:
88                 return parse_header_2013(decfile->header, buffer);
89         case RNET_HEADER_SIZE_2014:
90                 return parse_header_2014(decfile->header, buffer);
91         default:
92                 return -EINVAL;
93         }
94 }
95
96 static int decfile_parse(struct rnet_decfile *decfile)
97 {
98         char *buffer = NULL;
99         size_t len = 0;
100         int r;
101         while ((r = getline(&buffer, &len, decfile->file)) > 0) {
102                 r = append_line(decfile, buffer);
103                 if (r) {
104                         free(buffer);
105                         goto out;
106                 }
107                 buffer = NULL;
108                 len = 0;
109         }
110         if (!(r = decfile_parse_header(decfile)) && !(r = decfile_parse_file(decfile)))
111                 return 0;
112 out:
113         decfile_release_lines(decfile);
114         return r;
115 }
116
117 struct rnet_decfile * rnet_decfile_open(char *filename)
118 {
119         struct rnet_decfile *decfile;
120         int r = -ENOMEM;
121         decfile = malloc(sizeof(*decfile));
122         if (!decfile)
123                 return NULL;
124         decfile->header = pmhash_new();
125         if (!decfile->header)
126                 goto out_header;
127         decfile->message = rnet_message_new();
128         if (!decfile->message)
129                 goto out_message;
130         decfile->filename = strdup(filename);
131         if (!decfile->filename)
132                 goto out_filename;
133         decfile->file = fopen(filename, "r");
134         if (!decfile->file)
135                 goto out_file;
136         decfile->lines_len = 0;
137         decfile->lines = NULL;
138         if ((r = decfile_parse(decfile)))
139                 goto out_parse;
140         return decfile;
141 out_parse:
142         fclose(decfile->file);
143 out_file:
144         free(decfile->filename);
145 out_filename:
146         rnet_message_del(decfile->message);
147 out_message:
148         pmhash_del(decfile->header);
149 out_header:
150         free(decfile);
151         errno = -r;
152         return NULL;
153 }
154
155 void rnet_decfile_close(struct rnet_decfile *decfile)
156 {
157         decfile_release_lines(decfile);
158         fclose(decfile->file);
159         free(decfile->filename);
160         free(decfile);
161 }
162
163 static char * get_header(struct rnet_decfile *decfile)
164 {
165         int i;
166         for (i = 0; i < decfile->lines_len; i++) {
167                 if (!strncmp(decfile->lines[i], "IRPF", 4)) {
168                         return decfile->lines[i];
169                 }
170         }
171         return NULL;
172 }
173
174 static int parse_header_2014(struct pmhash *hash, char *buffer)
175 {
176         int r;
177         char *p = buffer;
178         char *key;
179         char *val;
180         char *tail;
181
182 #define parse(field, sz) \
183         r = -ENOMEM; \
184         val = malloc(sz + 1); \
185         if (!val) \
186                 goto out_val; \
187         val[sz] = 0; \
188         memcpy(val, p, sz); \
189         p += sz; \
190         key = strdup(field); \
191         if (!key) \
192                 goto out_key; \
193         if (pmhash_add(&hash, key, val)) \
194                 goto out_add;
195
196         parse("sistema", 8);
197         parse("exerc", 4);
198         if (strcmp(val, "2014")) {
199                 r = -EINVAL;
200                 goto out_val;
201         }
202         parse("ano", 4);
203         parse("codigo_recnet", 4);
204         parse("in_ret", 1);
205         parse("cpf", 11);
206         parse("filler", 3);
207         parse("tipo_ni", 1);
208         parse("nr_versao", 3);
209         parse("nome", 60);
210         parse("uf", 2);
211         parse("hash", 10);
212
213         if (p - buffer != RNET_HEADER_HEAD_2014) {
214                 fprintf(stderr, "RNET_HEADER_HEAD_2014 in decfile.h needs to be adjusted to %ti\n", p - buffer);
215                 goto out_val;
216         }
217
218         parse("in_cert", 1);
219         parse("dt_nasc", 8);
220         parse("in_comp", 1);
221         parse("in_res", 1);
222         parse("in_gerada", 1);
223         parse("nr_recibo_anterior", 10);
224         parse("in_pgd", 1);
225         parse("so", 14);
226         parse("versao_so", 7);
227         parse("jvm", 9);
228         parse("nr_recibo", 10);
229         parse("municipio", 4);
230         parse("conjuge", 11);
231         parse("obrig", 1);
232         parse("impdevido", 13);
233         parse("nr_recibo", 10);
234         parse("in_seg", 1);
235         parse("imppago", 2);
236         parse("impant", 1);
237         parse("mudend", 1);
238         parse("cep", 8);
239         parse("debito", 1);
240         parse("banco", 3);
241         parse("agencia", 4);
242         parse("filler", 1);
243         parse("data_julgado", 8);
244         parse("imppagar", 13);
245         parse("tribfonte", 1);
246         parse("cpfrra", 11);
247         parse("trib_rra", 1);
248         parse("cpf_rra2", 11);
249         parse("trib_3rra", 1);
250         parse("cpf_rra3", 11);
251         parse("trib_4rra", 1);
252         parse("cpf_rra4", 11);
253         parse("vr_doacao", 13);
254         parse("cnpj1", 14);
255         parse("cnpj2", 14);
256         parse("cnpj3", 14);
257         parse("cnpj4", 14);
258         parse("cpf_dep1", 11);
259         parse("dnas_dep1", 8);
260         parse("cpf_dep2", 11);
261         parse("dnas_dep2", 8);
262         parse("cpf_dep3", 11);
263         parse("dnas_dep3", 8);
264         parse("cpf_dep4", 11);
265         parse("dnas_dep4", 8);
266         parse("cpf_dep5", 11);
267         parse("dnas_dep5", 8);
268         parse("cpf_dep6", 11);
269         parse("dnas_dep6", 8);
270         parse("cnpj_med1", 14);
271         parse("cnpj_med2", 14);
272         parse("cpf_alim", 11);
273         parse("cpf_invent", 11);
274         parse("municipio", 40);
275         parse("contribuinte", 60);
276         parse("cpf_empregada", 11);
277         parse("hashcode", 12);
278         parse("data_nao_residente", 8);
279         parse("cpf_procurador", 11);
280         parse("obrigatoriedade", 3);
281         parse("rendtrib", 13);
282         parse("cnpj_prev", 14);
283         parse("cnpj_prev2", 14);
284         parse("vr_totisentos", 13);
285         parse("vr_totexclusivo", 13);
286         parse("vr_totpagamentos", 13);
287         parse("nr_conta", 13);
288         parse("nr_dv_conta", 2);
289         parse("in_dv_conta", 1);
290
291         tail = p;
292
293         parse("versaotestpgd", 3);
294         parse("controle", 10);
295
296         if (*p++ != '\r') {
297                 fprintf(stderr,
298                         "missing CR at the %tith header character\n",
299                         p - buffer);
300                 goto out_val;
301         } else if (*p++ != '\n') {
302                 fprintf(stderr,
303                         "missing LF at the %tith header character\n",
304                         p - buffer);
305                 goto out_val;
306         } else if (*p != 0) {
307                 fprintf(stderr,
308                         "missing NUL at the %tith header character\n",
309                         p - buffer);
310                 goto out_val;
311         } else if (p - buffer != RNET_HEADER_SIZE_2014) {
312                 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);
313                 goto out_val;
314         } else if (p - tail != RNET_HEADER_TAIL_2014) {
315                 fprintf(stderr, "RNET_HEADER_TAIL_2014 in decfile.h needs to be adjusted to %ti\n", p - tail);
316                 goto out_val;
317         }
318
319         return 0;
320 out_add:
321         free(key);
322 out_key:
323         free(val);
324 out_val:
325         return r;
326 }
327
328 static int parse_header_2013(struct pmhash *hash, char *buffer)
329 {
330         int r;
331         char *p = buffer;
332         char *key;
333         char *val;
334         char *tail;
335
336 #define parse(field, sz) \
337         r = -ENOMEM; \
338         val = malloc(sz + 1); \
339         if (!val) \
340                 goto out_val; \
341         val[sz] = 0; \
342         memcpy(val, p, sz); \
343         p += sz; \
344         key = strdup(field); \
345         if (!key) \
346                 goto out_key; \
347         if (pmhash_add(&hash, key, val)) \
348                 goto out_add;
349
350         parse("sistema", 8);
351         parse("exerc", 4);
352         if (strcmp(val, "2013")) {
353                 r = -EINVAL;
354                 goto out_val;
355         }
356         parse("ano", 4);
357         parse("codigo_recnet", 4);
358         parse("in_ret", 1);
359         parse("cpf", 11);
360         parse("filler", 3);
361         parse("tipo_ni", 1);
362         parse("nr_versao", 3);
363         parse("nome", 60);
364         parse("uf", 2);
365         parse("hash", 10);
366
367         if (p - buffer != RNET_HEADER_HEAD_2013) {
368                 fprintf(stderr, "RNET_HEADER_HEAD_2013 in decfile.h needs to be adjusted to %ti\n", p - buffer);
369                 goto out_val;
370         }
371
372         parse("in_cert", 1);
373         parse("dt_nasc", 8);
374         parse("in_comp", 1);
375         parse("in_res", 1);
376         parse("in_gerada", 1);
377         parse("nr_recibo_anterior", 10);
378         parse("in_pgd", 1);
379         parse("so", 14);
380         parse("versao_so", 7);
381         parse("jvm", 9);
382         parse("nr_recibo", 10);
383         parse("municipio", 4);
384         parse("conjuge", 11);
385         parse("obrig", 1);
386         parse("impdevido", 13);
387         parse("nr_recibo", 10);
388         parse("in_seg", 1);
389         parse("imppago", 2);
390         parse("impant", 1);
391         parse("mudend", 1);
392         parse("cep", 8);
393         parse("debito", 1);
394         parse("banco", 3);
395         parse("agencia", 4);
396         parse("filler", 1);
397         parse("data_julgado", 8);
398         parse("imppagar", 13);
399         parse("tribfonte", 1);
400         parse("cpfrra", 11);
401         parse("trib_rra", 1);
402         parse("cpf_rra2", 11);
403         parse("trib_3rra", 1);
404         parse("cpf_rra3", 11);
405         parse("vr_doacao", 13);
406         parse("cnpj1", 14);
407         parse("cnpj2", 14);
408         parse("cnpj3", 14);
409         parse("cnpj4", 14);
410         parse("cpf_dep1", 11);
411         parse("dnas_dep1", 8);
412         parse("cpf_dep2", 11);
413         parse("dnas_dep2", 8);
414         parse("cpf_dep3", 11);
415         parse("dnas_dep3", 8);
416         parse("cpf_dep4", 11);
417         parse("dnas_dep4", 8);
418         parse("cpf_dep5", 11);
419         parse("dnas_dep5", 8);
420         parse("cpf_dep6", 11);
421         parse("dnas_dep6", 8);
422         parse("cnpj_med1", 14);
423         parse("cnpj_med2", 14);
424         parse("cpf_alim", 11);
425         parse("cpf_invent", 11);
426         parse("municipio", 40);
427         parse("contribuinte", 60);
428         parse("cpf_empregada", 11);
429         parse("hashcode", 12);
430         parse("data_nao_residente", 8);
431         parse("cpf_procurador", 11);
432         parse("obrigatoriedade", 3);
433         parse("rendtrib", 13);
434         parse("cnpj_prev", 14);
435         parse("cnpj_prev2", 14);
436         parse("vr_totisentos", 13);
437         parse("vr_totexclusivo", 13);
438         parse("vr_totpagamentos", 13);
439
440         tail = p;
441
442         parse("versaotestpgd", 3);
443         parse("controle", 10);
444
445         if (*p++ != '\r') {
446                 fprintf(stderr,
447                         "missing CR at the %tith header character\n",
448                         p - buffer);
449                 goto out_val;
450         } else if (*p++ != '\n') {
451                 fprintf(stderr,
452                         "missing LF at the %tith header character\n",
453                         p - buffer);
454                 goto out_val;
455         } else if (*p != 0) {
456                 fprintf(stderr,
457                         "missing NUL at the %tith header character\n",
458                         p - buffer);
459                 goto out_val;
460         } else if (p - buffer != RNET_HEADER_SIZE_2013) {
461                 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);
462                 goto out_val;
463         } else if (p - tail != RNET_HEADER_TAIL_2013) {
464                 fprintf(stderr, "RNET_HEADER_TAIL_2013 in decfile.h needs to be adjusted to %ti\n", p - tail);
465                 goto out_val;
466         }
467
468         return 0;
469 out_add:
470         free(key);
471 out_key:
472         free(val);
473 out_val:
474         return r;
475 }
476
477 char *rnet_decfile_get_header_field(struct rnet_decfile *decfile, char *field)
478 {
479         return pmhash_get(decfile->header, field);
480 }
481
482 /* returns true if register is declaration and not a receipt */
483 static int decfile_reg_is_dec(char *line)
484 {
485         if (line[0] >= '0' && line[0] <= '9' &&
486             line[1] >= '0' && line[1] <= '9')
487                 return 1;
488         if (!strncmp(line, "IRPF    ", 8))
489                 return 1;
490         if (!strncmp(line, "T9", 2))
491                 return 1;
492         return 0;
493 }
494
495 /* strip a register from its control number and append it to message */
496 static int append_stripped_reg_ctrl(struct rnet_message **message, char *line)
497 {
498         size_t len;
499         struct rnet_message *msg = *message;
500         int growth;
501         if (!decfile_reg_is_dec(line))
502                 return 0;
503         len = strlen(line);
504         if (len < 12)
505                 return -EINVAL;
506         growth = msg->len + len - 10 - msg->alen;
507         if (growth > 0) {
508                 if (rnet_message_expand(message, growth))
509                         return -ENOMEM;
510                 msg = *message;
511         }
512         memcpy(&msg->buffer[msg->len], line, len - 12);
513         msg->buffer[msg->len + len - 12] = '\r';
514         msg->buffer[msg->len + len - 11] = '\n';
515         msg->len += len - 10;
516         return 0;
517 }
518
519 static int decfile_parse_file(struct rnet_decfile *decfile)
520 {
521         int i;
522         int r;
523         for (i = 0; i < decfile->lines_len; i++) {
524                 r = append_stripped_reg_ctrl(&decfile->message,
525                                                 decfile->lines[i]);
526                 if (r)
527                         return r;
528         }
529         return 0;
530 }
531
532 struct rnet_message * rnet_decfile_get_file(struct rnet_decfile *decfile)
533 {
534         return decfile->message;
535 }
536
537 char * rnet_decfile_get_file_hash(struct rnet_decfile *decfile)
538 {
539         char *hash;
540         size_t len;
541         if (gcry_md_test_algo(GCRY_MD_MD5))
542                 return NULL;
543         len = gcry_md_get_algo_dlen(GCRY_MD_MD5);
544         hash = malloc(len);
545         if (!hash)
546                 return NULL;
547         gcry_md_hash_buffer(GCRY_MD_MD5, hash, decfile->message->buffer,
548                                         decfile->message->len);
549         return hash;
550 }
551
552 char * rnet_decfile_get_header(struct rnet_decfile *decfile)
553 {
554         return get_header(decfile);
555 }