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.
25 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <gnutls/gnutls.h>
38 #include "rnet_message.h"
39 #include "rnet_encode.h"
41 /* Program version and bug report address. */
43 const char *argp_program_version = PACKAGE_VERSION;
44 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
46 /* Documentation strings. */
48 static const char rnetclient_doc[] =
49 "Send the Brazilian Income Tax Report to the Brazilian "
51 static const char rnetclient_args_doc[] =
52 "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]";
54 /* Description and definition of each option accepted by the program. */
56 static const struct argp_option rnetclient_options_desc[] = {
57 { "declaration", 'd', "FILE", 0,
58 "The Income Tax Report file that will be sent.",
61 { "output-dir", 'o', "DIRECTORY", 0,
62 "The directory where you wish to save the receipt.",
68 struct rnetclient_args {
69 /* File representing the declaration. */
72 /* Output directory to save the receipt. */
76 /* Parser for command line arguments. */
78 static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
80 struct rnetclient_args *a = state->input;
83 /* The user has explicitly provided a filename through
93 /* The user has possibly provided a filename without
94 using any switches (e.g., by running './rnetclient
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)
104 "You need to provide the Income Tax Declaration "
111 /* Control struct used by argp. */
113 static struct argp rnetclient_argp = {
114 rnetclient_options_desc,
115 rnetclient_parse_opt,
121 static size_t chars2len (unsigned char buf[2]) {
122 return (buf[0] << 8 | buf[1]);
125 static void * get_creds(char *certfile)
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);
134 static void session_new(gnutls_session_t *session)
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);
143 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
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)
152 *out = malloc(len * 2 + 36);
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) {
168 *olen = zstrm.total_out + 6;
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;
179 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
183 zstrm.zalloc = Z_NULL;
184 zstrm.zfree = Z_NULL;
185 zstrm.opaque = Z_NULL;
186 if ((r = inflateInit(&zstrm)) != Z_OK)
188 *olen = chars2len((unsigned char *) buffer+3);
189 *out = malloc(*olen);
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) {
209 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
211 static int connect_rnet(int *c)
213 struct addrinfo *addresses;
214 struct addrinfo *addr;
215 struct addrinfo hint;
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);
227 for (addr = addresses; addr != NULL; addr = addr->ai_next) {
228 fd = socket(addr->ai_family, addr->ai_socktype,
231 if (!(r = connect(fd, addr->ai_addr,
237 freeaddrinfo(addresses);
244 static int handshake(int c)
249 r = write(c, buffer, 1);
252 r = write(c, "00000000000000", 14);
255 r = read(c, buffer, 1);
256 if (r != 1 && buffer[0] != 'E')
258 r = read(c, buffer, 14);
264 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
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.
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
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.
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?
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)
299 size_t clen = len < maxc ? len : maxc;
300 r = deflateRecord(buffer, clen, &out, &olen, header);
302 size_t n = gnutls_record_send(session, out, olen);
313 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
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);
330 rnet_message_expand(message, olen);
331 memcpy((*message)->buffer, out, olen);
332 (*message)->len = olen;
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);
345 static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetclient_args *args)
349 char *path, *fname, *tmp;
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);
358 if (stat(args->output_dir, &st) < 0) {
359 fprintf(stderr, "Could not stat directory \"%s\": %s\n", args->output_dir, strerror(errno));
362 if (!S_ISDIR(st.st_mode)) {
363 fprintf(stderr, "Error: \"%s\" is a not a directory.\n", args->output_dir);
366 path = args->output_dir;
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') {
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");
387 fname_len = strlen(p) + strlen(path) + 2;
388 fname = alloca(fname_len);
389 snprintf(fname, fname_len, "%s/%s", path, p);
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);
398 /* Now, open the file and write. */
399 fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
401 fprintf(stderr, "Could not create receipt file \"%s\": %s\n", fname, strerror(errno));
405 r = write(fd, buffer, len);
406 } while (r != len && errno == EAGAIN);
408 fprintf(stderr, "Could not write to receipt file: %s", strerror(errno));
410 fprintf(stderr, "Wrote the receipt file to %s.\n", fname);
414 static void handle_response_text_and_file(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
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);
424 static void handle_response_already_found(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
426 handle_response_text_and_file(cpf, message, args);
429 static void handle_response_error(struct rnet_message *message)
433 if (!rnet_message_parse(message, "texto", &value, &vlen))
434 fprintf(stderr, "%.*s\n", vlen, value);
435 fprintf(stderr, "Error transmiting DEC file.\n");
438 int main(int argc, char **argv)
442 struct rnet_decfile *decfile;
443 struct rnet_message *message = NULL;
444 struct rnetclient_args rnet_args;
445 gnutls_session_t session;
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
458 memset(&rnet_args, 0, sizeof (rnet_args));
459 err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
461 fprintf(stderr, "internal error while parsing command line arguments.");
463 decfile = rnet_decfile_open(rnet_args.input_file);
465 fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno));
469 cpf = rnet_decfile_get_header_field(decfile, "cpf");
471 gnutls_global_init();
473 session_new(&session);
474 r = connect_rnet(&c);
476 fprintf(stderr, "error connecting to server: %s\n",
477 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
480 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
485 if ((r = gnutls_handshake(session)) < 0)
486 fprintf(stderr, "error in handshake: %s\n",
489 rnet_encode(decfile, &message);
490 rnet_send(session, message->buffer, message->len, 1);
491 rnet_message_del(message);
494 r = rnet_recv(session, &message);
495 if (r || !message || message->len == 0) {
496 fprintf(stderr, "error when receiving response\n");
499 switch (message->buffer[0]) {
500 case 1: /* go ahead */
501 handle_response_text_and_file(cpf, message, &rnet_args);
504 handle_response_error(message);
508 handle_response_already_found(cpf, message, &rnet_args);
513 handle_response_text_and_file(cpf, message, &rnet_args);
517 rnet_message_del(message);
522 message = rnet_decfile_get_file(decfile);
523 rnet_send(session, message->buffer, message->len, 0);
526 r = rnet_recv(session, &message);
527 if (r || !message || message->len == 0) {
528 fprintf(stderr, "error when receiving response\n");
531 switch (message->buffer[0]) {
533 handle_response_error(message);
539 handle_response_text_and_file(cpf, message, &rnet_args);
544 gnutls_bye(session, GNUTLS_SHUT_RDWR);
546 rnet_decfile_close(decfile);
547 gnutls_global_deinit();