pidgin/pidgin

Fix memory leak when creating an IRCv3 account
default tip
19 hours ago, Markus Fischer
96d1ed6df91c
Fix memory leak when creating an IRCv3 account

Testing Done:
Created a new IRCv3 account while running in valgrind.

Reviewed at https://reviews.imfreedom.org/r/3262/
/*
* 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/>.
*/
#include <json-glib/json-glib.h>
#include "purpledemocontacts.h"
#include "purpledemoresource.h"
/******************************************************************************
* Helpers
*****************************************************************************/
static void
purple_demo_contacts_load_contact_icon(PurpleContactInfo *info,
const char *name)
{
PurpleAvatar *avatar = NULL;
GError *error = NULL;
char *path = NULL;
path = g_strdup_printf("/im/pidgin/purple/demo/buddy_icons/%s.png", name);
avatar = purple_avatar_new_from_resource(path, &error);
if(error != NULL) {
g_message("Failed to load find an icon for %s: %s", path,
error->message);
g_free(path);
g_clear_error(&error);
return;
}
g_free(path);
if(PURPLE_IS_AVATAR(avatar)) {
purple_contact_info_set_avatar(info, avatar);
g_clear_object(&avatar);
}
}
static void
purple_demo_contacts_load_contact_person(JsonObject *person_object,
PurpleContactInfo *info)
{
PurplePerson *person = NULL;
gboolean new_person = FALSE;
const char *value = NULL;
/* If the person has an id, grab it so we can use it when constructing the
* person object.
*/
if(json_object_has_member(person_object, "id")) {
value = json_object_get_string_member(person_object, "id");
}
/* See if the contact has an existing person. */
person = purple_contact_info_get_person(info);
if(PURPLE_IS_PERSON(person)) {
const char *existing_id = NULL;
/* If the existing person's id doesn't match the new one, NULL out
* person so it'll be recreated with the new id.
*/
existing_id = purple_person_get_id(person);
if(!purple_strequal(existing_id, value)) {
person = NULL;
}
}
/* If the person didn't exist or it had a different id, create a new person
* with the id.
*/
if(!PURPLE_IS_PERSON(person)) {
person = g_object_new(PURPLE_TYPE_PERSON, "id", value, NULL);
new_person = TRUE;
}
/* Alias */
if(json_object_has_member(person_object, "alias")) {
value = json_object_get_string_member(person_object, "alias");
if(!purple_strempty(value)) {
purple_person_set_alias(person, value);
}
}
/* Create the link between the person and the contact info. */
if(new_person) {
purple_person_add_contact_info(person, info);
purple_contact_info_set_person(info, person);
g_clear_object(&person);
}
}
static void
purple_demo_contacts_load_contact_presence(JsonObject *presence_object,
PurpleContactInfo *info)
{
PurplePresence *presence = NULL;
const gchar *value = NULL;
presence = purple_contact_info_get_presence(info);
/* Emoji */
if(json_object_has_member(presence_object, "emoji")) {
value = json_object_get_string_member(presence_object, "emoji");
if(!purple_strempty(value)) {
purple_presence_set_emoji(presence, value);
}
}
/* Idle Time */
if(json_object_has_member(presence_object, "idle")) {
GDateTime *now = NULL;
GDateTime *idle_since = NULL;
gint64 ivalue = 0;
ivalue = json_object_get_int_member(presence_object, "idle");
now = g_date_time_new_now_local();
idle_since = g_date_time_add_minutes(now, -1 * ivalue);
purple_presence_set_idle_time(presence, idle_since);
g_date_time_unref(idle_since);
g_date_time_unref(now);
}
/* Message */
if(json_object_has_member(presence_object, "message")) {
value = json_object_get_string_member(presence_object, "message");
if(!purple_strempty(value)) {
purple_presence_set_message(presence, value);
}
}
/* Mobile */
if(json_object_has_member(presence_object, "mobile")) {
gboolean bvalue = FALSE;
bvalue = json_object_get_boolean_member(presence_object, "mobile");
purple_presence_set_mobile(presence, bvalue);
}
/* Primitive */
if(json_object_has_member(presence_object, "primitive")) {
PurplePresencePrimitive primitive = PURPLE_PRESENCE_PRIMITIVE_OFFLINE;
value = json_object_get_string_member(presence_object, "primitive");
if(!purple_strempty(value)) {
GEnumClass *enum_class = NULL;
GEnumValue *enum_value = NULL;
enum_class = g_type_class_ref(PURPLE_TYPE_PRESENCE_PRIMITIVE);
enum_value = g_enum_get_value_by_nick(enum_class, value);
if(enum_value != NULL) {
primitive = enum_value->value;
}
g_type_class_unref(enum_class);
}
purple_presence_set_primitive(presence, primitive);
}
}
static void
purple_demo_contacts_load_contact(PurpleContactManager *manager,
PurpleAccount *account,
JsonObject *contact_object)
{
PurpleContact *contact = NULL;
PurpleContactInfo *info = NULL;
gboolean new_contact = FALSE;
const char *id = NULL;
const char *value = NULL;
/* If we have an id, grab so we can create the contact with it. */
if(json_object_has_member(contact_object, "id")) {
id = json_object_get_string_member(contact_object, "id");
}
/* Look for an existing contact before creating a new one. This stops us
* from getting multiples when we trigger connection errors.
*/
if(!purple_strempty(id)) {
contact = purple_contact_manager_find_with_id(manager, account, id);
}
/* If we didn't find an existing contact, create it now with the provided
* id.
*/
if(!PURPLE_IS_CONTACT(contact)) {
contact = purple_contact_new(account, id);
new_contact = TRUE;
}
info = PURPLE_CONTACT_INFO(contact);
/* Alias */
if(json_object_has_member(contact_object, "alias")) {
value = json_object_get_string_member(contact_object, "alias");
if(!purple_strempty(value)) {
purple_contact_info_set_alias(info, value);
}
}
/* Color */
if(json_object_has_member(contact_object, "color")) {
value = json_object_get_string_member(contact_object, "color");
if(!purple_strempty(value)) {
purple_contact_info_set_color(info, value);
}
}
/* Display Name */
if(json_object_has_member(contact_object, "display_name")) {
value = json_object_get_string_member(contact_object, "display_name");
if(!purple_strempty(value)) {
purple_contact_info_set_display_name(info, value);
}
}
/* Username */
if(json_object_has_member(contact_object, "username")) {
value = json_object_get_string_member(contact_object, "username");
if(!purple_strempty(value)) {
purple_contact_info_set_username(info, value);
purple_demo_contacts_load_contact_icon(info, value);
}
}
/* Load the profile if it exists. */
if(json_object_has_member(contact_object, "profile")) {
value = json_object_get_string_member(contact_object, "profile");
if(!purple_strempty(value)) {
g_object_set_data_full(G_OBJECT(info), "demo-profile",
g_strdup(value), g_free);
}
}
/* Load the tags. */
if(json_object_has_member(contact_object, "tags")) {
PurpleTags *tags = purple_contact_info_get_tags(info);
JsonArray *array = NULL;
GList *elements = NULL;
array = json_object_get_array_member(contact_object, "tags");
elements = json_array_get_elements(array);
while(elements != NULL) {
JsonNode *tag_node = elements->data;
const char *tag = json_node_get_string(tag_node);
purple_tags_add(tags, tag);
elements = g_list_delete_link(elements, elements);
}
}
/* Load the person. */
if(json_object_has_member(contact_object, "person")) {
JsonObject *person_object = NULL;
person_object = json_object_get_object_member(contact_object,
"person");
purple_demo_contacts_load_contact_person(person_object, info);
}
/* Load the presence. */
if(json_object_has_member(contact_object, "presence")) {
JsonObject *presence_object = NULL;
presence_object = json_object_get_object_member(contact_object,
"presence");
purple_demo_contacts_load_contact_presence(presence_object, info);
}
/* Finally add the contact to the contact manager if it's new. */
if(new_contact) {
purple_contact_manager_add(manager, contact);
}
g_clear_object(&contact);
}
/******************************************************************************
* Local Exports
*****************************************************************************/
void
purple_demo_contacts_load(PurpleAccount *account) {
PurpleContactManager *manager = NULL;
GError *error = NULL;
GInputStream *istream = NULL;
GList *contacts = NULL;
JsonArray *contacts_array = NULL;
JsonNode *root_node = NULL;
JsonParser *parser = NULL;
/* get a stream to the contacts.json resource */
istream = g_resource_open_stream(purple_demo_get_resource(),
"/im/pidgin/purple/demo/contacts.json",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
/* create our parser */
parser = json_parser_new();
if(!json_parser_load_from_stream(parser, istream, NULL, &error)) {
g_critical("%s", error->message);
g_clear_error(&error);
return;
}
root_node = json_parser_get_root(parser);
manager = purple_contact_manager_get_default();
/* Load the contacts! */
contacts_array = json_node_get_array(root_node);
contacts = json_array_get_elements(contacts_array);
while(contacts != NULL) {
JsonNode *contact_node = NULL;
JsonObject *contact_object = NULL;
contact_node = contacts->data;
contact_object = json_node_get_object(contact_node);
purple_demo_contacts_load_contact(manager, account, contact_object);
contacts = g_list_delete_link(contacts, contacts);
}
/* Clean up everything else... */
g_clear_object(&parser);
g_input_stream_close(istream, NULL, NULL);
g_object_unref(istream);
}