Check for responses and send second message.
[cascardo/libreceita.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 -1;
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(struct pmhash *hash, char *buffer);
77 static int decfile_parse_file(struct rnet_decfile *decfile);
78
79 static int decfile_parse_header(struct rnet_decfile *decfile)
80 {
81         char *buffer = get_header(decfile);
82         if (!buffer || strlen(buffer) != 765)
83                 return 1;
84         return parse_header(decfile->header, buffer);
85 }
86
87 static int decfile_parse(struct rnet_decfile *decfile)
88 {
89         char *buffer = NULL;
90         size_t len = 0;
91         int r;
92         while ((r = getline(&buffer, &len, decfile->file)) > 0) {
93                 r = append_line(decfile, buffer);
94                 if (r) {
95                         free(buffer);
96                         goto out;
97                 }
98                 buffer = NULL;
99                 len = 0;
100         }
101         if (!decfile_parse_header(decfile) && !decfile_parse_file(decfile))
102                 return 0;
103 out:
104         decfile_release_lines(decfile);
105         return -1;
106 }
107
108 struct rnet_decfile * rnet_decfile_open(char *filename)
109 {
110         struct rnet_decfile *decfile;
111         decfile = malloc(sizeof(*decfile));
112         if (!decfile)
113                 return NULL;
114         decfile->header = pmhash_new();
115         if (!decfile->header)
116                 goto out_header;
117         decfile->message = rnet_message_new();
118         if (!decfile->message)
119                 goto out_message;
120         decfile->filename = strdup(filename);
121         if (!decfile->filename)
122                 goto out_filename;
123         decfile->file = fopen(filename, "r");
124         if (!decfile->file)
125                 goto out_file;
126         decfile->lines_len = 0;
127         decfile->lines = NULL;
128         if (decfile_parse(decfile))
129                 goto out_parse;
130         return decfile;
131 out_parse:
132         fclose(decfile->file);
133 out_file:
134         free(decfile->filename);
135 out_filename:
136         rnet_message_del(decfile->message);
137 out_message:
138         pmhash_del(decfile->header);
139 out_header:
140         free(decfile);
141         return NULL;
142 }
143
144 void rnet_decfile_close(struct rnet_decfile *decfile)
145 {
146         decfile_release_lines(decfile);
147         fclose(decfile->file);
148         free(decfile->filename);
149         free(decfile);
150 }
151
152 static char * get_header(struct rnet_decfile *decfile)
153 {
154         int i;
155         for (i = 0; i < decfile->lines_len; i++) {
156                 if (!strncmp(decfile->lines[i], "IRPF", 4)) {
157                         return decfile->lines[i];
158                 }
159         }
160         return NULL;
161 }
162
163 static int parse_header(struct pmhash *hash, char *buffer)
164 {
165         char *p = buffer;
166         char *key;
167         char *val;
168
169 #define parse(field, sz) \
170         val = malloc(sz + 1); \
171         if (!val) \
172                 goto out_val; \
173         val[sz] = 0; \
174         memcpy(val, p, sz); \
175         p += sz; \
176         key = strdup(field); \
177         if (!key) \
178                 goto out_key; \
179         if (pmhash_add(&hash, key, val)) \
180                 goto out_add;
181
182         parse("sistema", 8);
183         parse("exerc", 4);
184         parse("ano", 4);
185         parse("codigo_recnet", 4);
186         parse("in_ret", 1);
187         parse("cpf", 11);
188         parse("filler", 3);
189         parse("tipo_ni", 1);
190         parse("nr_versao", 3);
191         parse("nome", 60);
192         parse("uf", 2);
193         parse("hash", 10);
194         parse("in_cert", 1);
195         parse("dt_nasc", 8);
196         parse("in_comp", 1);
197         parse("in_res", 1);
198         parse("in_gerada", 1);
199         parse("nr_recibo_anterior", 10);
200         parse("in_pgd", 1);
201         parse("so", 14);
202         parse("versao_so", 7);
203         parse("jvm", 9);
204         parse("nr_recibo", 10);
205         parse("municipio", 4);
206         parse("conjuge", 11);
207         parse("obrig", 1);
208         parse("impdevido", 13);
209         parse("nr_recibo", 10);
210         parse("in_seg", 1);
211         parse("imppago", 2);
212         parse("impant", 1);
213         parse("mudend", 1);
214         parse("cep", 8);
215         parse("debito", 1);
216         parse("banco", 3);
217         parse("agencia", 4);
218         parse("filler", 1);
219         parse("data_julgado", 8);
220         parse("imppagar", 13);
221         parse("tribfonte", 1);
222         parse("cpfrra", 11);
223         parse("trib_rra", 1);
224         parse("cpf_rra2", 11);
225         parse("trib_3rra", 1);
226         parse("cpf_rra3", 11);
227         parse("vr_doacao", 13);
228         parse("cnpj1", 14);
229         parse("cnpj2", 14);
230         parse("cnpj3", 14);
231         parse("cnpj4", 14);
232         parse("cpf_dep1", 11);
233         parse("dnas_dep1", 8);
234         parse("cpf_dep2", 11);
235         parse("dnas_dep2", 8);
236         parse("cpf_dep3", 11);
237         parse("dnas_dep3", 8);
238         parse("cpf_dep4", 11);
239         parse("dnas_dep4", 8);
240         parse("cpf_dep5", 11);
241         parse("dnas_dep5", 8);
242         parse("cpf_dep6", 11);
243         parse("dnas_dep6", 8);
244         parse("cnpj_med1", 14);
245         parse("cnpj_med2", 14);
246         parse("cpf_alim", 11);
247         parse("cpf_invent", 11);
248         parse("municipio", 40);
249         parse("contribuinte", 60);
250         parse("cpf_empregada", 11);
251         parse("hashcode", 12);
252         parse("data_nao_residente", 8);
253         parse("cpf_procurador", 11);
254         parse("obrigatoriedade", 3);
255         parse("rendtrib", 13);
256         parse("cnpj_prev", 14);
257         parse("cnpj_prev2", 14);
258         parse("vr_totisentos", 13);
259         parse("vr_totexclusivo", 13);
260         parse("vr_totpagamentos", 13);
261         parse("versaotestpgd", 3);
262         parse("controle", 10);
263
264         return 0;
265 out_add:
266         free(key);
267 out_key:
268         free(val);
269 out_val:
270         return -1;
271 }
272
273 char *rnet_decfile_get_header_field(struct rnet_decfile *decfile, char *field)
274 {
275         return pmhash_get(decfile->header, field);
276 }
277
278 /* returns true if register is declaration and not a receipt */
279 static int decfile_reg_is_dec(char *line)
280 {
281         if (line[0] >= '0' && line[0] <= '9' &&
282             line[1] >= '0' && line[1] <= '9')
283                 return 1;
284         if (!strncmp(line, "IRPF    ", 8))
285                 return 1;
286         if (!strncmp(line, "T9", 2))
287                 return 1;
288         return 0;
289 }
290
291 /* strip a register from its control number and append it to message */
292 static int append_stripped_reg_ctrl(struct rnet_message **message, char *line)
293 {
294         size_t len;
295         struct rnet_message *msg = *message;
296         if (!decfile_reg_is_dec(line))
297                 return 0;
298         len = strlen(line);
299         if (len < 12)
300                 return -EINVAL;
301         if (msg->alen - msg->len < len) {
302                 if (rnet_message_expand(message, MAX(msg->len, len)))
303                         return -ENOMEM;
304                 msg = *message;
305         }
306         memcpy(&msg->buffer[msg->len], line, len - 12);
307         msg->buffer[msg->len + len - 12] = '\r';
308         msg->buffer[msg->len + len - 11] = '\n';
309         msg->len += len - 10;
310         return 0;
311 }
312
313 static int decfile_parse_file(struct rnet_decfile *decfile)
314 {
315         int i;
316         int r;
317         for (i = 0; i < decfile->lines_len; i++) {
318                 r = append_stripped_reg_ctrl(&decfile->message,
319                                                 decfile->lines[i]);
320                 if (r)
321                         return r;
322         }
323         return 0;
324 }
325
326 struct rnet_message * rnet_decfile_get_file(struct rnet_decfile *decfile)
327 {
328         return decfile->message;
329 }
330
331 char * rnet_decfile_get_file_hash(struct rnet_decfile *decfile)
332 {
333         char *hash;
334         size_t len;
335         if (gcry_md_test_algo(GCRY_MD_MD5))
336                 return NULL;
337         len = gcry_md_get_algo_dlen(GCRY_MD_MD5);
338         hash = malloc(len);
339         if (!hash)
340                 return NULL;
341         gcry_md_hash_buffer(GCRY_MD_MD5, hash, decfile->message->buffer,
342                                         decfile->message->len);
343         return hash;
344 }
345
346 char * rnet_decfile_get_header(struct rnet_decfile *decfile)
347 {
348         return get_header(decfile);
349 }