static char *json_parse_chars(struct read_state *state) { struct write_state write; char ch, decoded_char; if (*state->read++ != '\"') { json_error_print(state, "Internal error parsing string value\n"); goto fail; } write_state_init(&write); while (true) { if (read_state_left(state) < 1) { json_error_print(state, "Unexpected end of file parsing string\n"); goto fail; } ch = *state->read++; if (ch == '\\') { read_state_put_back(state); decoded_char = json_parse_unescape_char(state); //TODO: this has failure states } else if (ch == '\"') { break; } else if (is_invalid_string_char(ch)) { json_error_print(state, "Invalid unescaped character in string\n"); goto fail; } else { decoded_char = ch; } if (write_state_left(&write) < 1) write_state_extend(&write); *write.write++ = decoded_char; } if (write_state_left(&write) < 1) write_state_extend(&write); *write.write++ = '\0'; return write.buf; fail: write_state_free(&write); return 0; }
static int doit(int s, const char *host, const char *user, const char *outfilename, const char *header_str, int leavep, int verbose, int forkp) { int ret; char out_buf[PUSH_BUFSIZ]; int out_len = 0; char *in_buf; size_t in_buf_size; size_t in_len = 0; char *in_ptr; pop_state state = INIT; unsigned count = 0, bytes; unsigned asked_for = 0, retrieved = 0, asked_deleted = 0, deleted = 0; unsigned sent_xdele = 0; int out_fd; char from_line[128]; size_t from_line_length; time_t now; struct write_state write_state; unsigned int numheaders = 1; char **headers = NULL; int i; char *tmp = NULL; in_buf = emalloc(PUSH_BUFSIZ + 1); in_ptr = in_buf; in_buf_size = PUSH_BUFSIZ; if (do_from) { char *tmp2; tmp2 = tmp = estrdup(header_str); out_fd = -1; if (verbose) fprintf (stderr, "%s@%s\n", user, host); while (*tmp != '\0') { tmp = strchr(tmp, ','); if (tmp == NULL) break; tmp++; numheaders++; } headers = emalloc(sizeof(char *) * (numheaders + 1)); for (i = 0; i < numheaders; i++) { headers[i] = strtok_r(tmp2, ",", &tmp2); } headers[numheaders] = NULL; } else { out_fd = open(outfilename, O_WRONLY | O_APPEND | O_CREAT, 0666); if (out_fd < 0) err (1, "open %s", outfilename); if (verbose) fprintf (stderr, "%s@%s -> %s\n", user, host, outfilename); } now = time(NULL); from_line_length = snprintf (from_line, sizeof(from_line), "From %s %s", "push", ctime(&now)); if (from_line_length < 0 || from_line_length > sizeof(from_line)) errx (1, "snprintf failed"); out_len = snprintf (out_buf, sizeof(out_buf), "USER %s\r\nPASS hej\r\nSTAT\r\n", user); if (out_len < 0 || out_len > sizeof(out_buf)) errx (1, "snprintf failed"); if (net_write (s, out_buf, out_len) != out_len) err (1, "write"); if (verbose > 1) fprintf (stderr, "%s", out_buf); if (!do_from) write_state_init (&write_state, out_fd); while(state != QUIT) { fd_set readset, writeset; FD_ZERO(&readset); FD_ZERO(&writeset); if (s >= FD_SETSIZE) errx (1, "fd too large"); FD_SET(s,&readset); if (verbose > 1) fprintf (stderr, "state: %s count: %d asked_for: %d " "retrieved: %d asked_deleted: %d\n", pop_state_string[state], count, asked_for, retrieved, asked_deleted); if (((state == STAT || state == RETR || state == TOP) && asked_for < count) || (state == XDELE && !sent_xdele) || (state == DELE && asked_deleted < count)) FD_SET(s,&writeset); ret = select (s + 1, &readset, &writeset, NULL, NULL); if (ret < 0) { if (errno == EAGAIN) continue; else err (1, "select"); } if (FD_ISSET(s, &readset)) { char *beg, *p; size_t rem; int blank_line = 0; if(in_len >= in_buf_size) { char *tmp = erealloc(in_buf, in_buf_size + PUSH_BUFSIZ + 1); in_ptr = tmp + (in_ptr - in_buf); in_buf = tmp; in_buf_size += PUSH_BUFSIZ; } ret = read (s, in_ptr, in_buf_size - in_len); if (ret < 0) err (1, "read"); else if (ret == 0) errx (1, "EOF during read"); in_len += ret; in_ptr += ret; *in_ptr = '\0'; beg = in_buf; rem = in_len; while(rem > 1 && (p = strstr(beg, "\r\n")) != NULL) { if (state == TOP) { char *copy = beg; for (i = 0; i < numheaders; i++) { size_t len; len = min(p - copy + 1, strlen(headers[i])); if (strncasecmp(copy, headers[i], len) == 0) { fprintf (stdout, "%.*s\n", (int)(p - copy), copy); } } if (beg[0] == '.' && beg[1] == '\r' && beg[2] == '\n') { if (numheaders > 1) fprintf (stdout, "\n"); state = STAT; if (++retrieved == count) { state = QUIT; net_write (s, "QUIT\r\n", 6); if (verbose > 1) fprintf (stderr, "QUIT\r\n"); } } rem -= p - beg + 2; beg = p + 2; } else if (state == RETR) { char *copy = beg; if (beg[0] == '.') { if (beg[1] == '\r' && beg[2] == '\n') { if(!blank_line) write_state_add(&write_state, "\n", 1); state = STAT; rem -= p - beg + 2; beg = p + 2; if (++retrieved == count) { write_state_flush (&write_state); if (fsync (out_fd) < 0) err (1, "fsync"); close(out_fd); if (leavep) { state = QUIT; net_write (s, "QUIT\r\n", 6); if (verbose > 1) fprintf (stderr, "QUIT\r\n"); } else { if (forkp) { pid_t pid; pid = fork(); if (pid < 0) warn ("fork"); else if(pid != 0) { if(verbose) fprintf (stderr, "(exiting)"); return 0; } } state = XDELE; if (verbose) fprintf (stderr, "deleting... "); } } continue; } else ++copy; } *p = '\n'; if(blank_line && strncmp(copy, "From ", min(p - copy + 1, 5)) == 0) write_state_add(&write_state, ">", 1); write_state_add(&write_state, copy, p - copy + 1); blank_line = (*copy == '\n'); rem -= p - beg + 2; beg = p + 2; } else if (rem >= 3 && strncmp (beg, "+OK", 3) == 0) { if (state == STAT) { if (!do_from) write_state_add(&write_state, from_line, from_line_length); blank_line = 0; if (do_from) state = TOP; else state = RETR; } else if (state == XDELE) { state = QUIT; net_write (s, "QUIT\r\n", 6); if (verbose > 1) fprintf (stderr, "QUIT\r\n"); break; } else if (state == DELE) { if (++deleted == count) { state = QUIT; net_write (s, "QUIT\r\n", 6); if (verbose > 1) fprintf (stderr, "QUIT\r\n"); break; } } else if (++state == STAT) { if(sscanf (beg + 4, "%u %u", &count, &bytes) != 2) errx(1, "Bad STAT-line: %.*s", (int)(p - beg), beg); if (verbose) { fprintf (stderr, "%u message(s) (%u bytes). " "fetching... ", count, bytes); if (do_from) fprintf (stderr, "\n"); } else if (do_count) { fprintf (stderr, "%u message(s) (%u bytes).\n", count, bytes); } if (count == 0) { state = QUIT; net_write (s, "QUIT\r\n", 6); if (verbose > 1) fprintf (stderr, "QUIT\r\n"); break; } } rem -= p - beg + 2; beg = p + 2; } else { if(state == XDELE) { state = DELE; rem -= p - beg + 2; beg = p + 2; } else errx (1, "Bad response: %.*s", (int)(p - beg), beg); } } if (!do_from) write_state_flush (&write_state); memmove (in_buf, beg, rem); in_len = rem; in_ptr = in_buf + rem; } if (FD_ISSET(s, &writeset)) { if ((state == STAT && !do_from) || state == RETR) out_len = snprintf (out_buf, sizeof(out_buf), "RETR %u\r\n", ++asked_for); else if ((state == STAT && do_from) || state == TOP) out_len = snprintf (out_buf, sizeof(out_buf), "TOP %u 0\r\n", ++asked_for); else if(state == XDELE) { out_len = snprintf(out_buf, sizeof(out_buf), "XDELE %u %u\r\n", 1, count); sent_xdele++; } else if(state == DELE) out_len = snprintf (out_buf, sizeof(out_buf), "DELE %u\r\n", ++asked_deleted); if (out_len < 0 || out_len > sizeof(out_buf)) errx (1, "snprintf failed"); if (net_write (s, out_buf, out_len) != out_len) err (1, "write"); if (verbose > 1) fprintf (stderr, "%s", out_buf); } } if (verbose) fprintf (stderr, "Done\n"); if (do_from) { free (tmp); free (headers); } else { write_state_destroy (&write_state); } return 0; }