void xfer_dcc_recv_file_child (struct t_xfer *xfer) { int flags, num_read, ack_enabled, ready; static char buffer[XFER_BLOCKSIZE_MAX]; time_t last_sent, new_time; unsigned long long pos_last_ack; fd_set read_fds, write_fds, except_fds; /* first connect to sender (blocking) */ if (!weechat_network_connect_to (xfer->proxy, xfer->sock, xfer->remote_address, xfer->port)) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_CONNECT_SENDER); return; } /* connection is OK, change DCC status (inform parent process) */ xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, XFER_NO_ERROR); /* make socket non-blocking */ flags = fcntl (xfer->sock, F_GETFL); if (flags == -1) flags = 0; fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK); last_sent = time (NULL); ack_enabled = 1; pos_last_ack = 0; while (1) { /* wait until there is something to read on socket (or error) */ FD_ZERO (&read_fds); FD_ZERO (&write_fds); FD_ZERO (&except_fds); FD_SET (xfer->sock, &read_fds); ready = select (xfer->sock + 1, &read_fds, &write_fds, &except_fds, NULL); if (ready == 0) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_RECV_BLOCK); return; } /* read maximum data on socket (until nothing is available) */ while (1) { num_read = recv (xfer->sock, buffer, sizeof (buffer), 0); if (num_read == -1) { if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR)) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_RECV_BLOCK); return; } /* * no more data available on socket: exit loop, send ACK, and * wait for new data on socket */ break; } else { if (num_read == 0) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_RECV_BLOCK); return; } if (write (xfer->file, buffer, num_read) == -1) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_WRITE_LOCAL); return; } xfer->pos += (unsigned long long) num_read; /* file received OK? */ if (xfer->pos >= xfer->size) { /* * extra delay before sending ACK, otherwise the send of ACK * may fail */ usleep (100000); /* send ACK to sender without checking return code (file OK) */ xfer_dcc_recv_file_send_ack (xfer); /* set status done and return */ xfer_network_write_pipe (xfer, XFER_STATUS_DONE, XFER_NO_ERROR); return; } /* update status of DCC (parent process) */ new_time = time (NULL); if (last_sent != new_time) { last_sent = new_time; xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, XFER_NO_ERROR); } } } /* send ACK to sender (if needed) */ if (ack_enabled && (xfer->pos > pos_last_ack)) { switch (xfer_dcc_recv_file_send_ack (xfer)) { case 0: /* send error, socket down? */ xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_SEND_ACK); return; case 1: /* send error, not fatal (buffer full?): disable ACKs */ ack_enabled = 0; break; case 2: /* send OK: save position in file as last ACK sent */ pos_last_ack = xfer->pos; break; } } } }
void xfer_dcc_recv_file_child (struct t_xfer *xfer) { int flags, num_read, ack_enabled, ready; static char buffer[XFER_BLOCKSIZE_MAX]; time_t last_sent, new_time; unsigned long long pos_last_ack; struct pollfd poll_fd; ssize_t written, total_written; unsigned char *bin_hash; char hash[9]; /* if resuming, hash the portion of the file we have */ if ((xfer->start_resume > 0) && xfer->hash_handle) { xfer_network_write_pipe (xfer, XFER_STATUS_HASHING, XFER_NO_ERROR); if (!xfer_dcc_resume_hash (xfer)) { gcry_md_close (*xfer->hash_handle); free (xfer->hash_handle); xfer->hash_handle = NULL; xfer_network_write_pipe (xfer, XFER_STATUS_HASHING, XFER_ERROR_HASH_RESUME_ERROR); } xfer_network_write_pipe (xfer, XFER_STATUS_CONNECTING, XFER_NO_ERROR); } /* first connect to sender (blocking) */ xfer->sock = weechat_network_connect_to (xfer->proxy, xfer->remote_address, xfer->remote_address_length); if (xfer->sock == -1) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_CONNECT_SENDER); return; } /* connection is OK, change DCC status (inform parent process) */ xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, XFER_NO_ERROR); /* make socket non-blocking */ flags = fcntl (xfer->sock, F_GETFL); if (flags == -1) flags = 0; fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK); last_sent = time (NULL); ack_enabled = 1; pos_last_ack = 0; while (1) { /* wait until there is something to read on socket (or error) */ poll_fd.fd = xfer->sock; poll_fd.events = POLLIN; poll_fd.revents = 0; ready = poll (&poll_fd, 1, -1); if (ready <= 0) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_RECV_BLOCK); return; } /* read maximum data on socket (until nothing is available) */ while (1) { num_read = recv (xfer->sock, buffer, sizeof (buffer), 0); if (num_read == -1) { if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR)) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_RECV_BLOCK); return; } /* * no more data available on socket: exit loop, send ACK, and * wait for new data on socket */ break; } else { if ((num_read == 0) && (xfer->pos < xfer->size)) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_RECV_BLOCK); return; } /* bytes received, write to disk */ total_written = 0; while (total_written < num_read) { written = write (xfer->file, buffer + total_written, num_read - total_written); if (written < 0) { if (errno == EINTR) continue; xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_WRITE_LOCAL); return; } else { if (xfer->hash_handle) { gcry_md_write (*xfer->hash_handle, buffer + total_written, written); } total_written += written; } } xfer->pos += (unsigned long long) num_read; /* file received OK? */ if (xfer->pos >= xfer->size) { /* check hash and report result to pipe */ if (xfer->hash_handle) { gcry_md_final (*xfer->hash_handle); bin_hash = gcry_md_read (*xfer->hash_handle, 0); if (bin_hash) { snprintf (hash, sizeof (hash), "%.2X%.2X%.2X%.2X", bin_hash[0], bin_hash[1], bin_hash[2], bin_hash[3]); if (weechat_strcasecmp (hash, xfer->hash_target) == 0) { xfer_network_write_pipe (xfer, XFER_STATUS_HASHED, XFER_NO_ERROR); } else { xfer_network_write_pipe (xfer, XFER_STATUS_HASHED, XFER_ERROR_HASH_MISMATCH); } } } fsync (xfer->file); /* * extra delay before sending ACK, otherwise the send of ACK * may fail */ usleep (100000); /* send ACK to sender without checking return code (file OK) */ xfer_dcc_recv_file_send_ack (xfer); /* set status done and return */ xfer_network_write_pipe (xfer, XFER_STATUS_DONE, XFER_NO_ERROR); return; } /* update status of DCC (parent process) */ new_time = time (NULL); if (last_sent != new_time) { last_sent = new_time; xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, XFER_NO_ERROR); } } } /* send ACK to sender (if needed) */ if (ack_enabled && (xfer->pos > pos_last_ack)) { switch (xfer_dcc_recv_file_send_ack (xfer)) { case 0: /* send error, socket down? */ xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_SEND_ACK); return; case 1: /* send error, not fatal (buffer full?): disable ACKs */ ack_enabled = 0; break; case 2: /* send OK: save position in file as last ACK sent */ pos_last_ack = xfer->pos; break; } } } }