void xfer_dcc_send_file_child (struct t_xfer *xfer) { int num_read, num_sent, blocksize; static char buffer[XFER_BLOCKSIZE_MAX]; uint32_t ack; time_t last_sent, new_time, last_second; long sent_last_second; blocksize = xfer->blocksize; if (weechat_config_integer (xfer_config_network_speed_limit) > 0) { if (blocksize > weechat_config_integer (xfer_config_network_speed_limit) * 1024) blocksize = weechat_config_integer (xfer_config_network_speed_limit) * 1024; } last_sent = time (NULL); last_second = time (NULL); sent_last_second = 0; while (1) { /* read DCC ACK (sent by receiver) */ if (xfer->pos > xfer->ack) { /* we should receive ACK for packets sent previously */ while (1) { num_read = recv (xfer->sock, (char *) &ack, 4, MSG_PEEK); if ((num_read < 1) && ((num_read != -1) || (errno != EAGAIN))) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_SEND_BLOCK); return; } if (num_read == 4) { recv (xfer->sock, (char *) &ack, 4, 0); xfer->ack = ntohl (ack); /* DCC send ok? */ if ((xfer->pos >= xfer->size) && (xfer->ack >= xfer->size)) { xfer_network_write_pipe (xfer, XFER_STATUS_DONE, XFER_NO_ERROR); return; } } else break; } } /* send a block to receiver */ if ((xfer->pos < xfer->size) && (xfer->fast_send || (xfer->pos <= xfer->ack))) { if ((weechat_config_integer (xfer_config_network_speed_limit) > 0) && (sent_last_second >= weechat_config_integer (xfer_config_network_speed_limit) * 1024)) { /* we're sending too fast (according to speed limit set by user) */ usleep (100); } else { lseek (xfer->file, xfer->pos, SEEK_SET); num_read = read (xfer->file, buffer, blocksize); if (num_read < 1) { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_READ_LOCAL); return; } num_sent = send (xfer->sock, buffer, num_read, 0); if (num_sent < 0) { /* socket is temporarily not available (receiver can't receive amount of data we sent ?!) */ if (errno == EAGAIN) usleep (1000); else { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_SEND_BLOCK); return; } } if (num_sent > 0) { xfer->pos += (unsigned long) num_sent; sent_last_second += (unsigned long) num_sent; new_time = time (NULL); if (last_sent != new_time) { last_sent = new_time; xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, XFER_NO_ERROR); } } } } else usleep (1000); if (time (NULL) > last_second) { last_second = time (NULL); sent_last_second = 0; } } }
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 num_read; static char buffer[XFER_BLOCKSIZE_MAX]; uint32_t pos; time_t last_sent, new_time; /* first connect to sender (blocking) */ if (!weechat_network_connect_to (xfer->proxy, xfer->sock, xfer->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); last_sent = time (NULL); while (1) { num_read = recv (xfer->sock, buffer, sizeof (buffer), 0); if (num_read == -1) { /* socket is temporarily not available (sender is not fast ?!) */ if ((errno == EAGAIN) || (errno == EINTR)) usleep (1000); else { xfer_network_write_pipe (xfer, XFER_STATUS_FAILED, XFER_ERROR_RECV_BLOCK); return; } } 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) num_read; pos = htonl (xfer->pos); /* we don't check return code, not a problem if an ACK send failed */ send (xfer->sock, (char *) &pos, 4, 0); /* file received ok? */ if (xfer->pos >= xfer->size) { xfer_network_write_pipe (xfer, XFER_STATUS_DONE, XFER_NO_ERROR); return; } new_time = time (NULL); if (last_sent != new_time) { last_sent = new_time; xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE, XFER_NO_ERROR); } } } }
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; } } } }