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>
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.
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.
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.
26 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
34 #include <gnutls/gnutls.h>
39 #include "rnet_message.h"
40 #include "rnet_encode.h"
42 /* Program version and bug report address. */
44 const char *argp_program_version = PACKAGE_VERSION;
45 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
47 /* Documentation strings. */
49 static const char rnetclient_doc[] =
50 "Send the Brazilian Income Tax Report to the Brazilian "
52 static const char rnetclient_args_doc[] =
53 "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]";
55 /* Description and definition of each option accepted by the program. */
57 static const struct argp_option rnetclient_options_desc[] = {
58 { "declaration", 'd', "FILE", 0,
59 "The Income Tax Report file that will be sent.",
62 { "output-dir", 'o', "DIRECTORY", 0,
63 "The directory where you wish to save the receipt.",
69 struct rnetclient_args {
70 /* File representing the declaration. */
73 /* Output directory to save the receipt. */
77 /* Parser for command line arguments. */
79 static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
81 struct rnetclient_args *a = state->input;
84 /* The user has explicitly provided a filename through
94 /* The user has possibly provided a filename without
95 using any switches (e.g., by running './rnetclient
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)
105 "You need to provide the Income Tax Declaration "
112 /* Control struct used by argp. */
114 static struct argp rnetclient_argp = {
115 rnetclient_options_desc,
116 rnetclient_parse_opt,
122 static size_t chars2len (unsigned char buf[2]) {
123 return (buf[0] << 8 | buf[1]);
126 static void * get_creds(char *certfile)
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);
135 static void session_new(gnutls_session_t *session)
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);
144 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
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)
153 *out = malloc(len * 2 + 36);
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) {
169 *olen = zstrm.total_out + 6;
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;
180 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
184 zstrm.zalloc = Z_NULL;
185 zstrm.zfree = Z_NULL;
186 zstrm.opaque = Z_NULL;
187 if ((r = inflateInit(&zstrm)) != Z_OK)
189 *olen = chars2len((unsigned char *) buffer+3);
190 *out = malloc(*olen);
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) {
210 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
212 static int connect_rnet(int *c)
214 struct addrinfo *addresses;
215 struct addrinfo *addr;
216 struct addrinfo hint;
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);
228 for (addr = addresses; addr != NULL; addr = addr->ai_next) {
229 fd = socket(addr->ai_family, addr->ai_socktype,
232 if (!(r = connect(fd, addr->ai_addr,
238 freeaddrinfo(addresses);
245 static int handshake(int c)
250 r = write(c, buffer, 1);
253 r = write(c, "00000000000000", 14);
256 r = read(c, buffer, 1);
257 if (r != 1 && buffer[0] != 'E')
259 r = read(c, buffer, 14);
265 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
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.
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
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.
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?
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)
300 size_t clen = len < maxc ? len : maxc;
301 r = deflateRecord(buffer, clen, &out, &olen, header);
303 size_t n = gnutls_record_send(session, out, olen);
314 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
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);
331 rnet_message_expand(message, olen);
332 memcpy((*message)->buffer, out, olen);
333 (*message)->len = olen;
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);
346 static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetclient_args *args)
350 char *path, *fname, *tmp;
353 /* If the user provided the output directory where she wishes
354 to save the receipt, then we use it. Otherwise, we save
355 the file in the current working directory (CWD). */
356 if (args->output_dir == NULL)
357 path = getcwd(cwd, PATH_MAX);
360 if (stat(args->output_dir, &st) < 0) {
361 fprintf(stderr, "Could not stat directory \"%s\": %s\n", args->output_dir, strerror(errno));
364 if (!S_ISDIR(st.st_mode)) {
365 fprintf(stderr, "Error: \"%s\" is a not a directory.\n", args->output_dir);
368 path = args->output_dir;
370 /* Now it's time to decide which filename to write. We use
371 the declaration's filename as a base layout, because the
372 proprietary version of the IRPF program only recognizes
373 receipts if they have the same name as the declaration
374 files (disconsidering the extensions). For example, if the
375 declaration file is named "123.DEC", the receipt should be
376 named "123.REC". Therefore, if the declaration file has
377 the ".DEC" extension, we strip it out and add the ".REC".
378 Otherwise, we use the default template, which is to save
379 the receipt with the name "$CPF.REC". */
380 tmp = strstr(args->input_file, ".DEC");
381 if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
383 /* We found the ".REC" extension. */
384 p = strdup(basename(args->input_file));
385 /* Replacing the ".DEC" by ".REC". Fortunately, we
386 just have to change one letter. */
387 tmp = strstr(p, ".DEC");
389 fname_len = strlen(p) + strlen(path) + 2;
390 fname = alloca(fname_len);
391 snprintf(fname, fname_len, "%s/%s", path, p);
394 /* The declaration filename does not follow the
395 convention, so we will not use it as a template.
396 We just generate a filename using "$CPF.REC". */
397 fname_len = strlen(cpf) + strlen(path) + sizeof(".REC") + 2;
398 fname = alloca(fname_len);
399 snprintf(fname, fname_len, "%s/%s.REC", path, cpf);
401 /* Now, open the file and write. */
402 fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
404 fprintf(stderr, "Could not create receipt file \"%s\": %s\n", fname, strerror(errno));
408 r = write(fd, buffer, len);
409 } while (r < 0 && errno == EAGAIN);
411 fprintf(stderr, "Could not write to receipt file: %s", strerror(errno));
413 fprintf(stderr, "Wrote the receipt file to %s.\n", fname);
417 static void handle_response_text_and_file(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
421 if (!rnet_message_parse(message, "texto", &value, &vlen))
422 fprintf(stderr, "%.*s\n", vlen, value);
423 if (!rnet_message_parse(message, "arquivo", &value, &vlen))
424 save_rec_file(cpf, value, vlen, args);
427 static void handle_response_already_found(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
429 handle_response_text_and_file(cpf, message, args);
432 static void handle_response_error(struct rnet_message *message)
436 if (!rnet_message_parse(message, "texto", &value, &vlen))
437 fprintf(stderr, "%.*s\n", vlen, value);
438 fprintf(stderr, "Error transmiting DEC file.\n");
441 int main(int argc, char **argv)
445 struct rnet_decfile *decfile;
446 struct rnet_message *message = NULL;
447 struct rnetclient_args rnet_args;
448 gnutls_session_t session;
453 /* Parsing the command line arguments. The argp_parse
454 function calls exit() if there is some error during the
455 parsing process (e.g., the user has provided an unknown
456 flag or the parsing function has called argp_error).
457 However, if our internal parsing function returns something
458 different than zero, then argp_parse returns this value to
459 us. This is a bug, and should not happen in the current
461 memset(&rnet_args, 0, sizeof (rnet_args));
462 err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
464 fprintf(stderr, "internal error while parsing command line arguments.");
466 decfile = rnet_decfile_open(rnet_args.input_file);
468 fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno));
472 cpf = rnet_decfile_get_header_field(decfile, "cpf");
474 gnutls_global_init();
476 session_new(&session);
477 r = connect_rnet(&c);
479 fprintf(stderr, "error connecting to server: %s\n",
480 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
484 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
490 if ((r = gnutls_handshake(session)) < 0) {
491 fprintf(stderr, "error in handshake: %s\n",
497 r = rnet_encode(decfile, &message);
499 fprintf(stderr, "error encoding message, file not supported?\n");
504 rnet_send(session, message->buffer, message->len, 1);
505 rnet_message_del(message);
508 r = rnet_recv(session, &message);
509 if (r || !message || message->len == 0) {
510 fprintf(stderr, "error when receiving response\n");
514 switch (message->buffer[0]) {
515 case 1: /* go ahead */
516 handle_response_text_and_file(cpf, message, &rnet_args);
519 handle_response_error(message);
523 handle_response_already_found(cpf, message, &rnet_args);
528 handle_response_text_and_file(cpf, message, &rnet_args);
532 rnet_message_del(message);
537 message = rnet_decfile_get_file(decfile);
538 rnet_send(session, message->buffer, message->len, 0);
541 r = rnet_recv(session, &message);
542 if (r || !message || message->len == 0) {
543 fprintf(stderr, "error when receiving response\n");
547 switch (message->buffer[0]) {
549 handle_response_error(message);
555 handle_response_text_and_file(cpf, message, &rnet_args);
560 gnutls_bye(session, GNUTLS_SHUT_RDWR);
564 gnutls_global_deinit();
565 rnet_decfile_close(decfile);