4ae761f4c3c401a970c67fef8e8c660af0434522
[cascardo/libreceita.git] / rnetclient.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  *  Copyright (C) 2014  Sergio Durigan Junior <sergiodj@sergiodj.net>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License along
17  *  with this program; if not, write to the Free Software Foundation, Inc.,
18  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #include <gnutls/gnutls.h>
34 #include <zlib.h>
35 #include <argp.h>
36 #include "config.h"
37 #include "decfile.h"
38 #include "rnet_message.h"
39 #include "rnet_encode.h"
40
41 /* Program version and bug report address.  */
42
43 const char *argp_program_version = PACKAGE_VERSION;
44 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
45
46 /* Documentation strings.  */
47
48 static const char rnetclient_doc[] =
49         "Send the Brazilian Income Tax Report to the Brazilian "
50         "Tax Authority";
51 static const char rnetclient_args_doc[] =
52         "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]";
53
54 /* Description and definition of each option accepted by the program.  */
55
56 static const struct argp_option rnetclient_options_desc[] = {
57         { "declaration", 'd', "FILE", 0,
58           "The Income Tax Report file that will be sent.",
59           0 },
60
61         { "output-dir", 'o', "DIRECTORY", 0,
62           "The directory where you wish to save the receipt.",
63           0 },
64
65         { NULL },
66 };
67
68 struct rnetclient_args {
69         /* File representing the declaration.  */
70         char *input_file;
71
72         /* Output directory to save the receipt.  */
73         char *output_dir;
74 };
75
76 /* Parser for command line arguments.  */
77
78 static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
79 {
80         struct rnetclient_args *a = state->input;
81         switch (key) {
82         case 'd':
83                 /* The user has explicitly provided a filename through
84                    the '-d' switch.  */
85                 a->input_file = arg;
86                 break;
87
88         case 'o':
89                 a->output_dir = arg;
90                 break;
91
92         case ARGP_KEY_ARG:
93                 /* The user has possibly provided a filename without
94                    using any switches (e.g., by running './rnetclient
95                    file').  */
96                 a->input_file = arg;
97                 break;
98
99         case ARGP_KEY_END:
100                 /* We have reached the end of the argument parsing.
101                    Let's check if the user has provided a filename.  */
102                 if (a->input_file == NULL)
103                         argp_error(state,
104                                    "You need to provide the Income Tax Declaration "
105                                    "filename.");
106         }
107
108         return 0;
109 }
110
111 /* Control struct used by argp.  */
112
113 static struct argp rnetclient_argp = {
114         rnetclient_options_desc,
115         rnetclient_parse_opt,
116         rnetclient_args_doc,
117         rnetclient_doc,
118         NULL, NULL, NULL
119 };
120
121 static size_t chars2len (unsigned char buf[2]) {
122         return (buf[0] << 8 | buf[1]);
123 }
124
125 static void * get_creds(char *certfile)
126 {
127         static gnutls_certificate_credentials_t cred;
128         gnutls_certificate_allocate_credentials(&cred);
129         gnutls_certificate_set_x509_trust_file(cred, certfile,
130                                         GNUTLS_X509_FMT_PEM);
131         return cred;
132 }
133
134 static void session_new(gnutls_session_t *session)
135 {
136         static void *cred;
137         cred = get_creds("cert.pem");
138         gnutls_init(session, GNUTLS_CLIENT);
139         gnutls_set_default_priority(*session);
140         gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, cred);
141 }
142
143 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
144 {
145         z_stream zstrm;
146         int r;
147         zstrm.zalloc = Z_NULL;
148         zstrm.zfree = Z_NULL;
149         zstrm.opaque = Z_NULL;
150         if ((r = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION)) != Z_OK)
151                 return -1;
152         *out = malloc(len * 2 + 36);
153         if (!out) {
154                 deflateEnd(&zstrm);
155                 return -1;
156         }
157         zstrm.next_in = (z_const Bytef *) buffer;
158         zstrm.avail_in = len;
159         zstrm.next_out = (Bytef *) *out + 6;
160         zstrm.avail_out = len * 2 + 30;
161         while ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
162                 zstrm.avail_out > 0);
163         if ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
164                 deflateEnd(&zstrm);
165                 free(*out);
166                 return -1;
167         }
168         *olen = zstrm.total_out + 6;
169         (*out)[0] = 0x1;
170         (*out)[1] = (zstrm.total_out >> 8);
171         (*out)[2] = (zstrm.total_out & 0xff);
172         (*out)[3] = (len >> 8);
173         (*out)[4] = (len & 0xff);
174         (*out)[5] = header ? 0x01 : 0x0;
175         deflateEnd(&zstrm);
176         return 0;
177 }
178
179 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
180 {
181         z_stream zstrm;
182         int r;
183         zstrm.zalloc = Z_NULL;
184         zstrm.zfree = Z_NULL;
185         zstrm.opaque = Z_NULL;
186         if ((r = inflateInit(&zstrm)) != Z_OK)
187                 return -1;
188         *olen = chars2len((unsigned char *) buffer+3);
189         *out = malloc(*olen);
190         if (!out) {
191                 inflateEnd(&zstrm);
192                 return -1;
193         }
194         zstrm.next_in = (z_const Bytef *) buffer + 6;
195         zstrm.avail_in = len - 6;
196         zstrm.next_out = (Bytef *) *out;
197         zstrm.avail_out = *olen;
198         while ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
199                 zstrm.avail_out > 0);
200         if ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
201                 inflateEnd(&zstrm);
202                 free(*out);
203                 return -1;
204         }
205         inflateEnd(&zstrm);
206         return 0;
207 }
208
209 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
210
211 static int connect_rnet(int *c)
212 {
213         struct addrinfo *addresses;
214         struct addrinfo *addr;
215         struct addrinfo hint;
216         int r;
217         int fd = *c = -1;
218         memset(&hint, 0, sizeof(hint));
219         hint.ai_family = AF_UNSPEC;
220         hint.ai_socktype = SOCK_STREAM;
221         hint.ai_protocol = IPPROTO_TCP;
222         hint.ai_flags = AI_ADDRCONFIG;
223         r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses);
224         if (r) {
225                 return r;
226         }
227         for (addr = addresses; addr != NULL; addr = addr->ai_next) {
228                 fd = socket(addr->ai_family, addr->ai_socktype,
229                                 addr->ai_protocol);
230                 if (fd >= 0)
231                         if (!(r = connect(fd, addr->ai_addr,
232                                                 addr->ai_addrlen)))
233                                 break;
234                 close(fd);
235                 fd = -1;
236         }
237         freeaddrinfo(addresses);
238         *c = fd;
239         if (fd == -1)
240                 return EAI_SYSTEM;
241         return 0;
242 }
243
244 static int handshake(int c)
245 {
246         char buffer[16];
247         int r;
248         buffer[0] = 1;
249         r = write(c, buffer, 1);
250         if (r < 1)
251                 return -1;
252         r = write(c, "00000000000000", 14);
253         if (r < 14)
254                 return -1;
255         r = read(c, buffer, 1);
256         if (r != 1 && buffer[0] != 'E')
257                 return -1;
258         r = read(c, buffer, 14);
259         if (r != 14)
260                 return -1;
261         return 0;
262 }
263
264 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
265 {
266         int r = 0;
267         /* Large files have to be uploaded as multiple
268            separately-deflated chunks, because the compressed and
269            uncompressed lengths in each record are encoded in unsigned
270            16-bit integers each.
271
272            The header can't be split into multiple chunks, and it
273            should never have to, since it won't ever get even close to
274            64KiB.
275
276            The uploaded file may be larger: to upload such large
277            files, it suffices to send multiple records till the entire
278            file is transferred, without waiting for a response.  Since
279            we've alread informed the server of the file size in the
280            header, it knows exactly how much data to expect before
281            sending a response.  It will only send an error message
282            before that if it times us out.
283
284            Odds are that any reasonably large size will do, but it
285            can't be too close to 64KiB, otherwise there won't be room
286            for the compressed length should it not compress well,
287            which should never happen for capital-ASCII-only
288            declaration files, but who knows?
289
290            This chunk size worked at the first try, uploading a
291            ~100KiB file, so let's stick with it.  */
292         const int maxc = 64472;
293         if (header && len > maxc)
294                 return -1;
295
296         do {
297                 char *out = NULL;
298                 size_t olen;
299                 size_t clen = len < maxc ? len : maxc;
300                 r = deflateRecord(buffer, clen, &out, &olen, header);
301                 if (!r) {
302                         size_t n = gnutls_record_send(session, out, olen);
303                         if (n != olen)
304                                 r = -1;
305                 }
306                 free(out);
307                 buffer += clen;
308                 len -= clen;
309         } while (len && !r);
310         return r;
311 }
312
313 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
314 {
315         char *out;
316         size_t olen;
317         char *buffer;
318         size_t len;
319         rnet_message_expand(message, 6);
320         buffer = (*message)->buffer;
321         gnutls_record_recv(session, buffer, 6);
322         if (buffer[0] == 0x01) {
323                 len = chars2len((unsigned char *) buffer+1);
324                 rnet_message_expand(message, len);
325                 buffer = (*message)->buffer + 6;
326                 gnutls_record_recv(session, buffer, len);
327                 inflateRecord(buffer - 6, len + 6, &out, &olen);
328                 rnet_message_del(*message);
329                 *message = NULL;
330                 rnet_message_expand(message, olen);
331                 memcpy((*message)->buffer, out, olen);
332                 (*message)->len = olen;
333                 free(out);
334         } else {
335                 len = chars2len((unsigned char *) buffer+1);
336                 rnet_message_expand(message, len - 1);
337                 buffer = (*message)->buffer + 6;
338                 gnutls_record_recv(session, buffer, len - 1);
339                 (*message)->len = len + 4;
340                 rnet_message_strip(*message, 4);
341         }
342         return 0;
343 }
344
345 static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetclient_args *args)
346 {
347         int fd;
348         char cwd[PATH_MAX];
349         char *path, *fname, *tmp;
350         size_t fname_len, r;
351         /* If the user provided the output directory where she wishes
352            to save the receipt, then we use it.  Otherwise, we save
353            the file in the current working directory (CWD).  */
354         if (args->output_dir == NULL)
355                 path = getcwd(cwd, PATH_MAX);
356         else {
357                 struct stat st;
358                 if (stat(args->output_dir, &st) < 0) {
359                         fprintf(stderr, "Could not stat directory \"%s\": %s\n", args->output_dir, strerror(errno));
360                         return;
361                 }
362                 if (!S_ISDIR(st.st_mode)) {
363                         fprintf(stderr, "Error: \"%s\" is a not a directory.\n", args->output_dir);
364                         return;
365                 }
366                 path = args->output_dir;
367         }
368         /* Now it's time to decide which filename to write.  We use
369            the declaration's filename as a base layout, because the
370            proprietary version of the IRPF program only recognizes
371            receipts if they have the same name as the declaration
372            files (disconsidering the extensions).  For example, if the
373            declaration file is named "123.DEC", the receipt should be
374            named "123.REC".  Therefore, if the declaration file has
375            the ".DEC" extension, we strip it out and add the ".REC".
376            Otherwise, we use the default template, which is to save
377            the receipt with the name "$CPF.REC".  */
378         tmp = strstr(args->input_file, ".DEC");
379         if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
380                 const char *p;
381                 /* We found the ".REC" extension.  */
382                 p = strdup(args->input_file);
383                 /* Replacing the ".DEC" by ".REC".  Fortunately, we
384                    just have to change one letter.  */
385                 tmp = strstr(p, ".DEC");
386                 tmp[1] = 'R';
387                 fname_len = strlen(p) + strlen(path) + 2;
388                 fname = alloca(fname_len);
389                 snprintf(fname, fname_len, "%s/%s", path, p);
390         } else {
391                 /* The declaration filename does not follow the
392                    convention, so we will not use it as a template.
393                    We just generate a filename using "$CPF.REC".  */
394                 fname_len = strlen(cpf) + strlen(path) + sizeof(".REC") + 2;
395                 fname = alloca(fname_len);
396                 snprintf(fname, fname_len, "%s/%s.REC", path, cpf);
397         }
398         /* Now, open the file and write.  */
399         fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
400         if (fd < 0) {
401                 fprintf(stderr, "Could not create receipt file \"%s\": %s\n", fname, strerror(errno));
402                 return;
403         }
404         do {
405                 r = write(fd, buffer, len);
406         } while (r != len && errno == EAGAIN);
407         if (r != len)
408                 fprintf(stderr, "Could not write to receipt file: %s", strerror(errno));
409         else
410                 fprintf(stderr, "Wrote the receipt file to %s.\n", fname);
411         close(fd);
412 }
413
414 static void handle_response_text_and_file(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
415 {
416         char *value;
417         int vlen;
418         if (!rnet_message_parse(message, "texto", &value, &vlen))
419                 fprintf(stderr, "%.*s\n", vlen, value);
420         if (!rnet_message_parse(message, "arquivo", &value, &vlen))
421                 save_rec_file(cpf, value, vlen, args);
422 }
423
424 static void handle_response_already_found(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
425 {
426         handle_response_text_and_file(cpf, message, args);
427 }
428
429 static void handle_response_error(struct rnet_message *message)
430 {
431         char *value;
432         int vlen;
433         if (!rnet_message_parse(message, "texto", &value, &vlen))
434                 fprintf(stderr, "%.*s\n", vlen, value);
435         fprintf(stderr, "Error transmiting DEC file.\n");
436 }
437
438 int main(int argc, char **argv)
439 {
440         int c;
441         int r;
442         struct rnet_decfile *decfile;
443         struct rnet_message *message = NULL;
444         struct rnetclient_args rnet_args;
445         gnutls_session_t session;
446         int finish = 0;
447         char *cpf;
448         error_t err;
449
450         /* Parsing the command line arguments.  The argp_parse
451            function calls exit() if there is some error during the
452            parsing process (e.g., the user has provided an unknown
453            flag or the parsing function has called argp_error).
454            However, if our internal parsing function returns something
455            different than zero, then argp_parse returns this value to
456            us.  This is a bug, and should not happen in the current
457            state.  */
458         memset(&rnet_args, 0, sizeof (rnet_args));
459         err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
460         if (err != 0)
461                 fprintf(stderr, "internal error while parsing command line arguments.");
462
463         decfile = rnet_decfile_open(rnet_args.input_file);
464         if (!decfile) {
465                 fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno));
466                 exit(1);
467         }
468
469         cpf = rnet_decfile_get_header_field(decfile, "cpf");
470
471         gnutls_global_init();
472
473         session_new(&session);
474         r = connect_rnet(&c);
475         if (r) {
476                 fprintf(stderr, "error connecting to server: %s\n",
477                         r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
478                 exit(1);
479         }
480         gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
481         r = handshake(c);
482         if (r < 0) {
483                 exit(1);
484         }
485         if ((r = gnutls_handshake(session)) < 0)
486                 fprintf(stderr, "error in handshake: %s\n",
487                                 gnutls_strerror(r));
488
489         rnet_encode(decfile, &message);
490         rnet_send(session, message->buffer, message->len, 1);
491         rnet_message_del(message);
492
493         message = NULL;
494         r = rnet_recv(session, &message);
495         if (r || !message || message->len == 0) {
496                 fprintf(stderr, "error when receiving response\n");
497                 goto out;
498         }
499         switch (message->buffer[0]) {
500         case 1: /* go ahead */
501                 handle_response_text_and_file(cpf, message, &rnet_args);
502                 break;
503         case 3: /* error */
504                 handle_response_error(message);
505                 finish = 1;
506                 break;
507         case 4:
508                 handle_response_already_found(cpf, message, &rnet_args);
509                 finish = 1;
510                 break;
511         case 2:
512         case 5:
513                 handle_response_text_and_file(cpf, message, &rnet_args);
514                 finish = 1;
515                 break;
516         }
517         rnet_message_del(message);
518
519         if (finish)
520                 goto out;
521
522         message = rnet_decfile_get_file(decfile);
523         rnet_send(session, message->buffer, message->len, 0);
524
525         message = NULL;
526         r = rnet_recv(session, &message);
527         if (r || !message || message->len == 0) {
528                 fprintf(stderr, "error when receiving response\n");
529                 goto out;
530         }
531         switch (message->buffer[0]) {
532         case 3: /* error */
533                 handle_response_error(message);
534                 break;
535         case 2:
536         case 4:
537         case 5:
538         case 1:
539                 handle_response_text_and_file(cpf, message, &rnet_args);
540                 break;
541         }
542         
543 out:
544         gnutls_bye(session, GNUTLS_SHUT_RDWR);
545         close(c);
546         rnet_decfile_close(decfile);
547         gnutls_global_deinit();
548
549         return 0;
550 }