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 = buffer;
158 zstrm.avail_in = len;
159 zstrm.next_out = *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(buffer+3);
189 *out = malloc(*olen);
194 zstrm.next_in = buffer + 6;
195 zstrm.avail_in = len - 6;
196 zstrm.next_out = *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;
216 struct sockaddr_in saddr;
220 memset(&hint, 0, sizeof(hint));
221 hint.ai_family = AF_UNSPEC;
222 hint.ai_socktype = SOCK_STREAM;
223 hint.ai_protocol = IPPROTO_TCP;
224 hint.ai_flags = AI_ADDRCONFIG;
225 r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses);
229 for (addr = addresses; addr != NULL; addr = addr->ai_next) {
230 fd = socket(addr->ai_family, addr->ai_socktype,
233 if (!(r = connect(fd, addr->ai_addr,
239 freeaddrinfo(addresses);
246 static int handshake(int c)
251 r = write(c, buffer, 1);
254 r = write(c, "00000000000000", 14);
257 r = read(c, buffer, 1);
258 if (r != 1 && buffer[0] != 'E')
260 r = read(c, buffer, 14);
266 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
269 /* Large files have to be uploaded as multiple
270 separately-deflated chunks, because the compressed and
271 uncompressed lengths in each record are encoded in unsigned
272 16-bit integers each.
274 The header can't be split into multiple chunks, and it
275 should never have to, since it won't ever get even close to
278 The uploaded file may be larger: to upload such large
279 files, it suffices to send multiple records till the entire
280 file is transferred, without waiting for a response. Since
281 we've alread informed the server of the file size in the
282 header, it knows exactly how much data to expect before
283 sending a response. It will only send an error message
284 before that if it times us out.
286 Odds are that any reasonably large size will do, but it
287 can't be too close to 64KiB, otherwise there won't be room
288 for the compressed length should it not compress well,
289 which should never happen for capital-ASCII-only
290 declaration files, but who knows?
292 This chunk size worked at the first try, uploading a
293 ~100KiB file, so let's stick with it. */
294 const int maxc = 64472;
295 if (header && len > maxc)
301 size_t clen = len < maxc ? len : maxc;
302 r = deflateRecord(buffer, clen, &out, &olen, header);
304 size_t n = gnutls_record_send(session, out, olen);
315 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
322 rnet_message_expand(message, 6);
323 buffer = (*message)->buffer;
324 r = gnutls_record_recv(session, buffer, 6);
325 if (buffer[0] == 0x01) {
326 len = chars2len(buffer+1);
327 rnet_message_expand(message, len);
328 buffer = (*message)->buffer + 6;
329 r = gnutls_record_recv(session, buffer, len);
330 inflateRecord(buffer - 6, len + 6, &out, &olen);
331 rnet_message_del(*message);
333 rnet_message_expand(message, olen);
334 memcpy((*message)->buffer, out, olen);
335 (*message)->len = olen;
338 len = chars2len(buffer+1);
339 rnet_message_expand(message, len - 1);
340 buffer = (*message)->buffer + 6;
341 r = gnutls_record_recv(session, buffer, len - 1);
342 (*message)->len = len + 4;
343 rnet_message_strip(*message, 4);
348 static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetclient_args *args)
352 char *path, *fname, *tmp;
354 /* If the user provided the output directory where she wishes
355 to save the receipt, then we use it. Otherwise, we save
356 the file in the current working directory (CWD). */
357 if (args->output_dir == NULL)
358 path = getcwd(cwd, PATH_MAX);
361 if (stat(args->output_dir, &st) < 0) {
362 fprintf(stderr, "Could not stat directory \"%s\": %s\n", args->output_dir, strerror(errno));
365 if (!S_ISDIR(st.st_mode)) {
366 fprintf(stderr, "Error: \"%s\" is a not a directory.\n", args->output_dir);
369 path = args->output_dir;
371 /* Now it's time to decide which filename to write. We use
372 the declaration's filename as a base layout, because the
373 proprietary version of the IRPF program only recognizes
374 receipts if they have the same name as the declaration
375 files (disconsidering the extensions). For example, if the
376 declaration file is named "123.DEC", the receipt should be
377 named "123.REC". Therefore, if the declaration file has
378 the ".DEC" extension, we strip it out and add the ".REC".
379 Otherwise, we use the default template, which is to save
380 the receipt with the name "$CPF.REC". */
381 tmp = strstr(args->input_file, ".DEC");
382 if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
384 /* We found the ".REC" extension. */
385 p = strdup(args->input_file);
386 /* Replacing the ".DEC" by ".REC". Fortunately, we
387 just have to change one letter. */
388 tmp = strstr(p, ".DEC");
390 fname_len = strlen(p) + strlen(path) + 2;
391 fname = alloca(fname_len);
392 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 != len && 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));
483 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
488 if ((r = gnutls_handshake(session)) < 0)
489 fprintf(stderr, "error in handshake: %s\n",
492 rnet_encode(decfile, &message);
493 rnet_send(session, message->buffer, message->len, 1);
494 rnet_message_del(message);
497 r = rnet_recv(session, &message);
498 if (r || !message || message->len == 0) {
499 fprintf(stderr, "error when receiving response\n");
502 switch (message->buffer[0]) {
503 case 1: /* go ahead */
504 handle_response_text_and_file(cpf, message, &rnet_args);
507 handle_response_error(message);
511 handle_response_already_found(cpf, message, &rnet_args);
516 handle_response_text_and_file(cpf, message, &rnet_args);
520 rnet_message_del(message);
525 message = rnet_decfile_get_file(decfile);
526 rnet_send(session, message->buffer, message->len, 0);
529 r = rnet_recv(session, &message);
530 if (r || !message || message->len == 0) {
531 fprintf(stderr, "error when receiving response\n");
534 switch (message->buffer[0]) {
536 handle_response_error(message);
542 handle_response_text_and_file(cpf, message, &rnet_args);
547 gnutls_bye(session, GNUTLS_SHUT_RDWR);
549 rnet_decfile_close(decfile);
550 gnutls_global_deinit();