Sun, 10 Aug 2025 23:44:08 +0800
Add Purple.Conversation.find_message_by_id
The method was added so that a protocol or plugin could easily lookup
for the reference for a message. This will be especially useful when a
protocol received a quoted message but only with an id.
/* * 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 <birb.h> #include "purpleimage.h" struct _PurpleImage { GObject parent; char *filename; GBytes *contents; }; enum { PROP_0, PROP_CONTENTS, PROP_DATA, PROP_FILENAME, PROP_SIZE, N_PROPERTIES, }; static GParamSpec *properties[N_PROPERTIES] = {NULL, }; /****************************************************************************** * Helpers *****************************************************************************/ static void purple_image_set_filename(PurpleImage *image, const char *filename) { g_return_if_fail(PURPLE_IS_IMAGE(image)); if(g_set_str(&image->filename, filename)) { g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_FILENAME]); } } static void purple_image_set_contents(PurpleImage *image, GBytes *contents) { g_return_if_fail(PURPLE_IS_IMAGE(image)); if(image->contents != contents) { GObject *obj = G_OBJECT(image); g_clear_pointer(&image->contents, g_bytes_unref); if(contents != NULL) { image->contents = g_bytes_ref(contents); } g_object_freeze_notify(obj); g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_CONTENTS]); g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_DATA]); g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_SIZE]); g_object_thaw_notify(obj); } } /****************************************************************************** * GObject Implementation *****************************************************************************/ G_DEFINE_FINAL_TYPE(PurpleImage, purple_image, G_TYPE_OBJECT); static void purple_image_finalize(GObject *obj) { PurpleImage *image = PURPLE_IMAGE(obj); g_clear_pointer(&image->contents, g_bytes_unref); g_clear_pointer(&image->filename, g_free); G_OBJECT_CLASS(purple_image_parent_class)->finalize(obj); } static void purple_image_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { PurpleImage *image = PURPLE_IMAGE(obj); gsize size = 0; switch (param_id) { case PROP_CONTENTS: g_value_set_boxed(value, purple_image_get_contents(image)); break; case PROP_DATA: g_value_set_pointer(value, (gpointer)purple_image_get_data(image, NULL)); break; case PROP_FILENAME: g_value_set_string(value, purple_image_get_filename(image)); break; case PROP_SIZE: purple_image_get_data(image, &size); g_value_set_uint64(value, size); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_image_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) { PurpleImage *image = PURPLE_IMAGE(obj); switch (param_id) { case PROP_FILENAME: purple_image_set_filename(image, g_value_get_string(value)); break; case PROP_CONTENTS: purple_image_set_contents(image, g_value_get_boxed(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); break; } } static void purple_image_init(G_GNUC_UNUSED PurpleImage *image) { } static void purple_image_class_init(PurpleImageClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = purple_image_finalize; obj_class->get_property = purple_image_get_property; obj_class->set_property = purple_image_set_property; /** * PurpleImage:contents: * * The contents of the image. * * Since: 3.0 */ properties[PROP_CONTENTS] = g_param_spec_boxed( "contents", NULL, NULL, G_TYPE_BYTES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleImage:data: * * The raw image data. * * Generally the [property@Image:contents] property should be used, but if * just the data is necessary this saves a step. * * Since: 3.0 */ properties[PROP_DATA] = g_param_spec_pointer( "data", NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * PurpleImage:filename: * * The filename for the image if one was provided. * * 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 | G_PARAM_EXPLICIT_NOTIFY); /** * PurpleImage:size: * * The size of the image in bytes. * * Since: 3.0 */ properties[PROP_SIZE] = g_param_spec_uint64( "size", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, N_PROPERTIES, properties); } /****************************************************************************** * Public API *****************************************************************************/ GBytes * purple_image_get_contents(PurpleImage *image) { g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL); return image->contents; } gconstpointer purple_image_get_data(PurpleImage *image, gsize *size) { g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL); if(image->contents != NULL) { return g_bytes_get_data(image->contents, size); } return NULL; } const char * purple_image_get_filename(PurpleImage *image) { g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL); return image->filename; } PurpleImage * purple_image_new_from_bytes(GBytes *bytes) { g_return_val_if_fail(bytes != NULL, NULL); return g_object_new( PURPLE_TYPE_IMAGE, "contents", bytes, NULL); } PurpleImage * purple_image_new_from_data(gconstpointer data, gsize size) { PurpleImage *image = NULL; GBytes *contents = NULL; g_return_val_if_fail(data != NULL, NULL); g_return_val_if_fail(size > 0, NULL); contents = g_bytes_new(data, size); image = g_object_new( PURPLE_TYPE_IMAGE, "contents", contents, NULL); g_bytes_unref(contents); return image; } PurpleImage * purple_image_new_from_filename(const char *filename, GError **error) { PurpleImage *image = NULL; GBytes *bytes = NULL; GError *local_error = NULL; char *contents = NULL; gsize length = 0; if(!g_file_get_contents(filename, &contents, &length, &local_error)) { g_propagate_error(error, local_error); return NULL; } bytes = g_bytes_new_take(contents, length); image = g_object_new( PURPLE_TYPE_IMAGE, "contents", bytes, "filename", filename, NULL ); g_bytes_unref(bytes); return image; } PurpleImage * purple_image_new_from_resource(const char *path, GError **error) { PurpleImage *image = NULL; GBytes *contents = NULL; GError *local_error = NULL; g_return_val_if_fail(!birb_str_is_empty(path), NULL); contents = g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, &local_error); if(local_error != NULL) { g_clear_pointer(&contents, g_bytes_unref); g_propagate_error(error, local_error); return NULL; } image = g_object_new( PURPLE_TYPE_IMAGE, "contents", contents, NULL); g_clear_pointer(&contents, g_bytes_unref); return image; } gboolean purple_image_save(PurpleImage *image, const char *filename, GError **error) { gconstpointer data = NULL; gsize size = 0; g_return_val_if_fail(PURPLE_IS_IMAGE(image), FALSE); g_return_val_if_fail(!birb_str_is_empty(filename), FALSE); data = g_bytes_get_data(image->contents, &size); return g_file_set_contents(filename, data, size, error); }