Thu, 07 Aug 2025 21:40:13 -0500
Add an avatar-for-display property to Purple.ContactInfo
Testing Done:
Ran the tests under valgrind and called in the turtles.
Reviewed at https://reviews.imfreedom.org/r/4086/
/* * Purple - Internet Messaging Library * Copyright (C) Pidgin Developers <devel@pidgin.im> * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this library; if not, see <https://www.gnu.org/licenses/>. */ #include "purpleaccount.h" #include "purplecontactinfo.h" #include "purpleenums.h" #include "purplefiletransfer.h" #include "util.h" enum { PROP_0, PROP_ACCOUNT, PROP_REMOTE, PROP_INITIATOR, PROP_CANCELLABLE, PROP_STATE, PROP_ERROR, PROP_LOCAL_FILE, PROP_FILENAME, PROP_FILE_SIZE, PROP_CONTENT_TYPE, PROP_MESSAGE, N_PROPERTIES, }; static GParamSpec *properties[N_PROPERTIES] = {NULL, }; struct _PurpleFileTransfer { GObject parent; PurpleAccount *account; PurpleContactInfo *remote; PurpleContactInfo *initiator; GCancellable *cancellable; PurpleFileTransferState state; GError *error; GFile *local_file; char *filename; guint64 file_size; char *content_type; char *message; }; /****************************************************************************** * Helpers *****************************************************************************/ static void purple_file_transfer_set_account(PurpleFileTransfer *transfer, PurpleAccount *account) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); g_return_if_fail(PURPLE_IS_ACCOUNT(account)); if(g_set_object(&transfer->account, account)) { g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_ACCOUNT]); } } static void purple_file_transfer_set_remote(PurpleFileTransfer *transfer, PurpleContactInfo *remote) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); g_return_if_fail(PURPLE_IS_CONTACT_INFO(remote)); if(g_set_object(&transfer->remote, remote)) { g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_REMOTE]); } } static void purple_file_transfer_set_initiator(PurpleFileTransfer *transfer, PurpleContactInfo *initiator) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); g_return_if_fail(PURPLE_IS_CONTACT_INFO(initiator)); if(g_set_object(&transfer->initiator, initiator)) { g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_INITIATOR]); } } static void purple_file_transfer_set_filename(PurpleFileTransfer *transfer, const char *filename) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); g_return_if_fail(!purple_strempty(filename)); if(g_set_str(&transfer->filename, filename)) { g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_FILENAME]); } } static void purple_file_transfer_set_file_size(PurpleFileTransfer *transfer, guint64 size) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); if(transfer->file_size != size) { transfer->file_size = size; g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_FILE_SIZE]); } } static char * purple_file_transfer_get_local_file_name_and_size(GFile *local_file, guint64 *file_size) { GError *error = NULL; GFileInfo *info = NULL; const char *display_name = NULL; char *ret = NULL; info = g_file_query_info(local_file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, &error); if(error != NULL) { char *path = g_file_get_path(local_file); g_warning("failed to query %s: %s", path, error->message); g_clear_pointer(&path, g_free); g_clear_error(&error); g_clear_object(&info); return NULL; } display_name = g_file_info_get_attribute_string(info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); /* display_name is only valid as long as the GFileInfo object is around, * but it is freed before this function returns, so we need to dupe the * display name attribute to return it. */ ret = g_strdup(display_name); if(file_size != NULL) { *file_size = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_STANDARD_SIZE); } g_clear_object(&info); return ret; } /****************************************************************************** * GObject Implementation *****************************************************************************/ G_DEFINE_FINAL_TYPE(PurpleFileTransfer, purple_file_transfer, G_TYPE_OBJECT) static void purple_file_transfer_finalize(GObject *obj) { PurpleFileTransfer *transfer = PURPLE_FILE_TRANSFER(obj); g_clear_object(&transfer->account); g_clear_object(&transfer->remote); g_clear_object(&transfer->initiator); g_clear_object(&transfer->cancellable); g_clear_error(&transfer->error); g_clear_object(&transfer->local_file); g_clear_pointer(&transfer->filename, g_free); g_clear_pointer(&transfer->content_type, g_free); g_clear_pointer(&transfer->message, g_free); G_OBJECT_CLASS(purple_file_transfer_parent_class)->finalize(obj); } static void purple_file_transfer_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { PurpleFileTransfer *transfer = PURPLE_FILE_TRANSFER(obj); switch(param_id) { case PROP_ACCOUNT: g_value_set_object(value, purple_file_transfer_get_account(transfer)); break; case PROP_REMOTE: g_value_set_object(value, purple_file_transfer_get_remote(transfer)); break; case PROP_INITIATOR: g_value_set_object(value, purple_file_transfer_get_initiator(transfer)); break; case PROP_CANCELLABLE: g_value_set_object(value, purple_file_transfer_get_cancellable(transfer)); break; case PROP_STATE: g_value_set_enum(value, purple_file_transfer_get_state(transfer)); break; case PROP_ERROR: g_value_set_boxed(value, purple_file_transfer_get_error(transfer)); break; case PROP_LOCAL_FILE: g_value_set_object(value, purple_file_transfer_get_local_file(transfer)); break; case PROP_FILENAME: g_value_set_string(value, purple_file_transfer_get_filename(transfer)); break; case PROP_FILE_SIZE: g_value_set_uint64(value, purple_file_transfer_get_file_size(transfer)); break; case PROP_CONTENT_TYPE: g_value_set_string(value, purple_file_transfer_get_content_type(transfer)); break; case PROP_MESSAGE: g_value_set_string(value, purple_file_transfer_get_message(transfer)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_file_transfer_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { PurpleFileTransfer *transfer = PURPLE_FILE_TRANSFER(obj); switch(param_id) { case PROP_ACCOUNT: purple_file_transfer_set_account(transfer, g_value_get_object(value)); break; case PROP_REMOTE: purple_file_transfer_set_remote(transfer, g_value_get_object(value)); break; case PROP_INITIATOR: purple_file_transfer_set_initiator(transfer, g_value_get_object(value)); break; case PROP_STATE: purple_file_transfer_set_state(transfer, g_value_get_enum(value)); break; case PROP_ERROR: purple_file_transfer_set_error(transfer, g_value_get_boxed(value)); break; case PROP_LOCAL_FILE: purple_file_transfer_set_local_file(transfer, g_value_get_object(value)); break; case PROP_FILENAME: purple_file_transfer_set_filename(transfer, g_value_get_string(value)); break; case PROP_FILE_SIZE: purple_file_transfer_set_file_size(transfer, g_value_get_uint64(value)); break; case PROP_CONTENT_TYPE: purple_file_transfer_set_content_type(transfer, g_value_get_string(value)); break; case PROP_MESSAGE: purple_file_transfer_set_message(transfer, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_file_transfer_init(PurpleFileTransfer *transfer) { transfer->cancellable = g_cancellable_new(); } static void purple_file_transfer_class_init(PurpleFileTransferClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = purple_file_transfer_finalize; obj_class->get_property = purple_file_transfer_get_property; obj_class->set_property = purple_file_transfer_set_property; /** * PurpleFileTransfer:account: * * The account that this file transfer is for. * * Since: 3.0 */ properties[PROP_ACCOUNT] = g_param_spec_object( "account", NULL, NULL, PURPLE_TYPE_ACCOUNT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:remote: * * The [class@ContactInfo] for the remote user of this file transfer. * * Since: 3.0 */ properties[PROP_REMOTE] = g_param_spec_object( "remote", NULL, NULL, PURPLE_TYPE_CONTACT_INFO, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:initiator: * * The [class@ContactInfo] that initiated this file transfer. * * Since: 3.0 */ properties[PROP_INITIATOR] = g_param_spec_object( "initiator", NULL, NULL, PURPLE_TYPE_CONTACT_INFO, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:cancellable: * * The [class@Gio.Cancellable] for this transfer. It may be used to cancel * the file transfer at any time. * * Since: 3.0 */ properties[PROP_CANCELLABLE] = g_param_spec_object( "cancellable", NULL, NULL, G_TYPE_CANCELLABLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:state: * * The state of the transfer. * * This is typically only set by the protocol plugin that is performing the * file transfer. * * If the state is set to error then [property@FileTransfer:error] * should be set. * * Since: 3.0 */ properties[PROP_STATE] = g_param_spec_enum( "state", NULL, NULL, PURPLE_TYPE_FILE_TRANSFER_STATE, PURPLE_FILE_TRANSFER_STATE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:error: * * A #GError representing the failure of the transfer. This should only be * set if [property@FileTransfer:state] is set to error. * * This should be used to tell the user about network issues or if the * transfer was cancelled and so on. * * Since: 3.0 */ properties[PROP_ERROR] = g_param_spec_boxed( "error", NULL, NULL, G_TYPE_ERROR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:local-file: * * The local file that is being sent or received. * * When sending a file, this is the file that's being sent. When receiving * a file, this is the file where the transfer is being written. * * Since: 3.0 */ properties[PROP_LOCAL_FILE] = g_param_spec_object( "local-file", NULL, NULL, G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:filename: * * The base filename for the transfer. This is used as the default filename * for the receiving side. * * Since: 3.0 */ properties[PROP_FILENAME] = g_param_spec_string( "filename", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:file-size: * * The size of the file in bytes. A value of %0 typically means the size is * unknown, but it is possible to transfer empty files as well. * * Since: 3.0 */ properties[PROP_FILE_SIZE] = g_param_spec_uint64( "file-size", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:content-type: * * The content or media type of the file that's being transferred. This is * meant to be used as a hint to user interfaces so they can provide * previews or appropriate actions for the files. * * See the [Media Types page](https://www.iana.org/assignments/media-types/media-types.xhtml) * for more information. * * Since: 3.0 */ properties[PROP_CONTENT_TYPE] = g_param_spec_string( "content-type", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * PurpleFileTransfer:message: * * Some protocols support sending a message with the file transfer. This * field is to hold that message. * * Since: 3.0 */ properties[PROP_MESSAGE] = g_param_spec_string( "message", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, N_PROPERTIES, properties); } /****************************************************************************** * Public API *****************************************************************************/ PurpleFileTransfer * purple_file_transfer_new_send(PurpleAccount *account, PurpleContactInfo *remote, GFile *local_file) { PurpleFileTransfer *transfer = NULL; gchar *filename = NULL; guint64 file_size = 0; g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(remote), NULL); filename = purple_file_transfer_get_local_file_name_and_size(local_file, &file_size); transfer = g_object_new( PURPLE_TYPE_FILE_TRANSFER, "account", account, "remote", remote, "initiator", purple_account_get_contact_info(account), "local-file", local_file, "filename", filename, "file-size", file_size, NULL); g_clear_pointer(&filename, g_free); return transfer; } PurpleFileTransfer * purple_file_transfer_new_receive(PurpleAccount *account, PurpleContactInfo *remote, const char *filename, guint64 file_size) { g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(remote), NULL); g_return_val_if_fail(!purple_strempty(filename), NULL); return g_object_new( PURPLE_TYPE_FILE_TRANSFER, "account", account, "remote", remote, "initiator", remote, "filename", filename, "file-size", file_size, NULL); } PurpleAccount * purple_file_transfer_get_account(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->account; } PurpleContactInfo * purple_file_transfer_get_remote(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->remote; } PurpleContactInfo * purple_file_transfer_get_initiator(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->initiator; } GCancellable * purple_file_transfer_get_cancellable(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->cancellable; } PurpleFileTransferState purple_file_transfer_get_state(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), PURPLE_FILE_TRANSFER_STATE_UNKNOWN); return transfer->state; } void purple_file_transfer_set_state(PurpleFileTransfer *transfer, PurpleFileTransferState state) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); if(transfer->state != state) { transfer->state = state; g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_STATE]); } } GError * purple_file_transfer_get_error(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->error; } void purple_file_transfer_set_error(PurpleFileTransfer *transfer, GError *error) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); if(transfer->error != error) { g_clear_error(&transfer->error); transfer->error = error; g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_ERROR]); } } GFile * purple_file_transfer_get_local_file(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->local_file; } void purple_file_transfer_set_local_file(PurpleFileTransfer *transfer, GFile *local_file) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); g_return_if_fail(G_IS_FILE(local_file)); if(g_set_object(&transfer->local_file, local_file)) { g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_LOCAL_FILE]); } } const char * purple_file_transfer_get_filename(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->filename; } guint64 purple_file_transfer_get_file_size(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), 0); return transfer->file_size; } const char * purple_file_transfer_get_content_type(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->content_type; } void purple_file_transfer_set_content_type(PurpleFileTransfer *transfer, const char *content_type) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); if(g_set_str(&transfer->content_type, content_type)) { g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_CONTENT_TYPE]); } } const char * purple_file_transfer_get_message(PurpleFileTransfer *transfer) { g_return_val_if_fail(PURPLE_IS_FILE_TRANSFER(transfer), NULL); return transfer->message; } void purple_file_transfer_set_message(PurpleFileTransfer *transfer, const char *message) { g_return_if_fail(PURPLE_IS_FILE_TRANSFER(transfer)); if(g_set_str(&transfer->message, message)) { g_object_notify_by_pspec(G_OBJECT(transfer), properties[PROP_MESSAGE]); } }