--- a/libpurple/meson.build Fri Sep 23 03:44:40 2022 -0500
+++ b/libpurple/meson.build Fri Sep 23 03:47:08 2022 -0500
@@ -45,6 +45,7 @@
'purplechatconversation.c',
'purpleconnectionerrorinfo.c',
'purpleconversationmanager.c',
'purpleconversationuiops.c',
@@ -147,6 +148,7 @@
'purplechatconversation.h',
'purpleconnectionerrorinfo.h',
'purpleconversationmanager.h',
'purpleconversationuiops.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplecontact.c Fri Sep 23 03:47:08 2022 -0500
@@ -0,0 +1,438 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * This program 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 program 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 program; if not, see <https://www.gnu.org/licenses/>. +#include "purplecontact.h" + PurpleAccount *account; + PurplePresence *presence; +static GParamSpec *properties[N_PROPERTIES] = {NULL, }; +G_DEFINE_TYPE(PurpleContact, purple_contact, G_TYPE_OBJECT) +/****************************************************************************** + *****************************************************************************/ +purple_contact_set_account(PurpleContact *contact, PurpleAccount *account) { + g_return_if_fail(PURPLE_IS_CONTACT(contact)); + g_return_if_fail(PURPLE_IS_ACCOUNT(account)); + if(g_set_object(&contact->account, account)) { + g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_ACCOUNT]); +purple_contact_set_id(PurpleContact *contact, const gchar *id) { + g_return_if_fail(PURPLE_IS_CONTACT(contact)); + contact->id = g_strdup(id); + contact->id = g_uuid_string_random(); + g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_ID]); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +purple_contact_get_property(GObject *obj, guint param_id, GValue *value, + PurpleContact *contact = PURPLE_CONTACT(obj); + g_value_set_string(value, purple_contact_get_id(contact)); + g_value_set_object(value, purple_contact_get_account(contact)); + g_value_set_string(value, purple_contact_get_username(contact)); + case PROP_DISPLAY_NAME: + g_value_set_string(value, purple_contact_get_display_name(contact)); + g_value_set_string(value, purple_contact_get_alias(contact)); + g_value_set_object(value, purple_contact_get_avatar(contact)); + g_value_set_object(value, purple_contact_get_presence(contact)); + g_value_set_object(value, purple_contact_get_tags(contact)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +purple_contact_set_property(GObject *obj, guint param_id, const GValue *value, + PurpleContact *contact = PURPLE_CONTACT(obj); + purple_contact_set_id(contact, g_value_get_string(value)); + purple_contact_set_account(contact, g_value_get_object(value)); + purple_contact_set_username(contact, g_value_get_string(value)); + case PROP_DISPLAY_NAME: + purple_contact_set_display_name(contact, g_value_get_string(value)); + purple_contact_set_alias(contact, g_value_get_string(value)); + purple_contact_set_avatar(contact, g_value_get_object(value)); + purple_contact_set_presence(contact, g_value_get_object(value)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +purple_contact_dispose(GObject *obj) { + PurpleContact *contact = PURPLE_CONTACT(obj); + g_clear_object(&contact->account); + g_clear_object(&contact->avatar); + g_clear_object(&contact->presence); + g_clear_object(&contact->tags); + G_OBJECT_CLASS(purple_contact_parent_class)->dispose(obj); +purple_contact_finalize(GObject *obj) { + PurpleContact *contact = PURPLE_CONTACT(obj); + g_clear_pointer(&contact->id, g_free); + g_clear_pointer(&contact->username, g_free); + g_clear_pointer(&contact->display_name, g_free); + g_clear_pointer(&contact->alias, g_free); + G_OBJECT_CLASS(purple_contact_parent_class)->finalize(obj); +purple_contact_constructed(GObject *obj) { + PurpleContact *contact = NULL; + G_OBJECT_CLASS(purple_contact_parent_class)->constructed(obj); + contact = PURPLE_CONTACT(obj); + if(contact->id == NULL) { + purple_contact_set_id(contact, NULL); +purple_contact_init(PurpleContact *contact) { + contact->tags = purple_tags_new(); +purple_contact_class_init(PurpleContactClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + obj_class->get_property = purple_contact_get_property; + obj_class->set_property = purple_contact_set_property; + obj_class->constructed = purple_contact_constructed; + obj_class->dispose = purple_contact_dispose; + obj_class->finalize = purple_contact_finalize; + * The protocol specific id for the contact. + properties[PROP_ID] = g_param_spec_string( + "The id of the contact", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + * PurpleContact:account: + * The account that this contact belongs to. + properties[PROP_ACCOUNT] = g_param_spec_object( + "The account this contact belongs to", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + * PurpleContact:username: + * The username for this contact. In rare cases this can change, like when + * a user changes their "nick" on IRC which is their user name. + properties[PROP_USERNAME] = g_param_spec_string( + "username", "username", + "The username of the contact", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + * PurpleContact:display-name: + * The display name for this contact. This is generally set by the person + * the contact is representing and controlled via the protocol plugin. + properties[PROP_DISPLAY_NAME] = g_param_spec_string( + "display-name", "display-name", + "The display name of the contact", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + * The alias for this contact. This is controlled by the libpurple user and + * may be used by the protocol if it allows for aliasing. + properties[PROP_ALIAS] = g_param_spec_string( + "The alias of the contact.", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + * PurpleContact:avatar: + * The avatar for this contact. This is typically controlled by the protocol + * and should only be read by others. + properties[PROP_AVATAR] = g_param_spec_object( + "The avatar of the contact", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + * PurpleContact:presence: + * The [class@Purple.Presence] for this contact. This is typically + * controlled by the protocol and should only be read by others. + properties[PROP_PRESENCE] = g_param_spec_object( + "presence", "presence", + "The presence of the contact", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + * The [class@Purple.Tags] for this contact. + properties[PROP_TAGS] = g_param_spec_object( + "The tags for the contact", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); +/****************************************************************************** + *****************************************************************************/ +purple_contact_new(PurpleAccount *account, const gchar *username) { + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); + g_return_val_if_fail(username != NULL, NULL); + return g_object_new(PURPLE_TYPE_CONTACT, +purple_contact_get_account(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); + return contact->account; +purple_contact_get_id(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); +purple_contact_get_username(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); + return contact->username; +purple_contact_set_username(PurpleContact *contact, const gchar *username) { + g_return_if_fail(PURPLE_IS_CONTACT(contact)); + g_return_if_fail(username != NULL); + g_free(contact->username); + contact->username = g_strdup(username); + g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_USERNAME]); +purple_contact_get_display_name(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); + return contact->display_name; +purple_contact_set_display_name(PurpleContact *contact, + const gchar *display_name) + g_return_if_fail(PURPLE_IS_CONTACT(contact)); + g_free(contact->display_name); + contact->display_name = g_strdup(display_name); + g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_DISPLAY_NAME]); +purple_contact_get_alias(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); +purple_contact_set_alias(PurpleContact *contact, const gchar *alias) { + g_return_if_fail(PURPLE_IS_CONTACT(contact)); + g_free(contact->alias); + contact->alias = g_strdup(alias); + g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_ALIAS]); +purple_contact_get_avatar(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); + return contact->avatar; +purple_contact_set_avatar(PurpleContact *contact, GdkPixbuf *avatar) { + g_return_if_fail(PURPLE_IS_CONTACT(contact)); + if(g_set_object(&contact->avatar, avatar)) { + g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_AVATAR]); +purple_contact_get_presence(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); + return contact->presence; +purple_contact_set_presence(PurpleContact *contact, PurplePresence *presence) { + g_return_if_fail(PURPLE_IS_CONTACT(contact)); + if(g_set_object(&contact->presence, presence)) { + g_object_notify_by_pspec(G_OBJECT(contact), properties[PROP_PRESENCE]); +purple_contact_get_tags(PurpleContact *contact) { + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplecontact.h Fri Sep 23 03:47:08 2022 -0500
@@ -0,0 +1,229 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * This program 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 program 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 program; if not, see <https://www.gnu.org/licenses/>. +#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION) +# error "only <pidgin.h> may be included directly" +#ifndef PURPLE_CONTACT_H +#define PURPLE_CONTACT_H +#include <glib-object.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <libpurple/account.h> +#include <libpurple/purpletags.h> +#define PURPLE_TYPE_CONTACT (purple_contact_get_type()) +G_DECLARE_FINAL_TYPE(PurpleContact, purple_contact, PURPLE, CONTACT, GObject) + * A representation of a user. Contacts are used everywhere you need to refer to + * a user. Be it a chat, an direct message, a file transfer, etc. + * @account: The [class@Purple.Account] this contact is from. + * @username: The username of the contact. + * Creates a new [class@Purple.Contact]. +PurpleContact *purple_contact_new(PurpleAccount *account, const gchar *username); + * purple_contact_get_account: + * @contact: The instance. + * Gets the [class@Purple.Account] that @contact belongs to. + * Returns: (transfer none): The [class@Purple.Account] that @contact belongs +PurpleAccount *purple_contact_get_account(PurpleContact *contact); + * purple_contact_get_id: + * @contact: The instance. + * Gets the id of @contact. + * If a protocol would like to set this, it should call + * [ctor@GObject.Object.new] and pass in the id attribute manually. + * Returns: The id of the contact. +const gchar *purple_contact_get_id(PurpleContact *contact); + * purple_contact_get_username: + * @contact: The instance. + * Gets the username of @contact. + * Returns: The username of @contact. +const gchar *purple_contact_get_username(PurpleContact *contact); + * purple_contact_set_username: + * @contact: The instance. + * @username: The new username. + * Sets the username of @contact to @username. + * This is primarily used by protocol plugins like IRC when a user changes + * their "nick" which is their username. +void purple_contact_set_username(PurpleContact *contact, const gchar *username); + * purple_contact_get_display_name: + * @contact: The instance. + * Gets the display name for @contact. The display name is typically set by the + * contact and is handled by the protocol plugin. + * Returns: (nullable): The display name of @contact if one is set, otherwise + * %NULL will be returned. +const gchar *purple_contact_get_display_name(PurpleContact *contact); + * purple_contact_set_display_name: + * @contact: The instance. + * @display_name: (nullable): The new displayname. + * Sets the display name of @contact to @display_name. + * This should primarily only be used by protocol plugins and everyone else + * should be using [method@Purple.Contact.set_alias]. +void purple_contact_set_display_name(PurpleContact *contact, const gchar *display_name); + * purple_contact_get_alias: + * @contact: The instance. + * Gets the alias for @contact. + * Returns: (nullable): The alias of @contact if one is set, otherwise %NULL. +const gchar *purple_contact_get_alias(PurpleContact *contact); + * purple_contact_set_alias: + * @contact: The instance. + * @alias: (nullable): The new alias. + * Sets the alias of @contact to @alias. + * Protocol plugins may use this value to synchronize across instances. +void purple_contact_set_alias(PurpleContact *contact, const gchar *alias); + * purple_contact_get_avatar: + * @contact: The instance. + * Gets the avatar for @contact if one is set. + * Returns: (transfer none): The avatar if set, otherwise %NULL. +GdkPixbuf *purple_contact_get_avatar(PurpleContact *contact); + * purple_contact_set_avatar: + * @contact: The instance. + * @avatar: (nullable): The new avatar to set. + * Sets the avatar for @contact to @avatar. If @avatar is %NULL an existing + * avatar will be removed. + * Typically this should only called by the protocol plugin. +void purple_contact_set_avatar(PurpleContact *contact, GdkPixbuf *avatar); + * purple_contact_get_presence: + * @contact: The instance. + * Gets the [class@Purple.Presence] for @contact. + * Returns: (nullable): The presence for @contact if one is set, otherwise +PurplePresence *purple_contact_get_presence(PurpleContact *contact); + * purple_contact_set_presence: + * @contact: The instance. + * @presence: (nullable): The new [class@Purple.Presence] to set. + * Sets the presence for @contact to @presence. If @presence is %NULL, the + * existing presence will be cleared. + * Typically this should only be called by the protocol plugin. +void purple_contact_set_presence(PurpleContact *contact, PurplePresence *presence); + * purple_contact_get_tags: + * @contact: The instance. + * Gets the [class@Purple.Tags] instance for @contact. + * Returns: (transfer none): The tags for @contact. +PurpleTags *purple_contact_get_tags(PurpleContact *contact); +#endif /* PURPLE_CONTACT_H */ --- a/libpurple/tests/meson.build Fri Sep 23 03:44:40 2022 -0500
+++ b/libpurple/tests/meson.build Fri Sep 23 03:47:08 2022 -0500
@@ -3,6 +3,7 @@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_contact.c Fri Sep 23 03:47:08 2022 -0500
@@ -0,0 +1,129 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <https://www.gnu.org/licenses/>. +/****************************************************************************** + *****************************************************************************/ +test_purple_contact_new(void) { + PurpleAccount *account = NULL; + PurpleContact *contact = NULL; + account = purple_account_new("test", "test"); + contact = purple_contact_new(account, "username"); + g_assert_true(purple_contact_get_account(contact) == account); + g_assert_cmpstr(purple_contact_get_username(contact), ==, "username"); + g_clear_object(&contact); + g_clear_object(&account); +test_purple_contact_properties(void) { + PurpleAccount *account = NULL; + PurpleAccount *account1 = NULL; + PurpleContact *contact = NULL; + PurplePresence *presence = NULL; + PurplePresence *presence1 = NULL; + PurpleTags *tags = NULL; + GdkPixbuf *avatar = NULL; + GdkPixbuf *avatar1 = NULL; + gchar *username = NULL; + gchar *display_name = NULL; + account = purple_account_new("test", "test"); + avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 1, 1); + presence = g_object_new(PURPLE_TYPE_PRESENCE, NULL); + /* Use g_object_new so we can test setting properties by name. All of them + * call the setter methods, so by doing it this way we exercise more of the + contact = g_object_new( + "username", "username", + "display-name", "display-name", + /* Now use g_object_get to read all of the properties. */ + "display-name", &display_name, + "presence", &presence1, + /* Compare all the things. */ + g_assert_true(account1 == account); + g_assert_cmpstr(username, ==, "username"); + g_assert_cmpstr(display_name, ==, "display-name"); + g_assert_cmpstr(alias, ==, "alias"); + g_assert_true(avatar1 == avatar); + g_assert_true(presence1 == presence); + g_assert_nonnull(tags); + /* Free/unref all the things. */ + g_clear_pointer(&id, g_free); + g_clear_object(&account1); + g_clear_pointer(&username, g_free); + g_clear_pointer(&display_name, g_free); + g_clear_pointer(&alias, g_free); + g_clear_object(&avatar1); + g_clear_object(&presence1); + g_clear_object(&presence); + g_clear_object(&avatar); + g_clear_object(&contact); + g_clear_object(&account); +/****************************************************************************** + *****************************************************************************/ +main(gint argc, gchar *argv[]) { + g_test_init(&argc, &argv, NULL); + g_test_add_func("/contact/new", + test_purple_contact_new); + g_test_add_func("/contact/properties", + test_purple_contact_properties); \ No newline at end of file
--- a/po/POTFILES.in Fri Sep 23 03:44:40 2022 -0500
+++ b/po/POTFILES.in Fri Sep 23 03:47:08 2022 -0500
@@ -239,6 +239,7 @@
libpurple/purplebuddypresence.c
libpurple/purplechatconversation.c
libpurple/purplechatuser.c
+libpurple/purplecontact.c libpurple/purpleconversation.c
libpurple/purpleconversationmanager.c
libpurple/purpleconversationuiops.c
@@ -289,6 +290,7 @@
libpurple/tests/test_account_manager.c
libpurple/tests/test_account_option.c
libpurple/tests/test_circular_buffer.c
+libpurple/tests/test_contact.c libpurple/tests/test_credential_manager.c
libpurple/tests/test_credential_provider.c
libpurple/tests/test_history_adapter.c