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