Document operating system as GNU.
[cascardo/rnetclient.git] / decfile.c
1 /*
2  *  Copyright (C) 2012-2013  Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #define _GNU_SOURCE
20 #include "decfile.h"
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <gcrypt.h>
27 #include "pmhash.h"
28 #include "rnet_message.h"
29
30 #ifndef MAX
31 #define MAX(a,b) (a >= b) ? a : b
32 #endif
33
34 struct rnet_decfile {
35         char *filename;
36         FILE *file;
37         char **lines;
38         int lines_len;
39         struct pmhash *header;
40         struct rnet_message *message;
41 };
42
43 /*
44  * line should be an allocated buffer given to append_line
45  * this means, free(line) will be called when decfile is released
46  */
47 static int append_line(struct rnet_decfile *decfile, char *line)
48 {
49         size_t len;
50         char **old_lines;
51         decfile->lines_len += 1;
52         len = sizeof(*decfile->lines) * decfile->lines_len;
53         old_lines = decfile->lines;
54         decfile->lines = realloc(decfile->lines, len);
55         if (!decfile->lines) {
56                 decfile->lines = old_lines;
57                 goto out;
58         }
59         decfile->lines[decfile->lines_len - 1] = line;
60         return 0;
61 out:
62         decfile->lines_len -= 1;
63         return -ENOMEM;
64 }
65
66 static void decfile_release_lines(struct rnet_decfile *decfile)
67 {
68         int i;
69         for (i = 0; i < decfile->lines_len; i++)
70                 free(decfile->lines[i]);
71         free(decfile->lines);
72         decfile->lines = NULL;
73 }
74
75 static char * get_header(struct rnet_decfile *decfile);
76 static int parse_header_2013(struct pmhash *hash, char *buffer);
77 static int parse_header_2014(struct pmhash *hash, char *buffer);
78 static int decfile_parse_file(struct rnet_decfile *decfile);
79
80 static int decfile_parse_header(struct rnet_decfile *decfile)
81 {
82         char *buffer = get_header(decfile);
83         if (!buffer)
84                 return -EINVAL;
85         switch (strlen(buffer)) {
86         case RNET_HEADER_SIZE_2013:
87                 return parse_header_2013(decfile->header, buffer);
88         case RNET_HEADER_SIZE_2014:
89                 return parse_header_2014(decfile->header, buffer);
90         default:
91                 return -EINVAL;
92         }
93 }
94
95 static int decfile_parse(struct rnet_decfile *decfile)
96 {
97         char *buffer = NULL;
98         size_t len = 0;
99         int r;
100         while ((r = getline(&buffer, &len, decfile->file)) > 0) {
101                 r = append_line(decfile, buffer);
102                 if (r) {
103                         free(buffer);
104                         goto out;
105                 }
106                 buffer = NULL;
107                 len = 0;
108         }
109         if (!(r = decfile_parse_header(decfile)) && !(r = decfile_parse_file(decfile)))
110                 return 0;
111 out:
112         decfile_release_lines(decfile);
113         return r;
114 }
115
116 struct rnet_decfile * rnet_decfile_open(char *filename)
117 {
118         struct rnet_decfile *decfile;
119         int r = -ENOMEM;
120         decfile = malloc(sizeof(*decfile));
121         if (!decfile)
122                 return NULL;
123         decfile->header = pmhash_new();
124         if (!decfile->header)
125                 goto out_header;
126         decfile->message = rnet_message_new();
127         if (!decfile->message)
128                 goto out_message;
129         decfile->filename = strdup(filename);
130         if (!decfile->filename)
131                 goto out_filename;
132         decfile->file = fopen(filename, "r");
133         if (!decfile->file)
134                 goto out_file;
135         decfile->lines_len = 0;
136         decfile->lines = NULL;
137         if ((r = decfile_parse(decfile)))
138                 goto out_parse;
139         return decfile;
140 out_parse:
141         fclose(decfile->file);
142 out_file:
143         free(decfile->filename);
144 out_filename:
145         rnet_message_del(decfile->message);
146 out_message:
147         pmhash_del(decfile->header);
148 out_header:
149         free(decfile);
150         errno = -r;
151         return NULL;
152 }
153
154 void rnet_decfile_close(struct rnet_decfile *decfile)
155 {
156         decfile_release_lines(decfile);
157         fclose(decfile->file);
158         free(decfile->filename);
159         free(decfile);
160 }
161
162 static char * get_header(struct rnet_decfile *decfile)
163 {
164         int i;
165         for (i = 0; i < decfile->lines_len; i++) {
166                 if (!strncmp(decfile->lines[i], "IRPF", 4)) {
167                         return decfile->lines[i];
168                 }
169         }
170         return NULL;
171 }
172
173 static int parse_header_2014(struct pmhash *hash, char *buffer)
174 {
175         int r;
176         char *p = buffer;
177         char *key;
178         char *val;
179
180 #define parse(field, sz) \
181         r = -ENOMEM; \
182         val = malloc(sz + 1); \
183         if (!val) \
184                 goto out_val; \
185         val[sz] = 0; \
186         memcpy(val, p, sz); \
187         p += sz; \
188         key = strdup(field); \
189         if (!key) \
190                 goto out_key; \
191         if (pmhash_add(&hash, key, val)) \
192                 goto out_add;
193
194         parse("sistema", 8);
195         parse("exerc", 4);
196         if (strcmp(val, "2014")) {
197                 r = -EINVAL;
198                 goto out_val;
199         }
200         parse("ano", 4);
201         parse("codigo_recnet", 4);
202         parse("in_ret", 1);
203         parse("cpf", 11);
204         parse("filler", 3);
205         parse("tipo_ni", 1);
206         parse("nr_versao", 3);
207         parse("nome", 60);
208         parse("uf", 2);
209         parse("hash", 10);
210         parse("in_cert", 1);
211         parse("dt_nasc", 8);
212         parse("in_comp", 1);
213         parse("in_res", 1);
214         parse("in_gerada", 1);
215         parse("nr_recibo_anterior", 10);
216         parse("in_pgd", 1);
217         parse("so", 14);
218         parse("versao_so", 7);
219         parse("jvm", 9);
220         parse("nr_recibo", 10);
221         parse("municipio", 4);
222         parse("conjuge", 11);
223         parse("obrig", 1);
224         parse("impdevido", 13);
225         parse("nr_recibo", 10);
226         parse("in_seg", 1);
227         parse("imppago", 2);
228         parse("impant", 1);
229         parse("mudend", 1);
230         parse("cep", 8);
231         parse("debito", 1);
232         parse("banco", 3);
233         parse("agencia", 4);
234         parse("filler", 1);
235         parse("data_julgado", 8);
236         parse("imppagar", 13);
237         parse("tribfonte", 1);
238         parse("cpfrra", 11);
239         parse("trib_rra", 1);
240         parse("cpf_rra2", 11);
241         parse("trib_3rra", 1);
242         parse("cpf_rra3", 11);
243         parse("trib_4rra", 1);
244         parse("cpf_rra4", 11);
245         parse("vr_doacao", 13);
246         parse("cnpj1", 14);
247         parse("cnpj2", 14);
248         parse("cnpj3", 14);
249         parse("cnpj4", 14);
250         parse("cpf_dep1", 11);
251         parse("dnas_dep1", 8);
252         parse("cpf_dep2", 11);
253         parse("dnas_dep2", 8);
254         parse("cpf_dep3", 11);
255         parse("dnas_dep3", 8);
256         parse("cpf_dep4", 11);
257         parse("dnas_dep4", 8);
258         parse("cpf_dep5", 11);
259         parse("dnas_dep5", 8);
260         parse("cpf_dep6", 11);
261         parse("dnas_dep6", 8);
262         parse("cnpj_med1", 14);
263         parse("cnpj_med2", 14);
264         parse("cpf_alim", 11);
265         parse("cpf_invent", 11);
266         parse("municipio", 40);
267         parse("contribuinte", 60);
268         parse("cpf_empregada", 11);
269         parse("hashcode", 12);
270         parse("data_nao_residente", 8);
271         parse("cpf_procurador", 11);
272         parse("obrigatoriedade", 3);
273         parse("rendtrib", 13);
274         parse("cnpj_prev", 14);
275         parse("cnpj_prev2", 14);
276         parse("vr_totisentos", 13);
277         parse("vr_totexclusivo", 13);
278         parse("vr_totpagamentos", 13);
279         parse("nr_conta", 13);
280         parse("nr_dv_conta", 2);
281         parse("in_dv_conta", 1);
282         parse("versaotestpgd", 3);
283         parse("controle", 10);
284
285         return 0;
286 out_add:
287         free(key);
288 out_key:
289         free(val);
290 out_val:
291         return r;
292 }
293
294 static int parse_header_2013(struct pmhash *hash, char *buffer)
295 {
296         int r;
297         char *p = buffer;
298         char *key;
299         char *val;
300
301 #define parse(field, sz) \
302         r = -ENOMEM; \
303         val = malloc(sz + 1); \
304         if (!val) \
305                 goto out_val; \
306         val[sz] = 0; \
307         memcpy(val, p, sz); \
308         p += sz; \
309         key = strdup(field); \
310         if (!key) \
311                 goto out_key; \
312         if (pmhash_add(&hash, key, val)) \
313                 goto out_add;
314
315         parse("sistema", 8);
316         parse("exerc", 4);
317         if (strcmp(val, "2013")) {
318                 r = -EINVAL;
319                 goto out_val;
320         }
321         parse("ano", 4);
322         parse("codigo_recnet", 4);
323         parse("in_ret", 1);
324         parse("cpf", 11);
325         parse("filler", 3);
326         parse("tipo_ni", 1);
327         parse("nr_versao", 3);
328         parse("nome", 60);
329         parse("uf", 2);
330         parse("hash", 10);
331         parse("in_cert", 1);
332         parse("dt_nasc", 8);
333         parse("in_comp", 1);
334         parse("in_res", 1);
335         parse("in_gerada", 1);
336         parse("nr_recibo_anterior", 10);
337         parse("in_pgd", 1);
338         parse("so", 14);
339         parse("versao_so", 7);
340         parse("jvm", 9);
341         parse("nr_recibo", 10);
342         parse("municipio", 4);
343         parse("conjuge", 11);
344         parse("obrig", 1);
345         parse("impdevido", 13);
346         parse("nr_recibo", 10);
347         parse("in_seg", 1);
348         parse("imppago", 2);
349         parse("impant", 1);
350         parse("mudend", 1);
351         parse("cep", 8);
352         parse("debito", 1);
353         parse("banco", 3);
354         parse("agencia", 4);
355         parse("filler", 1);
356         parse("data_julgado", 8);
357         parse("imppagar", 13);
358         parse("tribfonte", 1);
359         parse("cpfrra", 11);
360         parse("trib_rra", 1);
361         parse("cpf_rra2", 11);
362         parse("trib_3rra", 1);
363         parse("cpf_rra3", 11);
364         parse("vr_doacao", 13);
365         parse("cnpj1", 14);
366         parse("cnpj2", 14);
367         parse("cnpj3", 14);
368         parse("cnpj4", 14);
369         parse("cpf_dep1", 11);
370         parse("dnas_dep1", 8);
371         parse("cpf_dep2", 11);
372         parse("dnas_dep2", 8);
373         parse("cpf_dep3", 11);
374         parse("dnas_dep3", 8);
375         parse("cpf_dep4", 11);
376         parse("dnas_dep4", 8);
377         parse("cpf_dep5", 11);
378         parse("dnas_dep5", 8);
379         parse("cpf_dep6", 11);
380         parse("dnas_dep6", 8);
381         parse("cnpj_med1", 14);
382         parse("cnpj_med2", 14);
383         parse("cpf_alim", 11);
384         parse("cpf_invent", 11);
385         parse("municipio", 40);
386         parse("contribuinte", 60);
387         parse("cpf_empregada", 11);
388         parse("hashcode", 12);
389         parse("data_nao_residente", 8);
390         parse("cpf_procurador", 11);
391         parse("obrigatoriedade", 3);
392         parse("rendtrib", 13);
393         parse("cnpj_prev", 14);
394         parse("cnpj_prev2", 14);
395         parse("vr_totisentos", 13);
396         parse("vr_totexclusivo", 13);
397         parse("vr_totpagamentos", 13);
398         parse("versaotestpgd", 3);
399         parse("controle", 10);
400
401         return 0;
402 out_add:
403         free(key);
404 out_key:
405         free(val);
406 out_val:
407         return r;
408 }
409
410 char *rnet_decfile_get_header_field(struct rnet_decfile *decfile, char *field)
411 {
412         return pmhash_get(decfile->header, field);
413 }
414
415 /* returns true if register is declaration and not a receipt */
416 static int decfile_reg_is_dec(char *line)
417 {
418         if (line[0] >= '0' && line[0] <= '9' &&
419             line[1] >= '0' && line[1] <= '9')
420                 return 1;
421         if (!strncmp(line, "IRPF    ", 8))
422                 return 1;
423         if (!strncmp(line, "T9", 2))
424                 return 1;
425         return 0;
426 }
427
428 /* strip a register from its control number and append it to message */
429 static int append_stripped_reg_ctrl(struct rnet_message **message, char *line)
430 {
431         size_t len;
432         struct rnet_message *msg = *message;
433         int growth;
434         if (!decfile_reg_is_dec(line))
435                 return 0;
436         len = strlen(line);
437         if (len < 12)
438                 return -EINVAL;
439         growth = msg->len + len - 10 - msg->alen;
440         if (growth > 0) {
441                 if (rnet_message_expand(message, growth))
442                         return -ENOMEM;
443                 msg = *message;
444         }
445         memcpy(&msg->buffer[msg->len], line, len - 12);
446         msg->buffer[msg->len + len - 12] = '\r';
447         msg->buffer[msg->len + len - 11] = '\n';
448         msg->len += len - 10;
449         return 0;
450 }
451
452 static int decfile_parse_file(struct rnet_decfile *decfile)
453 {
454         int i;
455         int r;
456         for (i = 0; i < decfile->lines_len; i++) {
457                 r = append_stripped_reg_ctrl(&decfile->message,
458                                                 decfile->lines[i]);
459                 if (r)
460                         return r;
461         }
462         return 0;
463 }
464
465 struct rnet_message * rnet_decfile_get_file(struct rnet_decfile *decfile)
466 {
467         return decfile->message;
468 }
469
470 char * rnet_decfile_get_file_hash(struct rnet_decfile *decfile)
471 {
472         char *hash;
473         size_t len;
474         if (gcry_md_test_algo(GCRY_MD_MD5))
475                 return NULL;
476         len = gcry_md_get_algo_dlen(GCRY_MD_MD5);
477         hash = malloc(len);
478         if (!hash)
479                 return NULL;
480         gcry_md_hash_buffer(GCRY_MD_MD5, hash, decfile->message->buffer,
481                                         decfile->message->len);
482         return hash;
483 }
484
485 char * rnet_decfile_get_header(struct rnet_decfile *decfile)
486 {
487         return get_header(decfile);
488 }