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