X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=rnetclient.c;h=756cee8878db1a703c0dc870704a9b2fe09cfc6a;hb=d972335f59e6423b5181c5e49f6cde728cb3d4b9;hp=41e57b36e5aef1b1199a61f865154dfa8d3813f7;hpb=6742a53bd88c11e2f8c3231fdaf0f86ba133646d;p=cascardo%2Frnetclient.git diff --git a/rnetclient.c b/rnetclient.c index 41e57b3..756cee8 100644 --- a/rnetclient.c +++ b/rnetclient.c @@ -50,7 +50,11 @@ static const char rnetclient_doc[] = "Send the Brazilian Income Tax Report to the Brazilian " "Tax Authority"; static const char rnetclient_args_doc[] = - "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]"; + "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]" + " [-s|--server-name SERVER]" + " [-c|--client-version CLIENT]"; + +#define RNET_ADDRESS "receitanet.receita.fazenda.gov.br" /* Description and definition of each option accepted by the program. */ @@ -63,6 +67,14 @@ static const struct argp_option rnetclient_options_desc[] = { "The directory where you wish to save the receipt.", 0 }, + { "server-name", 's', "SERVER", 0, + "The server to connect to. Default is " RNET_ADDRESS ".", + 0 }, + + { "client-version", 'c', "CLIENT", 0, + "The client version to be used.", + 0 }, + { NULL }, }; @@ -72,6 +84,15 @@ struct rnetclient_args { /* Output directory to save the receipt. */ char *output_dir; + + /* Output filename. */ + char output_file[PATH_MAX]; + + /* Server to connect to. */ + char *server_name; + + /* Client version to use. */ + char *client_version; }; /* Parser for command line arguments. */ @@ -90,6 +111,14 @@ static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state a->output_dir = arg; break; + case 's': + a->server_name = arg; + break; + + case 'c': + a->client_version = arg; + break; + case ARGP_KEY_ARG: /* The user has possibly provided a filename without using any switches (e.g., by running './rnetclient @@ -207,9 +236,7 @@ static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen) return 0; } -#define RNET_ADDRESS "receitanet.receita.fazenda.gov.br" - -static int connect_rnet(int *c) +static int connect_rnet(int *c, char *server_name) { struct addrinfo *addresses; struct addrinfo *addr; @@ -221,7 +248,7 @@ static int connect_rnet(int *c) hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = IPPROTO_TCP; hint.ai_flags = AI_ADDRCONFIG; - r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses); + r = getaddrinfo(server_name, "3456", &hint, &addresses); if (r) { return r; } @@ -277,7 +304,7 @@ static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int hea The uploaded file may be larger: to upload such large files, it suffices to send multiple records till the entire file is transferred, without waiting for a response. Since - we've alread informed the server of the file size in the + we've already informed the server of the file size in the header, it knows exactly how much data to expect before sending a response. It will only send an error message before that if it times us out. @@ -343,30 +370,13 @@ static int rnet_recv(gnutls_session_t session, struct rnet_message **message) return 0; } -static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetclient_args *args) +static int open_rec_file(char *cpf, struct rnetclient_args *args) { int fd; - char cwd[PATH_MAX]; - char *path, *fname, *tmp; - size_t fname_len; - ssize_t r; - /* If the user provided the output directory where she wishes - to save the receipt, then we use it. Otherwise, we save - the file in the current working directory (CWD). */ - if (args->output_dir == NULL) - path = getcwd(cwd, PATH_MAX); - else { - struct stat st; - if (stat(args->output_dir, &st) < 0) { - fprintf(stderr, "Could not stat directory \"%s\": %s\n", args->output_dir, strerror(errno)); - return; - } - if (!S_ISDIR(st.st_mode)) { - fprintf(stderr, "Error: \"%s\" is a not a directory.\n", args->output_dir); - return; - } - path = args->output_dir; - } + char *path, *tmp; + + path = args->output_dir; + /* Now it's time to decide which filename to write. We use the declaration's filename as a base layout, because the proprietary version of the IRPF program only recognizes @@ -386,47 +396,53 @@ static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetcli just have to change one letter. */ tmp = strstr(p, ".DEC"); tmp[1] = 'R'; - fname_len = strlen(p) + strlen(path) + 2; - fname = alloca(fname_len); - snprintf(fname, fname_len, "%s/%s", path, p); + snprintf(args->output_file, PATH_MAX, "%s/%s", path, p); free(p); } else { /* The declaration filename does not follow the convention, so we will not use it as a template. We just generate a filename using "$CPF.REC". */ - fname_len = strlen(cpf) + strlen(path) + sizeof(".REC") + 2; - fname = alloca(fname_len); - snprintf(fname, fname_len, "%s/%s.REC", path, cpf); + snprintf(args->output_file, PATH_MAX, "%s/%s.REC", path, cpf); } /* Now, open the file and write. */ - fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); + fd = open(args->output_file, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR); if (fd < 0) { - fprintf(stderr, "Could not create receipt file \"%s\": %s\n", fname, strerror(errno)); - return; + fprintf(stderr, "Could not create receipt file \"%s\": %s\n", + args->output_file, strerror(errno)); } + return fd; +} + +static int save_rec_file(int fd, char *buffer, int len, const struct rnetclient_args *args) +{ + ssize_t r; + do { r = write(fd, buffer, len); } while (r < 0 && errno == EAGAIN); - if (r != len) - fprintf(stderr, "Could not write to receipt file: %s", strerror(errno)); - else - fprintf(stderr, "Wrote the receipt file to %s.\n", fname); - close(fd); + if (r != len) { + fprintf(stderr, "Could not write to receipt file: %s", + strerror(errno)); + } else { + fprintf(stderr, "Wrote the receipt file to %s.\n", + args->output_file); + } + return r; } -static void handle_response_text_and_file(char *cpf, struct rnet_message *message, const struct rnetclient_args *args) +static void handle_response_text_and_file(int fd, struct rnet_message *message, const struct rnetclient_args *args) { char *value; int vlen; if (!rnet_message_parse(message, "texto", &value, &vlen)) fprintf(stderr, "%.*s\n", vlen, value); if (!rnet_message_parse(message, "arquivo", &value, &vlen)) - save_rec_file(cpf, value, vlen, args); + save_rec_file(fd, value, vlen, args); } -static void handle_response_already_found(char *cpf, struct rnet_message *message, const struct rnetclient_args *args) +static void handle_response_already_found(int fd, struct rnet_message *message, const struct rnetclient_args *args) { - handle_response_text_and_file(cpf, message, args); + handle_response_text_and_file(fd, message, args); } static void handle_response_error(struct rnet_message *message) @@ -449,6 +465,8 @@ int main(int argc, char **argv) int finish = 0; char *cpf; error_t err; + char cwd[PATH_MAX]; + int outfd; /* Parsing the command line arguments. The argp_parse function calls exit() if there is some error during the @@ -459,10 +477,31 @@ int main(int argc, char **argv) us. This is a bug, and should not happen in the current state. */ memset(&rnet_args, 0, sizeof (rnet_args)); + rnet_args.server_name = RNET_ADDRESS; + rnet_args.client_version = RNET_DEFAULT_VERSION; err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args); if (err != 0) fprintf(stderr, "internal error while parsing command line arguments."); + /* If the user provided the output directory where she wishes + to save the receipt, then we use it. Otherwise, we save + the file in the current working directory (CWD). */ + if (rnet_args.output_dir == NULL) { + rnet_args.output_dir = getcwd(cwd, PATH_MAX); + } else { + struct stat st; + if (stat(rnet_args.output_dir, &st) < 0) { + fprintf(stderr, "Could not stat directory \"%s\": %s\n", + rnet_args.output_dir, strerror(errno)); + exit(1); + } + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "Error: \"%s\" is a not a directory.\n", + rnet_args.output_dir); + exit(1); + } + } + decfile = rnet_decfile_open(rnet_args.input_file); if (!decfile) { fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno)); @@ -471,10 +510,16 @@ int main(int argc, char **argv) cpf = rnet_decfile_get_header_field(decfile, "cpf"); + outfd = open_rec_file(cpf, &rnet_args); + if (outfd < 0) { + r = 1; + goto out_rec; + } + gnutls_global_init(); session_new(&session); - r = connect_rnet(&c); + r = connect_rnet(&c, rnet_args.server_name); if (r) { fprintf(stderr, "error connecting to server: %s\n", r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); @@ -494,7 +539,7 @@ int main(int argc, char **argv) goto out_handshake; } - r = rnet_encode(decfile, &message); + r = rnet_encode(decfile, &message, rnet_args.client_version); if (r < 0) { fprintf(stderr, "error encoding message, file not supported?\n"); r = 1; @@ -513,19 +558,19 @@ int main(int argc, char **argv) } switch (message->buffer[0]) { case 1: /* go ahead */ - handle_response_text_and_file(cpf, message, &rnet_args); + handle_response_text_and_file(outfd, message, &rnet_args); break; case 3: /* error */ handle_response_error(message); finish = 1; break; case 4: - handle_response_already_found(cpf, message, &rnet_args); + handle_response_already_found(outfd, message, &rnet_args); finish = 1; break; case 2: case 5: - handle_response_text_and_file(cpf, message, &rnet_args); + handle_response_text_and_file(outfd, message, &rnet_args); finish = 1; break; } @@ -552,7 +597,7 @@ int main(int argc, char **argv) case 4: case 5: case 1: - handle_response_text_and_file(cpf, message, &rnet_args); + handle_response_text_and_file(outfd, message, &rnet_args); break; } @@ -562,6 +607,8 @@ out_handshake: close(c); out_connect: gnutls_global_deinit(); + close(outfd); +out_rec: rnet_decfile_close(decfile); return r;