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