Пример #1
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;
            }
        }
    }
}
Пример #2
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;
    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;
            }
        }
    }
}