--- a/libpurple/xfer.c Fri Dec 20 02:44:13 2019 +0000
+++ b/libpurple/xfer.c Sat Dec 21 03:12:10 2019 -0500
@@ -121,6 +121,18 @@
static GParamSpec *properties[PROP_LAST];
+/* GObject signal enums */ +static guint signals[SIG_LAST] = {0}; G_DEFINE_TYPE_WITH_PRIVATE(PurpleXfer, purple_xfer, G_TYPE_OBJECT);
static int purple_xfer_choose_file(PurpleXfer *xfer);
@@ -571,12 +583,25 @@
+do_query_local(PurpleXfer *xfer, const gchar *filename) + if (g_stat(filename, &st) == -1) { + purple_xfer_show_file_error(xfer, filename); + purple_xfer_set_size(xfer, st.st_size); purple_xfer_request_accepted(PurpleXfer *xfer, const gchar *filename)
PurpleXferClass *klass = NULL;
PurpleXferPrivate *priv = NULL;
gchar *msg, *utf8, *base;
@@ -606,8 +631,7 @@
if (priv->type == PURPLE_XFER_TYPE_SEND) {
/* Check the filename. */
- PurpleXferUiOps *ui_ops;
- ui_ops = purple_xfer_get_ui_ops(xfer);
if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
@@ -626,18 +650,13 @@
- if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
- if (g_stat(filename, &st) == -1) {
- purple_xfer_show_file_error(xfer, filename);
- purple_xfer_set_local_filename(xfer, filename);
- purple_xfer_set_size(xfer, st.st_size);
- purple_xfer_set_local_filename(xfer, filename);
+ g_signal_emit(xfer, signals[SIG_QUERY_LOCAL], 0, filename, + purple_xfer_set_local_filename(xfer, filename); base = g_path_get_basename(filename);
utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL);
@@ -1206,11 +1225,23 @@
return do_write(xfer, buffer, s);
+do_write_local(PurpleXfer *xfer, const guchar *buffer, gssize size) + PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer); + if (priv->dest_fp == NULL) { + purple_debug_error("xfer", "File is not opened for writing"); + return fwrite(buffer, 1, size, priv->dest_fp); purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size)
PurpleXferPrivate *priv = NULL;
- PurpleXferUiOps *ui_ops;
@@ -1220,7 +1251,6 @@
priv = purple_xfer_get_instance_private(xfer);
- ui_ops = purple_xfer_get_ui_ops(xfer);
fs_known = (priv->size > 0);
remaining = purple_xfer_get_bytes_remaining(xfer);
@@ -1231,17 +1261,7 @@
- if (ui_ops && ui_ops->ui_write) {
- wc = ui_ops->ui_write(xfer, buffer, size);
- if (priv->dest_fp == NULL) {
- purple_debug_error("xfer",
- "File is not opened for writing\n");
- purple_xfer_cancel_local(xfer);
- wc = fwrite(buffer, 1, size, priv->dest_fp);
+ g_signal_emit(xfer, signals[SIG_WRITE_LOCAL], 0, buffer, size, &wc); purple_debug_error("xfer",
@@ -1258,51 +1278,39 @@
+do_read_local(PurpleXfer *xfer, guchar *buffer, gssize size) + PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer); + if (priv->dest_fp == NULL) { + purple_debug_error("xfer", "File is not opened for reading"); + got_len = fread(buffer, 1, size, priv->dest_fp); + if ((got_len < 0 || got_len != size) && ferror(priv->dest_fp)) { + purple_debug_error("xfer", "Unable to read file."); purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size)
- PurpleXferPrivate *priv = NULL;
- PurpleXferUiOps *ui_ops;
g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
g_return_val_if_fail(buffer != NULL, 0);
- priv = purple_xfer_get_instance_private(xfer);
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops && ui_ops->ui_read) {
- guchar *buffer_got = NULL;
- got_len = ui_ops->ui_read(xfer, &buffer_got, size);
- if (got_len >= 0 && (gsize)got_len > size) {
- purple_debug_error("xfer",
- "Got too much data from UI.\n");
- purple_xfer_cancel_local(xfer);
- memcpy(buffer, buffer_got, got_len);
- if (priv->dest_fp == NULL) {
- purple_debug_error("xfer",
- "File is not opened for reading\n");
- purple_xfer_cancel_local(xfer);
- got_len = fread(buffer, 1, size, priv->dest_fp);
- if ((got_len < 0 || (gsize)got_len != size) &&
- purple_debug_error("xfer",
- "Unable to read file.\n");
- purple_xfer_cancel_local(xfer);
+ g_signal_emit(xfer, signals[SIG_READ_LOCAL], 0, buffer, size, &got_len); + if (got_len < 0 || got_len > size) { + purple_debug_error("xfer", "Unable to read file."); + purple_xfer_cancel_local(xfer); @@ -1313,6 +1321,19 @@
+do_data_not_sent(PurpleXfer *xfer, const guchar *buffer, gsize size) + PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer); + if (priv->buffer == NULL) { + priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE); + g_byte_array_append(priv->buffer, buffer, size); do_transfer(PurpleXfer *xfer)
@@ -1342,7 +1363,8 @@
(gsize)purple_xfer_get_bytes_remaining(xfer),
(gsize)priv->current_buffer_size
+ gboolean read_more = TRUE; + gboolean existing_buffer = FALSE; /* this is so the protocol can keep the connection open
if it needs to for some odd reason. */
@@ -1355,15 +1377,16 @@
+ existing_buffer = TRUE; if (priv->buffer->len < s) {
buffer = g_new(guchar, s);
result = purple_xfer_read_file(xfer, buffer, s);
@@ -1414,11 +1437,15 @@
purple_xfer_increase_buffer_size(xfer);
- if (ui_ops && ui_ops->data_not_sent)
- ui_ops->data_not_sent(xfer, buffer + r, result - r);
+ g_signal_emit(xfer, signals[SIG_DATA_NOT_SENT], 0, buffer + r, + purple_xfer_cancel_local(xfer);
+ if (existing_buffer && priv->buffer) { * If we wrote the whole buffer the byte array will be empty
@@ -1479,34 +1506,44 @@
+do_open_local(PurpleXfer *xfer) + PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer); + g_fopen(purple_xfer_get_local_filename(xfer), + priv->type == PURPLE_XFER_TYPE_RECEIVE ? "wb" : "rb"); + if (priv->dest_fp == NULL) { + purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer)); + if (fseek(priv->dest_fp, priv->bytes_sent, SEEK_SET) != 0) { + purple_debug_error("xfer", "couldn't seek"); + purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer)); begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond)
PurpleXferClass *klass = PURPLE_XFER_GET_CLASS(xfer);
PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
- PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
+ gboolean open_status = FALSE; if (priv->start_time != 0) {
purple_debug_error("xfer", "Transfer is being started multiple times\n");
- if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
- priv->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer),
- priv->type == PURPLE_XFER_TYPE_RECEIVE ? "wb" : "rb");
- if (priv->dest_fp == NULL) {
- purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
- purple_xfer_cancel_local(xfer);
- if (fseek(priv->dest_fp, priv->bytes_sent, SEEK_SET) != 0) {
- purple_debug_error("xfer", "couldn't seek\n");
- purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
- purple_xfer_cancel_local(xfer);
+ g_signal_emit(xfer, signals[SIG_OPEN_LOCAL], 0, &open_status); + purple_xfer_cancel_local(xfer); @@ -2127,20 +2164,12 @@
purple_xfer_constructed(GObject *object)
PurpleXfer *xfer = PURPLE_XFER(object);
- PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
G_OBJECT_CLASS(purple_xfer_parent_class)->constructed(object);
ui_ops = purple_xfers_get_ui_ops();
- if (ui_ops && ui_ops->data_not_sent) {
- /* If the ui will handle unsent data no need for buffer */
- priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
if (ui_ops != NULL && ui_ops->new_xfer != NULL) {
@@ -2199,6 +2228,14 @@
+ klass->open_local = do_open_local; + klass->query_local = do_query_local; + klass->read_local = do_read_local; + klass->write_local = do_write_local; + klass->data_not_sent = do_data_not_sent; properties[PROP_TYPE] = g_param_spec_enum("type", "Transfer type",
"The type of file transfer.", PURPLE_TYPE_XFER_TYPE,
PURPLE_XFER_TYPE_UNKNOWN,
@@ -2285,6 +2322,126 @@
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(obj_class, PROP_LAST, properties);
+ * PurpleXfer::open-local: + * Open a file locally for a file transfer. + * The default class handler will open a file using standard library + * functions. If you connect to this signal, you should connect to + * PurpleXfer::query-local, PurpleXfer::read-local, PurpleXfer::write-local + * and PurpleXfer::data-not-sent as well. + * Returns: %TRUE if the file was opened successfully, or %FALSE otherwise, + * and the transfer should be cancelled (libpurple will cancel). + signals[SIG_OPEN_LOCAL] = g_signal_new( + "open-local", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(PurpleXferClass, open_local), + g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_BOOLEAN, 0); + * PurpleXfer::query-local: + * @filename: The filename of the transfer. + * Query a file's properties locally. + * The default class handler will query a file using standard library + * functions, and set the transfer's size. If you connect to this signal, + * you should try to do the same, but it is not necessarily an error if you + * cannot. If you connect to this signal, you must connect to + * PurpleXfer::open-local, PurpleXfer::read-local, PurpleXfer::write-local + * and PurpleXfer::data-not-sent as well. + * Returns: %TRUE if the properties were queried successfully, or %FALSE + * otherwise, and the transfer should be cancelled (libpurple will + signals[SIG_QUERY_LOCAL] = g_signal_new( + "query-local", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(PurpleXferClass, query_local), + g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_BOOLEAN, 1, + * PurpleXfer::read-local: + * @buffer: (out): A pointer to a buffer to fill. + * @size: The maximum amount of data to put in the buffer. + * Read data locally to send to the protocol for a file transfer. + * The default class handler will read from a file using standard library + * functions. If you connect to this signal, you must connect to + * PurpleXfer::open-local, PurpleXfer::query-local, PurpleXfer::write-local + * and PurpleXfer::data-not-sent as well. + * Returns: The amount of data in the buffer, 0 if nothing is available, + * and a negative value if an error occurred and the transfer + * should be cancelled (libpurple will cancel). + signals[SIG_READ_LOCAL] = g_signal_new( + "read-local", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(PurpleXferClass, read_local), + g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_LONG, 2, + G_TYPE_POINTER, G_TYPE_LONG); + * PurpleXfer::write-local: + * @buffer: The buffer to write. + * @size: The size of the buffer. + * Write data received from the protocol locally. The signal handler must + * deal with the entire buffer and return size, or it is treated as an + * The default class handler will write to a file using standard library + * functions. If you connect to this signal, you must connect to + * PurpleXfer::open-local, PurpleXfer::query-local, PurpleXfer::read-local + * and PurpleXfer::data-not-sent as well. + * Returns: @size if the write was successful, or a value between 0 and + signals[SIG_WRITE_LOCAL] = g_signal_new( + "write-local", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(PurpleXferClass, write_local), + g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_LONG, 2, + G_TYPE_POINTER, G_TYPE_LONG); + * PurpleXfer::data-not-sent: + * @buffer: A pointer to the beginning of the unwritten data. + * @size: The amount of unwritten data. + * Notify the UI that not all the data read in was written. The UI should + * re-enqueue this data and return it the next time read is called. + * If you connect to this signal, you must connect to + * PurpleXfer::open-local, PurpleXfer::query-local, PurpleXfer::read-local + * and PurpleXfer::write-local as well. + * Returns: %TRUE if the data was re-enqueued successfully, or %FALSE + * otherwise, and the transfer should be cancelled (libpurple + signals[SIG_DATA_NOT_SENT] = g_signal_new( + "data-not-sent", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(PurpleXferClass, data_not_sent), + g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_BOOLEAN, 2, + G_TYPE_POINTER, G_TYPE_ULONG); --- a/libpurple/xfer.h Fri Dec 20 02:44:13 2019 +0000
+++ b/libpurple/xfer.h Sat Dec 21 03:12:10 2019 -0500
@@ -107,32 +107,6 @@
* @cancel_remote: UI op that's called when a transfer has been cancelled on
- * @ui_write: UI op to write data received from the protocol. The UI must deal
- * with the entire buffer and return size, or it is treated as an
- * <sbr/>@xfer: The file transfer structure
- * <sbr/>@buffer: The buffer to write
- * <sbr/>@size: The size of the buffer
- * <sbr/>Returns: size if the write was successful, or a value
- * between 0 and size on error.
- * @ui_read: UI op to read data to send to the protocol for a file transfer.
- * <sbr/>@xfer: The file transfer structure
- * <sbr/>@buffer: A pointer to a buffer. The UI must allocate this
- * buffer. libpurple will free the data.
- * <sbr/>@size: The maximum amount of data to put in the buffer.
- * <sbr/>Returns: The amount of data in the buffer, 0 if nothing is
- * available, and a negative value if an error occurred
- * and the transfer should be cancelled (libpurple will
- * @data_not_sent: Op to notify the UI that not all the data read in was
- * written. The UI should re-enqueue this data and return it the
- * next time read is called.
- * <sbr/>This <emphasis>MUST</emphasis> be implemented if read
- * and write are implemented.
- * <sbr/>@xfer: The file transfer structure
- * <sbr/>@buffer: A pointer to the beginning of the unwritten
- * <sbr/>@size: The amount of unwritten data.
* @add_thumbnail: Op to create a thumbnail image for a file transfer
* File transfer UI operations.
@@ -148,9 +122,6 @@
void (*update_progress)(PurpleXfer *xfer, double percent);
void (*cancel_local)(PurpleXfer *xfer);
void (*cancel_remote)(PurpleXfer *xfer);
- gssize (*ui_write)(PurpleXfer *xfer, const guchar *buffer, gssize size);
- gssize (*ui_read)(PurpleXfer *xfer, guchar **buffer, gssize size);
- void (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer, gsize size);
void (*add_thumbnail)(PurpleXfer *xfer, const gchar *formats);
@@ -166,6 +137,11 @@
* @read: Called when reading data from the file transfer.
* @write: Called when writing data to the file transfer.
* @ack: Called when a file transfer is acknowledged.
+ * @open_local: The vfunc for PurpleXfer::open-local. Since: 3.0.0 + * @query_local: The vfunc for PurpleXfer::query-local. Since: 3.0.0 + * @read_local: The vfunc for PurpleXfer::read-local. Since: 3.0.0 + * @write_local: The vfunc for PurpleXfer::write-local. Since: 3.0.0 + * @data_not_sent: The vfunc for PurpleXfer::data-not-sent. Since: 3.0.0 * Base class for all #PurpleXfer's
@@ -183,6 +159,13 @@
gssize (*write)(PurpleXfer *xfer, const guchar *buffer, gsize size);
void (*ack)(PurpleXfer *xfer, const guchar *buffer, gsize size);
+ gboolean (*open_local)(PurpleXfer *xfer); + gboolean (*query_local)(PurpleXfer *xfer, const gchar *filename); + gssize (*read_local)(PurpleXfer *xfer, guchar *buffer, gssize size); + gssize (*write_local)(PurpleXfer *xfer, const guchar *buffer, gssize size); + gboolean (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer,