Exemplo n.º 1
0
/*------------------------------------------------------------------------
 * A file has been received from the MXit server.
 *
 *  @param session		The	MXit session object
 *  @param fileid		A unique ID that identifies this file
 *  @param data			The file data
 *  @param datalen		The size of the data
 */
void mxit_xfer_rx_file( struct MXitSession* session, const char* fileid, const char* data, int datalen )
{
	PurpleXfer*			xfer	= NULL;

	purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_rx_file: (size=%i)\n", datalen );

	/* find the file-transfer object */
	xfer = find_mxit_xfer( session, fileid );
	if ( xfer ) {
		/* this is the transfer we have been looking for */
		purple_xfer_ref( xfer );
		purple_xfer_start( xfer, -1, NULL, 0 );

		if ( fwrite( data, datalen, 1, xfer->dest_fp ) > 0 ) {
			purple_xfer_unref( xfer );
			purple_xfer_set_completed( xfer, TRUE );
			purple_xfer_end( xfer );

			/* inform MXit that file was successfully received */
			mxit_send_file_received( session, fileid, RECV_STATUS_SUCCESS );
		}
		else {
			/* file write error */
			purple_xfer_error( purple_xfer_get_type( xfer ), purple_xfer_get_account( xfer ), purple_xfer_get_remote_user( xfer ), _( "Unable to save the file" ) );
			purple_xfer_cancel_local( xfer );
		}
	}
	else {
		/* file transfer not found */
		mxit_send_file_received( session, fileid, RECV_STATUS_BAD_ID );
	}
}
Exemplo n.º 2
0
/*------------------------------------------------------------------------
 * A file has been received from the MXit server.
 *
 *  @param session		The	MXit session object
 *  @param fileid		A unique ID that identifies this file
 *  @param data			The file data
 *  @param datalen		The size of the data
 */
void mxit_xfer_rx_file( struct MXitSession* session, const char* fileid, const char* data, int datalen )
{
	PurpleXfer*			xfer	= NULL;
	struct mxitxfer*	mx		= NULL;

	purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_rx_file: (size=%i)\n", datalen );

	/* find the file-transfer object */
	xfer = find_mxit_xfer( session, fileid );
	if ( xfer ) {
		mx = xfer->data;

		/* this is the transfer we have been looking for */
		purple_xfer_ref( xfer );
		purple_xfer_start( xfer, -1, NULL, 0 );
		fwrite( data, datalen, 1, xfer->dest_fp );
		purple_xfer_unref( xfer );
		purple_xfer_set_completed( xfer, TRUE );
		purple_xfer_end( xfer );

		/* inform MXit that file was successfully received */
		mxit_send_file_received( session, fileid, RECV_STATUS_SUCCESS );
	}
	else {
		/* file transfer not found */
		mxit_send_file_received( session, fileid, RECV_STATUS_BAD_ID );
	}
}
Exemplo n.º 3
0
static void ggp_edisc_xfer_recv_done(PurpleHttpConnection *hc,
	PurpleHttpResponse *response, gpointer _xfer)
{
	PurpleXfer *xfer = _xfer;
	ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);

	if (purple_xfer_is_cancelled(xfer))
		return;

	g_return_if_fail(edisc_xfer != NULL);

	edisc_xfer->hc = NULL;

	if (!purple_http_response_is_successful(response)) {
		ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
		return;
	}

	if (purple_xfer_get_bytes_remaining(xfer) == 0) {
		purple_xfer_set_completed(xfer, TRUE);
		purple_xfer_end(xfer);
		ggp_edisc_xfer_free(xfer);
	} else {
		purple_debug_warning("gg", "ggp_edisc_xfer_recv_done: didn't "
			"received everything\n");
		ggp_edisc_xfer_error(xfer, _("Error while receiving a file"));
	}
}
Exemplo n.º 4
0
static void tgprpl_xfer_send_on_finished (struct tgl_state *TLS, void *_data, int success, struct tgl_message *M) {
  debug ("tgprpl_xfer_on_finished()");
  struct tgp_xfer_send_data *data = _data;

  if (success) {
    if (! purple_xfer_is_canceled (data->xfer)) {
      debug ("purple_xfer_set_completed");
      purple_xfer_set_bytes_sent (data->xfer, purple_xfer_get_size (data->xfer));
      purple_xfer_set_completed (data->xfer, TRUE);
      purple_xfer_end (data->xfer);
    }
    write_secret_chat_file (TLS);
  } else {
    tgp_notify_on_error_gw (TLS, NULL, success);
    if (! purple_xfer_is_canceled (data->xfer)) {
      purple_xfer_cancel_remote (data->xfer);
    }
    failure ("send xfer failed");
  }

  data->loading = FALSE;

  data->xfer->data = NULL;
  purple_xfer_unref (data->xfer);
  tgprpl_xfer_free_data (data);
}
Exemplo n.º 5
0
static void tgprpl_xfer_recv_on_finished (struct tgl_state *TLS, void *_data, int success, const char *filename) {
  debug ("tgprpl_xfer_recv_on_finished()");
  struct tgp_xfer_send_data *data = _data;
  char *selected = g_strdup (purple_xfer_get_local_filename (data->xfer));

  if (success) {
    debug ("purple_xfer_set_completed");

    // always completed the file transfer to avoid a warning dialogue when closing (Adium)
    purple_xfer_set_bytes_sent (data->xfer, purple_xfer_get_size (data->xfer));
    purple_xfer_set_completed (data->xfer, TRUE);

    if (! purple_xfer_is_canceled (data->xfer)) {
      purple_xfer_end (data->xfer);
    }
  } else {
    tgp_notify_on_error_gw (TLS, NULL, success);
    if (! purple_xfer_is_canceled (data->xfer)) {
      purple_xfer_cancel_remote (data->xfer);
    }
    failure ("recv xfer failed");
  }

  data->loading = FALSE;

  data->xfer->data = NULL;
  purple_xfer_unref (data->xfer);
  tgprpl_xfer_free_data (data);

  debug ("moving transferred file from tgl directory %s to selected target %s", selected, filename);
  g_unlink (selected);
  g_rename (filename, selected);
  g_free (selected);
}
Exemplo n.º 6
0
void
msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
					  gsize size)
{
	PurpleXfer *xfer = slpcall->xfer;
	purple_xfer_set_completed(xfer, TRUE);
	purple_xfer_end(xfer);
}
Exemplo n.º 7
0
static void
xfer_completed_cb(struct pn_peer_call *call,
                  const guchar *body,
                  gsize size)
{
    PurpleXfer *xfer = call->xfer;
    purple_xfer_set_completed(xfer, TRUE);
    purple_xfer_end(xfer);
}
Exemplo n.º 8
0
/* just in case you were wondering, this is why DCC is gay */
static void irc_dccsend_send_read(gpointer data, int source, PurpleInputCondition cond)
{
    PurpleXfer *xfer = data;
    struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
    char buffer[64];
    int len;

    len = read(source, buffer, sizeof(buffer));

    if (len < 0 && errno == EAGAIN)
        return;
    else if (len <= 0) {
        /* XXX: Shouldn't this be canceling the transfer? */
        purple_input_remove(xd->inpa);
        xd->inpa = 0;
        return;
    }

    xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen);
    memcpy(xd->rxqueue + xd->rxlen, buffer, len);
    xd->rxlen += len;

    while (1) {
        gint32 val;
        size_t acked;

        if (xd->rxlen < 4)
            break;

        memcpy(&val, xd->rxqueue, sizeof(val));
        acked = ntohl(val);

        xd->rxlen -= 4;
        if (xd->rxlen) {
            unsigned char *tmp = g_memdup(xd->rxqueue + 4, xd->rxlen);
            g_free(xd->rxqueue);
            xd->rxqueue = tmp;
        } else {
            g_free(xd->rxqueue);
            xd->rxqueue = NULL;
        }

        if ((goffset)acked >= purple_xfer_get_size(xfer)) {
            purple_input_remove(xd->inpa);
            xd->inpa = 0;
            purple_xfer_set_completed(xfer, TRUE);
            purple_xfer_end(xfer);
            return;
        }
    }
}
Exemplo n.º 9
0
static void ggp_edisc_xfer_error(PurpleXfer *xfer, const gchar *msg)
{
	if (purple_xfer_is_cancelled(xfer))
		g_return_if_reached();
	purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
	purple_xfer_conversation_write(xfer, msg, TRUE);
	purple_xfer_error(
		purple_xfer_get_xfer_type(xfer),
		purple_xfer_get_account(xfer),
		purple_xfer_get_remote_user(xfer),
		msg);
	ggp_edisc_xfer_free(xfer);
	purple_xfer_end(xfer);
}
Exemplo n.º 10
0
static void
yahoo_process_filetrans_15_downloaded(PurpleHttpConnection *hc,
	PurpleHttpResponse *response, gpointer _xfer)
{
	PurpleXfer *xfer = _xfer;
	struct yahoo_xfer_data *xd;

	xd = purple_xfer_get_protocol_data(xfer);

	xd->hc = NULL;

	if (purple_xfer_is_cancelled(xfer))
		return;

	if (!purple_http_response_is_successful(response) ||
		purple_xfer_get_bytes_remaining(xfer) > 0)
	{
		purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
		purple_xfer_end(xfer);
	} else {
		purple_xfer_set_completed(xfer, TRUE);
		purple_xfer_end(xfer);
	}
}
Exemplo n.º 11
0
static void tgprpl_xfer_on_finished (struct tgl_state *TLS, void *_data, int success, struct tgl_message *M) {
  debug ("tgprpl_xfer_on_finished()");
  struct tgp_xfer_send_data *data = _data;
  
  if (success) {
    if (!data->done) {
      debug ("purple_xfer_set_completed");
      purple_xfer_set_bytes_sent (data->xfer, purple_xfer_get_size (data->xfer));
      purple_xfer_set_completed (data->xfer, TRUE);
      purple_xfer_end(data->xfer);
    }
  } else {
    failure ("ERROR xfer failed");
  }
  
  data->xfer->data = NULL;
  tgprpl_xfer_free_data (data);
}
Exemplo n.º 12
0
static void tgprpl_xfer_recv_on_finished (struct tgl_state *TLS, void *_data, int success, const char *filename) {
  debug ("tgprpl_xfer_recv_on_finished()");
  struct tgp_xfer_send_data *data = _data;

  if (success) {
    if (!data->done) {
      debug ("purple_xfer_set_completed");
      purple_xfer_set_bytes_sent (data->xfer, purple_xfer_get_size (data->xfer));
      purple_xfer_set_completed (data->xfer, TRUE);
      purple_xfer_end (data->xfer);
    }
    
    g_unlink (purple_xfer_get_local_filename (data->xfer));
    g_rename (filename, purple_xfer_get_local_filename (data->xfer));

  } else {
    failure ("ERROR xfer failed");
  }

  data->xfer->data = NULL;
  tgprpl_xfer_free_data (data);
}
Exemplo n.º 13
0
static void ggp_edisc_xfer_send_done(PurpleHttpConnection *hc,
	PurpleHttpResponse *response, gpointer _xfer)
{
	PurpleXfer *xfer = _xfer;
	ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
	const gchar *data = purple_http_response_get_data(response, NULL);
	JsonParser *parser;
	JsonObject *result;
	int result_status = -1;

	if (purple_xfer_is_cancelled(xfer))
		return;

	g_return_if_fail(edisc_xfer != NULL);

	edisc_xfer->hc = NULL;

	if (!purple_http_response_is_successful(response)) {
		ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
		return;
	}

	parser = ggp_json_parse(data);
	result = json_node_get_object(json_parser_get_root(parser));
	result = json_object_get_object_member(result, "result");
	if (json_object_has_member(result, "status"))
		result_status = json_object_get_int_member(result, "status");
	g_object_unref(parser);

	if (result_status == 0) {
		purple_xfer_set_completed(xfer, TRUE);
		purple_xfer_end(xfer);
		ggp_edisc_xfer_free(xfer);
	} else
		ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
}
Exemplo n.º 14
0
static void
do_transfer(PurpleXfer *xfer)
{
	PurpleXferUiOps *ui_ops;
	guchar *buffer = NULL;
	gssize r = 0;

	ui_ops = purple_xfer_get_ui_ops(xfer);

	if (xfer->type == PURPLE_XFER_RECEIVE) {
		r = purple_xfer_read(xfer, &buffer);
		if (r > 0) {
			size_t wc;
			if (ui_ops && ui_ops->ui_write)
				wc = ui_ops->ui_write(xfer, buffer, r);
			else
				wc = fwrite(buffer, 1, r, xfer->dest_fp);

			if (wc != r) {
				purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
				purple_xfer_cancel_local(xfer);
				g_free(buffer);
				return;
			}

			if ((purple_xfer_get_size(xfer) > 0) &&
				((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
				purple_xfer_set_completed(xfer, TRUE);
		} else if(r < 0) {
			purple_xfer_cancel_remote(xfer);
			g_free(buffer);
			return;
		}
	} else if (xfer->type == PURPLE_XFER_SEND) {
		size_t result = 0;
		size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
		PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
		gboolean read = TRUE;

		/* this is so the prpl can keep the connection open
		   if it needs to for some odd reason. */
		if (s == 0) {
			if (xfer->watcher) {
				purple_input_remove(xfer->watcher);
				xfer->watcher = 0;
			}
			return;
		}

		if (priv->buffer) {
			if (priv->buffer->len < s) {
				s -= priv->buffer->len;
				read = TRUE;
			} else {
				read = FALSE;
			}
		}

		if (read) {
			if (ui_ops && ui_ops->ui_read) {
				gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
				if (tmp == 0) {
					/*
					 * The UI claimed it was ready, but didn't have any data for
					 * us...  It will call purple_xfer_ui_ready when ready, which
					 * sets back up this watcher.
					 */
					if (xfer->watcher != 0) {
						purple_input_remove(xfer->watcher);
						xfer->watcher = 0;
					}

					/* Need to indicate the prpl is still ready... */
					priv->ready |= PURPLE_XFER_READY_PRPL;

					g_return_if_reached();
				} else if (tmp < 0) {
					purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
					purple_xfer_cancel_local(xfer);
					return;
				}

				result = tmp;
			} else {
				buffer = g_malloc(s);
				result = fread(buffer, 1, s, xfer->dest_fp);
				if (result != s) {
					purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
					purple_xfer_cancel_local(xfer);
					g_free(buffer);
					return;
				}
			}
		}

		if (priv->buffer) {
			g_byte_array_append(priv->buffer, buffer, result);
			g_free(buffer);
			buffer = priv->buffer->data;
			result = priv->buffer->len;
		}

		r = purple_xfer_write(xfer, buffer, result);

		if (r == -1) {
			purple_xfer_cancel_remote(xfer);
			if (!priv->buffer)
				/* We don't free buffer if priv->buffer is set, because in
				   that case buffer doesn't belong to us. */
				g_free(buffer);
			return;
		} else if (r == result) {
			/*
			 * We managed to write the entire buffer.  This means our
			 * network is fast and our buffer is too small, so make it
			 * bigger.
			 */
			purple_xfer_increase_buffer_size(xfer);
		} else {
			if (ui_ops && ui_ops->data_not_sent)
				ui_ops->data_not_sent(xfer, buffer + r, result - r);
		}

		if (priv->buffer) {
			/*
			 * Remove what we wrote
			 * If we wrote the whole buffer the byte array will be empty
			 * Otherwise we'll keep what wasn't sent for next time.
			 */
			buffer = NULL;
			g_byte_array_remove_range(priv->buffer, 0, r);
		}
	}

	if (r > 0) {
		if (purple_xfer_get_size(xfer) > 0)
			xfer->bytes_remaining -= r;

		xfer->bytes_sent += r;

		if (xfer->ops.ack != NULL)
			xfer->ops.ack(xfer, buffer, r);

		g_free(buffer);

		if (ui_ops != NULL && ui_ops->update_progress != NULL)
			ui_ops->update_progress(xfer,
				purple_xfer_get_progress(xfer));
	}

	if (purple_xfer_is_completed(xfer))
		purple_xfer_end(xfer);
}
Exemplo n.º 15
0
static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len)
{
	gint bytes ;
	qq_file_header fh;
	guint16 packet_type;
	guint16 packet_seq;
	guint8 sub_type;
	guint32 fragment_index;
	guint16 fragment_len;
	guint32 fragment_offset;
	qq_data *qd = (qq_data *) gc->proto_data;
	ft_info *info = (ft_info *) qd->xfer->data;

	bytes = 0;
	bytes += _qq_get_file_header(&fh, data + bytes);

	bytes += 1; /* skip an unknown byte */
	bytes += qq_get16(&packet_type, data + bytes);
	switch(packet_type)
	{
		case QQ_FILE_CMD_FILE_OP:
			bytes += qq_get16(&packet_seq, data + bytes);
			bytes += qq_get8(&sub_type, data + bytes);
			switch (sub_type)
			{
				case QQ_FILE_BASIC_INFO:
					bytes += 4;	/* file length, we have already known it from xfer */
					bytes += qq_get32(&info->fragment_num, data + bytes);
					bytes += qq_get32(&info->fragment_len, data + bytes);

					/* FIXME: We must check the md5 here,
					 * if md5 doesn't match we will ignore
					 * the packet or send sth as error number */

					info->max_fragment_index = 0;
					info->window = 0;
					purple_debug_info("QQ",
							"start receiving data, %d fragments with %d length each\n",
							info->fragment_num, info->fragment_len);
					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
							0, 0, NULL, 0);
					break;
				case QQ_FILE_DATA_INFO:
					bytes += qq_get32(&fragment_index, data + bytes);
					bytes += qq_get32(&fragment_offset, data + bytes);
					bytes += qq_get16(&fragment_len, data + bytes);
					purple_debug_info("QQ",
							"received %dth fragment with length %d, offset %d\n",
							fragment_index, fragment_len, fragment_offset);

					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
							fragment_index, packet_seq, NULL, 0);
					_qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset);
					break;
				case QQ_FILE_EOF:
					purple_debug_info("QQ", "end of receiving\n");
					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
							0, 0, NULL, 0);
					break;
			}
			break;
		case QQ_FILE_CMD_FILE_OP_ACK:
			bytes += qq_get16(&packet_seq, data + bytes);
			bytes += qq_get8(&sub_type, data + bytes);
			switch (sub_type)
			{
				case QQ_FILE_BASIC_INFO:
					info->max_fragment_index = 0;
					info->window = 0;
					/* It is ready to send file data */
					_qq_send_file_progess(gc);
					break;
				case QQ_FILE_DATA_INFO:
					bytes += qq_get32(&fragment_index, data + bytes);
					_qq_update_send_progess(gc, fragment_index);
					if (purple_xfer_is_completed(qd->xfer))
						_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0);
					/*	else
						_qq_send_file_progess(gc); */
					break;
				case QQ_FILE_EOF:
					/* FIXME: OK, we can end the connection successfully */

					_qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
					purple_xfer_set_completed(qd->xfer, TRUE);
					break;
			}
			break;
		case QQ_FILE_EOF:
			_qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
			purple_xfer_set_completed(qd->xfer, TRUE);
			purple_xfer_end(qd->xfer);
			break;
		case QQ_FILE_BASIC_INFO:
			purple_debug_info("QQ", "here\n");
			_qq_send_file_data_packet(gc, QQ_FILE_DATA_INFO, 0, 0, 0, NULL, 0);
			break;
		default:
			purple_debug_info("QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n",
					packet_type);
			break;
	}
}
Exemplo n.º 16
0
static void
do_transfer(PurpleXfer *xfer)
{
	PurpleXferUiOps *ui_ops;
	guchar *buffer = NULL;
	gssize r = 0;

	ui_ops = purple_xfer_get_ui_ops(xfer);

	if (xfer->type == PURPLE_XFER_RECEIVE) {
		r = purple_xfer_read(xfer, &buffer);
		if (r > 0) {
			size_t wc;
			if (ui_ops && ui_ops->ui_write)
				wc = ui_ops->ui_write(xfer, buffer, r);
			else
				wc = fwrite(buffer, 1, r, xfer->dest_fp);

			if (wc != r) {
				purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
				purple_xfer_cancel_local(xfer);
				g_free(buffer);
				return;
			}

			if ((purple_xfer_get_size(xfer) > 0) &&
				((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
				purple_xfer_set_completed(xfer, TRUE);
		} else if(r < 0) {
			purple_xfer_cancel_remote(xfer);
			g_free(buffer);
			return;
		}
	} else if (xfer->type == PURPLE_XFER_SEND) {
		size_t result;
		size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);

		/* this is so the prpl can keep the connection open
		   if it needs to for some odd reason. */
		if (s == 0) {
			if (xfer->watcher) {
				purple_input_remove(xfer->watcher);
				xfer->watcher = 0;
			}
			return;
		}

		if (ui_ops && ui_ops->ui_read) {
			gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
			if (tmp == 0) {
				/*
				 * UI isn't ready to send data. It will call
				 * purple_xfer_ui_ready when ready, which sets back up this
				 * watcher.
				 */
				if (xfer->watcher != 0) {
					purple_timeout_remove(xfer->watcher);
					xfer->watcher = 0;
				}

				return;
			} else if (tmp < 0) {
				purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
				purple_xfer_cancel_local(xfer);
				return;
			}

			result = tmp;
		} else {
			buffer = g_malloc0(s);
			result = fread(buffer, 1, s, xfer->dest_fp);
			if (result != s) {
				purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
				purple_xfer_cancel_local(xfer);
				g_free(buffer);
				return;
			}
		}

		/* Write as much as we're allowed to. */
		r = purple_xfer_write(xfer, buffer, result);

		if (r == -1) {
			purple_xfer_cancel_remote(xfer);
			g_free(buffer);
			return;
		} else if (r < result) {
			if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
				/* We have to seek back in the file now. */
				fseek(xfer->dest_fp, r - s, SEEK_CUR);
			}
			else {
				ui_ops->data_not_sent(xfer, buffer + r, result - r);
			}
		} else {
			/*
			 * We managed to write the entire buffer.  This means our
			 * network is fast and our buffer is too small, so make it
			 * bigger.
			 */
			purple_xfer_increase_buffer_size(xfer);
		}
	}

	if (r > 0) {
		if (purple_xfer_get_size(xfer) > 0)
			xfer->bytes_remaining -= r;

		xfer->bytes_sent += r;

		if (xfer->ops.ack != NULL)
			xfer->ops.ack(xfer, buffer, r);

		g_free(buffer);

		if (ui_ops != NULL && ui_ops->update_progress != NULL)
			ui_ops->update_progress(xfer,
				purple_xfer_get_progress(xfer));
	}

	if (purple_xfer_is_completed(xfer))
		purple_xfer_end(xfer);
}