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