--- a/finch/gntxfer.c Fri Dec 27 01:06:53 2019 -0500
+++ b/finch/gntxfer.c Mon Dec 30 07:08:44 2019 +0000
@@ -55,12 +55,11 @@
static PurpleGntXferDialog *xfer_dialog = NULL;
+#define UI_DATA "finch-ui-data" gint64 last_updated_time;
gboolean notified; /* Has the completion of the transfer been notified? */
@@ -256,8 +255,7 @@
for (iter = purple_xfers_get_all(); iter; iter = iter->next) {
PurpleXfer *xfer = (PurpleXfer *)iter->data;
- PurpleGntXferUiData *data = purple_xfer_get_ui_data(xfer);
+ if (purple_xfer_get_visible(xfer)) { finch_xfer_dialog_add_xfer(xfer);
finch_xfer_dialog_update_xfer(xfer);
gnt_tree_set_selected(GNT_TREE(tree), xfer);
@@ -296,8 +294,7 @@
- data = purple_xfer_get_ui_data(xfer);
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA); finch_xfer_dialog_show();
@@ -329,20 +326,14 @@
finch_xfer_dialog_remove_xfer(PurpleXfer *xfer)
- PurpleGntXferUiData *data;
g_return_if_fail(xfer_dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = purple_xfer_get_ui_data(xfer);
+ if (!purple_xfer_get_visible(xfer)) {
+ purple_xfer_set_visible(xfer, FALSE); gnt_tree_remove(GNT_TREE(xfer_dialog->tree), xfer);
@@ -364,13 +355,14 @@
g_return_if_fail(xfer_dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = purple_xfer_get_ui_data(xfer);
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA);
+ if (!purple_xfer_get_visible(xfer)) { if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) && (xfer_dialog->auto_clear)) {
finch_xfer_dialog_remove_xfer(xfer);
@@ -414,11 +406,10 @@
g_return_if_fail(xfer_dialog != NULL);
g_return_if_fail(xfer != NULL);
- if ((data = purple_xfer_get_ui_data(xfer)) == NULL)
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA); + if (data == NULL || !purple_xfer_get_visible(xfer) || data->notified) {
- if (data->in_list == FALSE || data->notified)
current_time = g_get_monotonic_time();
if (((current_time - data->last_updated_time) < G_USEC_PER_SEC) &&
@@ -465,31 +456,33 @@
**************************************************************************/
-finch_xfer_new_xfer(PurpleXfer *xfer)
+finch_xfer_progress_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec, + G_GNUC_UNUSED gpointer data) - PurpleGntXferUiData *data;
- /* This is where we're setting xfer's "ui_data" for the first time. */
- data = g_new0(PurpleGntXferUiData, 1);
- purple_xfer_set_ui_data(xfer, data);
-finch_xfer_destroy(PurpleXfer *xfer)
- PurpleGntXferUiData *data;
- data = purple_xfer_get_ui_data(xfer);
- purple_xfer_set_ui_data(xfer, NULL);
+ finch_xfer_dialog_update_xfer(xfer); -finch_xfer_add_xfer(PurpleXfer *xfer)
+finch_xfer_status_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec, + G_GNUC_UNUSED gpointer data) + if (purple_xfer_is_cancelled(xfer)) { + finch_xfer_dialog_cancel_xfer(xfer); +finch_xfer_visible_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec, + G_GNUC_UNUSED gpointer data) + if (!purple_xfer_get_visible(xfer)) { @@ -498,38 +491,25 @@
-finch_xfer_update_progress(PurpleXfer *xfer, double percent)
- finch_xfer_dialog_update_xfer(xfer);
-finch_xfer_cancel_local(PurpleXfer *xfer)
+finch_xfer_new_xfer(PurpleXfer *xfer)
- finch_xfer_dialog_cancel_xfer(xfer);
+ PurpleGntXferUiData *data; + /* This is where we're setting xfer's "ui_data" for the first time. */ + data = g_new0(PurpleGntXferUiData, 1); + g_object_set_data_full(G_OBJECT(xfer), UI_DATA, data, g_free);
-finch_xfer_cancel_remote(PurpleXfer *xfer)
- finch_xfer_dialog_cancel_xfer(xfer);
+ g_signal_connect(xfer, "notify::progress", + G_CALLBACK(finch_xfer_progress_notify), NULL); + g_signal_connect(xfer, "notify::status", + G_CALLBACK(finch_xfer_status_notify), NULL); + g_signal_connect(xfer, "notify::visible", + G_CALLBACK(finch_xfer_visible_notify), NULL); static PurpleXferUiOps ops =
- finch_xfer_update_progress,
- finch_xfer_cancel_local,
- finch_xfer_cancel_remote,
- NULL, /* data_not_sent */
- NULL /* add_thumbnail */
/**************************************************************************
--- a/libpurple/xfer.c Fri Dec 27 01:06:53 2019 -0500
+++ b/libpurple/xfer.c Mon Dec 30 07:08:44 2019 +0000
@@ -73,7 +73,8 @@
PurpleXferStatus status; /* File Transfer's status. */
- gpointer ui_data; /* UI-specific data */
+ gboolean visible; /* Hint the UI that the transfer should PurpleXferUiOps *ui_ops; /* UI-specific operations. */
@@ -115,12 +116,26 @@
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);
@@ -169,44 +184,19 @@
g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_STATUS]);
- if(priv->type == PURPLE_XFER_TYPE_SEND) {
- case PURPLE_XFER_STATUS_ACCEPTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer);
- case PURPLE_XFER_STATUS_STARTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer);
- case PURPLE_XFER_STATUS_DONE:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer);
- case PURPLE_XFER_STATUS_CANCEL_LOCAL:
- case PURPLE_XFER_STATUS_CANCEL_REMOTE:
- purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer);
- } else if(priv->type == PURPLE_XFER_TYPE_RECEIVE) {
- case PURPLE_XFER_STATUS_ACCEPTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer);
- case PURPLE_XFER_STATUS_STARTED:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer);
- case PURPLE_XFER_STATUS_DONE:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer);
- case PURPLE_XFER_STATUS_CANCEL_LOCAL:
- case PURPLE_XFER_STATUS_CANCEL_REMOTE:
- purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer);
+purple_xfer_set_visible(PurpleXfer *xfer, gboolean visible) + PurpleXferPrivate *priv = NULL; + g_return_if_fail(PURPLE_IS_XFER(xfer)); + priv = purple_xfer_get_instance_private(xfer); + priv->visible = visible; + g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_VISIBLE]); @@ -571,12 +561,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 +609,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 +628,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);
@@ -661,7 +658,7 @@
+ purple_xfer_set_visible(xfer, TRUE); @@ -763,6 +760,17 @@
+purple_xfer_get_visible(PurpleXfer *xfer) + PurpleXferPrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_XFER(xfer), FALSE); + priv = purple_xfer_get_instance_private(xfer); purple_xfer_is_cancelled(PurpleXfer *xfer)
g_return_val_if_fail(PURPLE_IS_XFER(xfer), TRUE);
@@ -932,7 +940,6 @@
purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed)
PurpleXferPrivate *priv = NULL;
- PurpleXferUiOps *ui_ops;
g_return_if_fail(PURPLE_IS_XFER(xfer));
@@ -973,10 +980,7 @@
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops != NULL && ui_ops->update_progress != NULL)
- ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
+ g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_PROGRESS]); @@ -1045,13 +1049,18 @@
purple_xfer_set_size(PurpleXfer *xfer, goffset size)
PurpleXferPrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_XFER(xfer));
priv = purple_xfer_get_instance_private(xfer);
- g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILE_SIZE]);
+ g_object_freeze_notify(obj); + g_object_notify_by_pspec(obj, properties[PROP_FILE_SIZE]); + g_object_notify_by_pspec(obj, properties[PROP_PROGRESS]); + g_object_thaw_notify(obj); @@ -1071,13 +1080,18 @@
purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent)
PurpleXferPrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_XFER(xfer));
priv = purple_xfer_get_instance_private(xfer);
priv->bytes_sent = bytes_sent;
- g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_BYTES_SENT]);
+ g_object_freeze_notify(obj); + g_object_notify_by_pspec(obj, properties[PROP_BYTES_SENT]); + g_object_notify_by_pspec(obj, properties[PROP_PROGRESS]); + g_object_thaw_notify(obj); @@ -1206,11 +1220,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 +1246,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 +1256,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 +1273,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,16 +1316,26 @@
+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)
PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
- PurpleXferUiOps *ui_ops;
- ui_ops = purple_xfer_get_ui_ops(xfer);
if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
r = purple_xfer_read(xfer, &buffer);
@@ -1342,7 +1355,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 +1369,16 @@
+ existing_buffer = TRUE; if (priv->buffer->len < s) {
buffer = g_new(guchar, s);
result = purple_xfer_read_file(xfer, buffer, s);
@@ -1414,11 +1429,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
@@ -1434,10 +1453,6 @@
klass->ack(xfer, buffer, r);
- if (ui_ops != NULL && ui_ops->update_progress != NULL)
- ui_ops->update_progress(xfer,
- purple_xfer_get_progress(xfer));
@@ -1479,34 +1494,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); @@ -1698,24 +1723,10 @@
-purple_xfer_add(PurpleXfer *xfer)
- PurpleXferUiOps *ui_ops;
- g_return_if_fail(PURPLE_IS_XFER(xfer));
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops != NULL && ui_ops->add_xfer != NULL)
- ui_ops->add_xfer(xfer);
purple_xfer_cancel_local(PurpleXfer *xfer)
PurpleXferClass *klass = NULL;
PurpleXferPrivate *priv = NULL;
- PurpleXferUiOps *ui_ops;
g_return_if_fail(PURPLE_IS_XFER(xfer));
@@ -1779,12 +1790,6 @@
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops != NULL && ui_ops->cancel_local != NULL) {
- ui_ops->cancel_local(xfer);
@@ -1793,7 +1798,6 @@
PurpleXferClass *klass = NULL;
PurpleXferPrivate *priv = NULL;
- PurpleXferUiOps *ui_ops;
@@ -1849,11 +1853,6 @@
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
- ui_ops->cancel_remote(xfer);
@@ -1883,19 +1882,6 @@
-purple_xfer_update_progress(PurpleXfer *xfer)
- PurpleXferUiOps *ui_ops;
- g_return_if_fail(PURPLE_IS_XFER(xfer));
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops != NULL && ui_ops->update_progress != NULL) {
- ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
purple_xfer_get_thumbnail(PurpleXfer *xfer, gsize *len)
@@ -1958,38 +1944,9 @@
purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats)
- PurpleXferUiOps *ui_ops;
g_return_if_fail(PURPLE_IS_XFER(xfer));
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops && ui_ops->add_thumbnail) {
- ui_ops->add_thumbnail(xfer, formats);
-void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data)
- PurpleXferPrivate *priv = NULL;
- g_return_if_fail(PURPLE_IS_XFER(xfer));
- priv = purple_xfer_get_instance_private(xfer);
- priv->ui_data = ui_data;
- g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_UI_DATA]);
-gpointer purple_xfer_get_ui_data(PurpleXfer *xfer)
- PurpleXferPrivate *priv = NULL;
- g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
- priv = purple_xfer_get_instance_private(xfer);
+ g_signal_emit(xfer, signals[SIG_ADD_THUMBNAIL], 0, formats, NULL); /**************************************************************************
@@ -2039,8 +1996,8 @@
purple_xfer_set_status(xfer, g_value_get_enum(value));
- purple_xfer_set_ui_data(xfer, g_value_get_pointer(value));
+ purple_xfer_set_visible(xfer, g_value_get_boolean(value)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
@@ -2103,8 +2060,11 @@
g_value_set_enum(value, purple_xfer_get_status(xfer));
- g_value_set_pointer(value, purple_xfer_get_ui_data(xfer));
+ g_value_set_double(value, purple_xfer_get_progress(xfer)); + g_value_set_boolean(value, purple_xfer_get_visible(xfer)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
@@ -2127,20 +2087,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) {
@@ -2153,7 +2105,6 @@
PurpleXfer *xfer = PURPLE_XFER(object);
PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
- PurpleXferUiOps *ui_ops;
/* Close the file browser, if it's open */
purple_request_close_with_handle(xfer);
@@ -2162,12 +2113,6 @@
purple_xfer_cancel_local(xfer);
- ui_ops = purple_xfer_get_ui_ops(xfer);
- if (ui_ops != NULL && ui_ops->destroy != NULL) {
xfers = g_list_remove(xfers, xfer);
@@ -2199,6 +2144,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,
@@ -2280,11 +2233,149 @@
PURPLE_TYPE_XFER_STATUS, PURPLE_XFER_STATUS_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- properties[PROP_UI_DATA] = g_param_spec_pointer("ui-data", "UI Data",
- "The UI specific data for this xfer",
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_PROGRESS] = g_param_spec_double( + "progress", "Progress", + "The current progress of the file transfer.", -1.0, 1.0, -1.0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + properties[PROP_VISIBLE] = g_param_spec_boolean( + "Hint for UIs whether this transfer should be visible.", FALSE, + 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); + * PurpleXfer::add-thumbnail: + * @formats: A comma-separated string of allowed image formats. + * Request that a thumbnail be added to a file transfer. + signals[SIG_ADD_THUMBNAIL] = g_signal_new( + "add-thumbnail", G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, NULL, + NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); @@ -2340,30 +2431,6 @@
void *handle = purple_xfers_get_handle();
- purple_signal_register(handle, "file-recv-accept",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
- purple_signal_register(handle, "file-send-accept",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
- purple_signal_register(handle, "file-recv-start",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
- purple_signal_register(handle, "file-send-start",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
- purple_signal_register(handle, "file-send-cancel",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
- purple_signal_register(handle, "file-recv-cancel",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
- purple_signal_register(handle, "file-send-complete",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
- purple_signal_register(handle, "file-recv-complete",
- purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
purple_signal_register(handle, "file-recv-request",
purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
--- a/libpurple/xfer.h Fri Dec 27 01:06:53 2019 -0500
+++ b/libpurple/xfer.h Mon Dec 30 07:08:44 2019 +0000
@@ -31,19 +31,11 @@
#define PURPLE_TYPE_XFER_UI_OPS (purple_xfer_ui_ops_get_type())
-#define PURPLE_TYPE_PROTOCOL_XFER (purple_protocol_xfer_get_type())
-#define PURPLE_PROTOCOL_XFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PROTOCOL_XFER, PurpleProtocolXfer))
-#define PURPLE_IS_PROTOCOL_XFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_XFER))
-#define PURPLE_PROTOCOL_XFER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_XFER, PurpleProtocolXferInterface))
/**************************************************************************/
/**************************************************************************/
typedef struct _PurpleXferUiOps PurpleXferUiOps;
-typedef struct _PurpleProtocolXfer PurpleProtocolXfer;
-typedef struct _PurpleProtocolXferInterface PurpleProtocolXferInterface;
@@ -99,41 +91,6 @@
* @new_xfer: UI op that's called after a new transfer is created.
- * @destroy: UI op that's called when a transfer is being destroyed.
- * @add_xfer: UI op that's called when a transfer should be added to the UI.
- * @update_progress: UI op that's called when a transfer's progress has been
- * @cancel_local: UI op that's called when a transfer has been cancelled on the
- * @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.
@@ -143,15 +100,6 @@
void (*new_xfer)(PurpleXfer *xfer);
- void (*destroy)(PurpleXfer *xfer);
- void (*add_xfer)(PurpleXfer *xfer);
- 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 +114,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,37 +136,17 @@
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,
- * PurpleProtocolXferInterface:
- * @can_receive: A method to determine if we can receive a file.
- * @send_file: A method to determine if we can send a file.
- * @new_xfer: A method to create a new file transfer.
- * The protocol file transfer interface.
- * This interface provides file transfer callbacks for the protocol.
-struct _PurpleProtocolXferInterface
- GTypeInterface parent_iface;
- gboolean (*can_receive)(PurpleProtocolXfer *prplxfer,
- PurpleConnection *connection, const gchar *who);
- void (*send_file)(PurpleProtocolXfer *prplxfer,
- PurpleConnection *connection, const gchar *who,
- const gchar *filename);
- PurpleXfer *(*new_xfer)(PurpleProtocolXfer *prplxfer,
- PurpleConnection *connection, const gchar *who);
/**************************************************************************/
/**************************************************************************/
@@ -331,6 +264,20 @@
PurpleXferStatus purple_xfer_get_status(PurpleXfer *xfer);
+ * purple_xfer_get_visible: + * @xfer: The file transfer. + * Returns whether the UI should show the file transfer in its listing. + * Note, this is just a hint for UIs and has no effect internally. + * Returns: The visibility. +gboolean purple_xfer_get_visible(PurpleXfer *xfer); * purple_xfer_is_cancelled:
* @xfer: The file transfer.
@@ -499,6 +446,19 @@
void purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatus status);
+ * purple_xfer_set_visible: + * @xfer: The file transfer. + * @visible: The visibility. + * Sets whether the UI should show the file transfer in its listing. + * Note, this is just a hint for UIs and has no effect internally. +void purple_xfer_set_visible(PurpleXfer *xfer, gboolean visible); * purple_xfer_set_message:
* @xfer: The file transfer.
@@ -660,15 +620,6 @@
void purple_xfer_end(PurpleXfer *xfer);
- * @xfer: The file transfer.
- * Adds a new file transfer to the list of file transfers. Call this only
- * if you are not using purple_xfer_start.
-void purple_xfer_add(PurpleXfer *xfer);
* purple_xfer_cancel_local:
* @xfer: The file transfer.
@@ -700,14 +651,6 @@
void purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg);
- * purple_xfer_update_progress:
- * @xfer: The file transfer.
- * Updates file transfer progress.
-void purple_xfer_update_progress(PurpleXfer *xfer);
* purple_xfer_conversation_write:
* @xfer: The file transfer to which this message relates.
* @message: The message to display.
@@ -785,27 +728,6 @@
void purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats);
- * purple_xfer_set_ui_data:
- * @xfer: The file transfer.
- * @ui_data: A pointer to associate with this file transfer.
- * Set the UI data associated with this file transfer.
-void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data);
- * purple_xfer_get_ui_data:
- * @xfer: The file transfer.
- * Get the UI data associated with this file transfer.
- * Returns: The UI data associated with this file transfer. This is a
- * convenience field provided to the UIs--it is not
- * used by the libpurple core.
-gpointer purple_xfer_get_ui_data(PurpleXfer *xfer);
/**************************************************************************/
/* File Transfer Subsystem API */
/**************************************************************************/
@@ -870,13 +792,41 @@
/******************************************************************************
*****************************************************************************/
+#define PURPLE_TYPE_PROTOCOL_XFER (purple_protocol_xfer_get_type()) * purple_protocol_xfer_get_type:
* Returns: The #GType for the protocol xfer interface.
-GType purple_protocol_xfer_get_type(void);
+G_DECLARE_INTERFACE(PurpleProtocolXfer, purple_protocol_xfer, PURPLE, + PROTOCOL_XFER, GObject) + * PurpleProtocolXferInterface: + * @can_receive: A method to determine if we can receive a file. + * @send_file: A method to determine if we can send a file. + * @new_xfer: A method to create a new file transfer. + * The protocol file transfer interface. + * This interface provides file transfer callbacks for the protocol. +struct _PurpleProtocolXferInterface { + GTypeInterface parent_iface; + gboolean (*can_receive)(PurpleProtocolXfer *prplxfer, + PurpleConnection *connection, const gchar *who); + void (*send_file)(PurpleProtocolXfer *prplxfer, + PurpleConnection *connection, const gchar *who, + const gchar *filename); + PurpleXfer *(*new_xfer)(PurpleProtocolXfer *prplxfer, + PurpleConnection *connection, const gchar *who); * purple_protocol_xfer_can_receive:
--- a/pidgin/gtkxfer.c Fri Dec 27 01:06:53 2019 -0500
+++ b/pidgin/gtkxfer.c Mon Dec 30 07:08:44 2019 +0000
@@ -46,8 +46,6 @@
PurpleXfer *selected_xfer;
@@ -78,14 +76,11 @@
G_DEFINE_TYPE(PidginXferDialog, pidgin_xfer_dialog, GTK_TYPE_DIALOG);
+#define UI_DATA "pidgin-ui-data" gint64 last_updated_time;
static PidginXferDialog *xfer_dialog = NULL;
@@ -237,7 +232,7 @@
if (dialog == NULL || xfer == NULL)
- data = purple_xfer_get_ui_data(xfer);
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA); get_xfer_info_strings(xfer, &kbsec, &time_elapsed, &time_remaining);
@@ -695,8 +690,7 @@
- data = purple_xfer_get_ui_data(xfer);
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA); pidgin_xfer_dialog_show(dialog);
@@ -729,8 +723,6 @@
- dialog->num_transfers++;
ensure_row_selected(dialog);
update_title_progress(dialog);
@@ -744,20 +736,19 @@
g_return_if_fail(dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = purple_xfer_get_ui_data(xfer);
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA);
+ if (!purple_xfer_get_visible(xfer)) {
+ purple_xfer_set_visible(xfer, FALSE); gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter);
- dialog->num_transfers--;
ensure_row_selected(dialog);
update_title_progress(dialog);
@@ -774,13 +765,14 @@
g_return_if_fail(dialog != NULL);
g_return_if_fail(xfer != NULL);
- data = purple_xfer_get_ui_data(xfer);
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA);
+ if (!purple_xfer_get_visible(xfer)) { if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL &&
gtk_toggle_button_get_active(
@@ -789,7 +781,7 @@
- data = purple_xfer_get_ui_data(xfer);
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA); update_detailed_info(dialog, xfer);
update_title_progress(dialog);
@@ -820,11 +812,10 @@
g_return_if_fail(dialog != NULL);
g_return_if_fail(xfer != NULL);
- if ((data = purple_xfer_get_ui_data(xfer)) == NULL)
+ data = g_object_get_data(G_OBJECT(xfer), UI_DATA); + if (data == NULL || !purple_xfer_get_visible(xfer)) {
- if (data->in_list == FALSE)
current_time = g_get_monotonic_time();
if (((current_time - data->last_updated_time) < G_USEC_PER_SEC) &&
@@ -901,59 +892,8 @@
**************************************************************************/
-pidgin_xfer_new_xfer(PurpleXfer *xfer)
- PidginXferUiData *data;
- /* This is where we're setting xfer's "ui_data" for the first time. */
- data = g_new0(PidginXferUiData, 1);
- purple_xfer_set_ui_data(xfer, data);
-pidgin_xfer_destroy(PurpleXfer *xfer)
- PidginXferUiData *data;
- data = purple_xfer_get_ui_data(xfer);
- purple_xfer_set_ui_data(xfer, NULL);
-pidgin_xfer_add_xfer(PurpleXfer *xfer)
- if (xfer_dialog == NULL)
- xfer_dialog = pidgin_xfer_dialog_new();
- pidgin_xfer_dialog_add_xfer(xfer_dialog, xfer);
-pidgin_xfer_update_progress(PurpleXfer *xfer, double percent)
- pidgin_xfer_dialog_update_xfer(xfer_dialog, xfer);
-pidgin_xfer_cancel_local(PurpleXfer *xfer)
- pidgin_xfer_dialog_cancel_xfer(xfer_dialog, xfer);
-pidgin_xfer_cancel_remote(PurpleXfer *xfer)
- pidgin_xfer_dialog_cancel_xfer(xfer_dialog, xfer);
-pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats)
+pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats, + G_GNUC_UNUSED gpointer data) purple_debug_info("xfer", "creating thumbnail for transfer\n");
@@ -1013,18 +953,61 @@
+pidgin_xfer_progress_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec, + G_GNUC_UNUSED gpointer data) + pidgin_xfer_dialog_update_xfer(xfer_dialog, xfer); +pidgin_xfer_status_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec, + G_GNUC_UNUSED gpointer data) + if (purple_xfer_is_cancelled(xfer)) { + pidgin_xfer_dialog_cancel_xfer(xfer_dialog, xfer); +pidgin_xfer_visible_notify(PurpleXfer *xfer, G_GNUC_UNUSED GParamSpec *pspec, + G_GNUC_UNUSED gpointer data) + if (!purple_xfer_get_visible(xfer)) { + if (xfer_dialog == NULL) { + xfer_dialog = pidgin_xfer_dialog_new(); + pidgin_xfer_dialog_add_xfer(xfer_dialog, xfer); +pidgin_xfer_new_xfer(PurpleXfer *xfer) + PidginXferUiData *data; + /* This is where we're setting xfer's "ui_data" for the first time. */ + data = g_new0(PidginXferUiData, 1); + g_object_set_data_full(G_OBJECT(xfer), UI_DATA, data, g_free); + g_signal_connect(xfer, "add-thumbnail", + G_CALLBACK(pidgin_xfer_add_thumbnail), NULL); + g_signal_connect(xfer, "notify::progress", + G_CALLBACK(pidgin_xfer_progress_notify), NULL); + g_signal_connect(xfer, "notify::status", + G_CALLBACK(pidgin_xfer_status_notify), NULL); + g_signal_connect(xfer, "notify::visible", + G_CALLBACK(pidgin_xfer_visible_notify), NULL); static PurpleXferUiOps ops =
- pidgin_xfer_update_progress,
- pidgin_xfer_cancel_local,
- pidgin_xfer_cancel_remote,
- pidgin_xfer_add_thumbnail
/**************************************************************************