pidgin/pidgin

Convert xfer I/O operations from UI ops to signals.

14 months ago, Elliott Sales de Andrade
7f38c3cc5c91
Parents 2a80cd63e914
Children 09402b6a28cf
Convert xfer I/O operations from UI ops to signals.
--- a/ChangeLog.API Fri Dec 20 02:44:13 2019 +0000
+++ b/ChangeLog.API Sat Dec 21 03:12:10 2019 -0500
@@ -475,6 +475,10 @@
* PurplePluginProtocolInfo.add_buddies_with_invite
* PurplePluginProtocolInfo.get_cb_away
* PurpleValue, use GValue instead.
+ * PurpleXferUiOps.data_not_sent. Use PurpleXfer::data-not-sent
+ instead.
+ * PurpleXferUiOps.ui_read. Use PurpleXfer::read-local instead.
+ * PurpleXferUiOps.ui_write. Use PurpleXfer::write-local instead.
* serv_got_attention
* serv_send_attention
* struct _PurpleAttentionType
--- a/finch/gntxfer.c Fri Dec 20 02:44:13 2019 +0000
+++ b/finch/gntxfer.c Sat Dec 21 03:12:10 2019 -0500
@@ -526,9 +526,6 @@
finch_xfer_update_progress,
finch_xfer_cancel_local,
finch_xfer_cancel_remote,
- NULL, /* ui_write */
- NULL, /* ui_read */
- NULL, /* data_not_sent */
NULL /* add_thumbnail */
};
--- 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 */
+enum
+{
+ SIG_OPEN_LOCAL,
+ SIG_QUERY_LOCAL,
+ SIG_READ_LOCAL,
+ SIG_WRITE_LOCAL,
+ SIG_DATA_NOT_SENT,
+ SIG_LAST
+};
+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 @@
}
}
+static gboolean
+do_query_local(PurpleXfer *xfer, const gchar *filename)
+{
+ GStatBuf st;
+
+ if (g_stat(filename, &st) == -1) {
+ purple_xfer_show_file_error(xfer, filename);
+ return FALSE;
+ }
+
+ purple_xfer_set_size(xfer, st.st_size);
+ return TRUE;
+}
+
void
purple_xfer_request_accepted(PurpleXfer *xfer, const gchar *filename)
{
PurpleXferClass *klass = NULL;
PurpleXferPrivate *priv = NULL;
- GStatBuf st;
gchar *msg, *utf8, *base;
PurpleBuddy *buddy;
@@ -606,8 +631,7 @@
if (priv->type == PURPLE_XFER_TYPE_SEND) {
/* Sending a file */
/* Check the filename. */
- PurpleXferUiOps *ui_ops;
- ui_ops = purple_xfer_get_ui_ops(xfer);
+ gboolean query_status;
#ifdef _WIN32
if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
@@ -626,18 +650,13 @@
return;
}
- 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);
- g_object_unref(xfer);
- return;
- }
-
- purple_xfer_set_local_filename(xfer, filename);
- purple_xfer_set_size(xfer, st.st_size);
- } else {
- purple_xfer_set_local_filename(xfer, filename);
+ g_signal_emit(xfer, signals[SIG_QUERY_LOCAL], 0, filename,
+ &query_status);
+ if (!query_status) {
+ g_object_unref(xfer);
+ return;
}
+ 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);
}
+static gssize
+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 -1;
+ }
+
+ return fwrite(buffer, 1, size, priv->dest_fp);
+}
+
gboolean
purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size)
{
PurpleXferPrivate *priv = NULL;
- PurpleXferUiOps *ui_ops;
gsize wc;
goffset remaining;
gboolean fs_known;
@@ -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 @@
size = remaining;
}
- if (ui_ops && ui_ops->ui_write) {
- wc = ui_ops->ui_write(xfer, buffer, size);
- } else {
- if (priv->dest_fp == NULL) {
- purple_debug_error("xfer",
- "File is not opened for writing\n");
- purple_xfer_cancel_local(xfer);
- return FALSE;
- }
- wc = fwrite(buffer, 1, size, priv->dest_fp);
- }
+ g_signal_emit(xfer, signals[SIG_WRITE_LOCAL], 0, buffer, size, &wc);
if (wc != size) {
purple_debug_error("xfer",
@@ -1258,51 +1278,39 @@
return TRUE;
}
+static gssize
+do_read_local(PurpleXfer *xfer, guchar *buffer, gssize size)
+{
+ PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
+ gssize got_len;
+
+ if (priv->dest_fp == NULL) {
+ purple_debug_error("xfer", "File is not opened for reading");
+ return -1;
+ }
+
+ 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.");
+ return -1;
+ }
+
+ return got_len;
+}
+
gssize
purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size)
{
- PurpleXferPrivate *priv = NULL;
- PurpleXferUiOps *ui_ops;
gssize got_len;
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) {
- g_free(buffer_got);
- purple_debug_error("xfer",
- "Got too much data from UI.\n");
- purple_xfer_cancel_local(xfer);
- return -1;
- }
-
- if (got_len > 0)
- memcpy(buffer, buffer_got, got_len);
- g_free(buffer_got);
- } else {
- if (priv->dest_fp == NULL) {
- purple_debug_error("xfer",
- "File is not opened for reading\n");
- purple_xfer_cancel_local(xfer);
- return -1;
- }
- got_len = fread(buffer, 1, size, priv->dest_fp);
- if ((got_len < 0 || (gsize)got_len != size) &&
- ferror(priv->dest_fp))
- {
- purple_debug_error("xfer",
- "Unable to read file.\n");
- purple_xfer_cancel_local(xfer);
- return -1;
- }
+ 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);
+ return -1;
}
if (got_len > 0) {
@@ -1313,6 +1321,19 @@
return got_len;
}
+static gboolean
+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);
+ }
+
+ return TRUE;
+}
+
static void
do_transfer(PurpleXfer *xfer)
{
@@ -1342,7 +1363,8 @@
(gsize)purple_xfer_get_bytes_remaining(xfer),
(gsize)priv->current_buffer_size
);
- gboolean read = TRUE;
+ 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 @@
}
if (priv->buffer) {
+ existing_buffer = TRUE;
if (priv->buffer->len < s) {
s -= priv->buffer->len;
- read = TRUE;
+ read_more = TRUE;
} else {
- read = FALSE;
+ read_more = FALSE;
}
}
- if (read) {
+ if (read_more) {
buffer = g_new(guchar, s);
result = purple_xfer_read_file(xfer, buffer, s);
if (result == 0) {
@@ -1414,11 +1437,15 @@
*/
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);
+ gboolean result;
+ g_signal_emit(xfer, signals[SIG_DATA_NOT_SENT], 0, buffer + r,
+ result - r, &result);
+ if (!result) {
+ purple_xfer_cancel_local(xfer);
+ }
}
- if (priv->buffer) {
+ if (existing_buffer && priv->buffer) {
/*
* Remove what we wrote
* If we wrote the whole buffer the byte array will be empty
@@ -1479,34 +1506,44 @@
do_transfer(xfer);
}
+static gboolean
+do_open_local(PurpleXfer *xfer)
+{
+ PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
+
+ 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));
+ return FALSE;
+ }
+
+ 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));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
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");
g_return_if_reached();
}
- 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);
- return;
- }
-
- 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);
- return;
- }
+ g_signal_emit(xfer, signals[SIG_OPEN_LOCAL], 0, &open_status);
+ if (!open_status) {
+ purple_xfer_cancel_local(xfer);
}
if (priv->fd != -1) {
@@ -2127,20 +2164,12 @@
purple_xfer_constructed(GObject *object)
{
PurpleXfer *xfer = PURPLE_XFER(object);
- PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
PurpleXferUiOps *ui_ops;
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 = NULL;
- } else {
- priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
- }
-
if (ui_ops != NULL && ui_ops->new_xfer != NULL) {
ui_ops->new_xfer(xfer);
}
@@ -2199,6 +2228,14 @@
klass->write = do_write;
klass->read = do_read;
+ 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 */
+
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);
+
+ /* Signals */
+
+ /**
+ * 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).
+ *
+ * Since: 3.0.0
+ */
+ 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
+ * cancel).
+ *
+ * Since: 3.0.0
+ */
+ 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,
+ G_TYPE_STRING);
+
+ /**
+ * 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).
+ *
+ * Since: 3.0.0
+ */
+ 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
+ * error.
+ *
+ * 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
+ * @size on error.
+ *
+ * Since: 3.0.0
+ */
+ 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
+ * will cancel).
+ *
+ * Since: 3.0.0
+ */
+ 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);
}
PurpleXfer *
--- 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 @@
* local end.
* @cancel_remote: UI op that's called when a transfer has been cancelled on
* the remote end.
- * @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
- * error.
- * <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
- * cancel).
- * @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
- * data.
- * <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,
+ gsize size);
+
/*< private >*/
gpointer reserved[4];
};
--- a/pidgin/gtkxfer.c Fri Dec 20 02:44:13 2019 +0000
+++ b/pidgin/gtkxfer.c Sat Dec 21 03:12:10 2019 -0500
@@ -1021,9 +1021,6 @@
pidgin_xfer_update_progress,
pidgin_xfer_cancel_local,
pidgin_xfer_cancel_remote,
- NULL,
- NULL,
- NULL,
pidgin_xfer_add_thumbnail
};