pidgin/pidgin

Port wincred keyring to PurpleCredentialManager

2021-01-30, Elliott Sales de Andrade
548fed625b8a
Port wincred keyring to PurpleCredentialManager

* Inline PURPLE_PLUGIN_INIT in wincred.
* Add skeleton of wincred PurpleCredentialProvider object.
* Implement password reading.
* Implement wincred password writing.
* Implement wincred password clearing.
* Autoload wincred keyring plugin.
* Cleanup formatting.

Testing Done:
Added XMPP account, saved password, disabled and re-enabled account which kept the password. Restarted Pidgin and password was still saved. Removed XMPP account, and password was deleted from Credential Manager.

I'm not sure that the password needs to be converted from UTF-8 to "Unicode", as I did not test modifying the password in Credential Manager.

Bugs closed: PIDGIN-17489

Reviewed at https://reviews.imfreedom.org/r/466/
/*
* Pidgin - Internet Messenger
* Copyright (C) Pidgin Developers <devel@pidgin.im>
*
* Pidgin 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 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/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_MESON_CONFIG
#include "meson-config.h"
#endif
#include <glib/gi18n-lib.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <json-glib/json-glib.h>
#include <talkatu.h>
#include "pidginabout.h"
#include "package_revision.h"
#include "gtkutils.h"
#include "pidgincore.h"
#include "pidginresources.h"
struct _PidginAboutDialog {
GtkDialog parent;
GtkWidget *close_button;
GtkWidget *application_name;
GtkWidget *stack;
GtkWidget *main_scrolled_window;
GtkTextBuffer *main_buffer;
GtkWidget *developers_page;
GtkWidget *developers_treeview;
GtkTreeStore *developers_store;
GtkWidget *translators_page;
GtkWidget *translators_treeview;
GtkTreeStore *translators_store;
GtkWidget *build_info_page;
GtkWidget *build_info_treeview;
GtkTreeStore *build_info_store;
};
/******************************************************************************
* Helpers
*****************************************************************************/
static void
pidgin_about_dialog_load_application_name(PidginAboutDialog *about) {
gchar *label = g_strdup_printf("%s %s", PIDGIN_NAME, VERSION);
gtk_label_set_text(GTK_LABEL(about->application_name), label);
g_free(label);
}
static void
pidgin_about_dialog_load_main_page(PidginAboutDialog *about) {
GtkTextIter start;
GInputStream *istream = NULL;
GString *str = NULL;
TalkatuMarkdownBuffer *md_buffer = NULL;
gchar buffer[8192];
gssize read = 0, size = 0;
/* now load the html */
istream = g_resource_open_stream(pidgin_get_resource(),
"/im/pidgin/Pidgin/About/about.md",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
str = g_string_new("");
while((read = g_input_stream_read(istream, buffer, sizeof(buffer), NULL, NULL)) > 0) {
g_string_append_len(str, (gchar *)buffer, read);
size += read;
}
gtk_text_buffer_get_start_iter(about->main_buffer, &start);
md_buffer = TALKATU_MARKDOWN_BUFFER(about->main_buffer);
talkatu_markdown_buffer_insert_markdown(md_buffer, &start, str->str, size);
g_string_free(str, TRUE);
g_input_stream_close(istream, NULL, NULL);
}
static void
pidgin_about_dialog_load_json(GtkTreeStore *store, const gchar *json_section) {
GInputStream *istream = NULL;
GList *l = NULL, *sections = NULL;
GError *error = NULL;
JsonParser *parser = NULL;
JsonNode *root_node = NULL;
JsonObject *root_object = NULL;
JsonArray *sections_array = NULL;
/* get a stream to the credits resource */
istream = g_resource_open_stream(pidgin_get_resource(),
"/im/pidgin/Pidgin/About/credits.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);
}
root_node = json_parser_get_root(parser);
root_object = json_node_get_object(root_node);
sections_array = json_object_get_array_member(root_object, json_section);
sections = json_array_get_elements(sections_array);
for(l = sections; l; l = l->next) {
GtkTreeIter section_iter;
JsonObject *section = json_node_get_object(l->data);
JsonArray *people = NULL;
gchar *markup = NULL;
const gchar *title = NULL;
guint idx = 0, n_people = 0;
title = json_object_get_string_member(section, "title");
markup = g_strdup_printf("<b><big>%s</big></b>", title);
gtk_tree_store_append(store, &section_iter, NULL);
gtk_tree_store_set(store, &section_iter,
0, markup,
1, 0.5f,
-1);
g_free(markup);
people = json_object_get_array_member(section, "people");
n_people = json_array_get_length(people);
for(idx = 0; idx < n_people; idx++) {
GtkTreeIter person_iter;
gtk_tree_store_append(store, &person_iter, &section_iter);
gtk_tree_store_set(store, &person_iter,
0, json_array_get_string_element(people, idx),
1, 0.5f,
-1);
}
}
g_list_free(sections);
/* clean up */
g_object_unref(G_OBJECT(parser));
g_input_stream_close(istream, NULL, NULL);
}
static void
pidgin_about_dialog_load_developers(PidginAboutDialog *about) {
pidgin_about_dialog_load_json(about->developers_store, "developers");
}
static void
pidgin_about_dialog_load_translators(PidginAboutDialog *about) {
pidgin_about_dialog_load_json(about->translators_store, "languages");
}
static void
pidgin_about_dialog_add_build_args(PidginAboutDialog *about, const gchar *title,
const gchar *build_args)
{
GtkTreeIter section, value;
gchar **splits = NULL;
gchar *markup = NULL;
gint idx = 0;
markup = g_strdup_printf("<b>%s</b>", title);
gtk_tree_store_append(about->build_info_store, &section, NULL);
gtk_tree_store_set(about->build_info_store, &section,
0, markup,
-1);
g_free(markup);
/* now walk through the arguments and add them */
splits = g_strsplit(build_args, " ", -1);
for(idx = 0; splits[idx]; idx++) {
gchar **value_split = g_strsplit(splits[idx], "=", 2);
if(value_split[0] == NULL || value_split[0][0] == '\0') {
continue;
}
gtk_tree_store_append(about->build_info_store, &value, &section);
gtk_tree_store_set(about->build_info_store, &value, 0, value_split[0],
1, value_split[1] ? value_split[1] : "", -1);
g_strfreev(value_split);
}
g_strfreev(splits);
}
static void
pidgin_about_dialog_build_info_add_version(GtkTreeStore *store,
GtkTreeIter *section,
const gchar *title,
guint major,
guint minor,
guint micro)
{
GtkTreeIter item;
gchar *version = g_strdup_printf("%u.%u.%u", major, minor, micro);
gtk_tree_store_append(store, &item, section);
gtk_tree_store_set(store, &item,
0, title,
1, version,
-1);
g_free(version);
}
static void
pidgin_about_dialog_load_build_info(PidginAboutDialog *about) {
GtkTreeIter section, item;
gchar *markup = NULL;
/* create the section */
markup = g_strdup_printf("<b>%s</b>", _("Build Information"));
gtk_tree_store_append(about->build_info_store, &section, NULL);
gtk_tree_store_set(about->build_info_store, &section,
0, markup,
-1);
g_free(markup);
/* add the commit hash */
gtk_tree_store_append(about->build_info_store, &item, &section);
gtk_tree_store_set(about->build_info_store, &item,
0, "Commit Hash",
1, REVISION,
-1);
/* add the purple version */
pidgin_about_dialog_build_info_add_version(about->build_info_store,
&section, _("Purple Version"),
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_MICRO_VERSION);
/* add the glib version */
pidgin_about_dialog_build_info_add_version(about->build_info_store,
&section, _("GLib Version"),
GLIB_MAJOR_VERSION,
GLIB_MINOR_VERSION,
GLIB_MICRO_VERSION);
/* add the gtk version */
pidgin_about_dialog_build_info_add_version(about->build_info_store,
&section, _("GTK Version"),
GTK_MAJOR_VERSION,
GTK_MINOR_VERSION,
GTK_MICRO_VERSION);
}
static void
pidgin_about_dialog_load_runtime_info(PidginAboutDialog *about) {
GtkTreeIter section;
gchar *markup = NULL;
/* create the section */
markup = g_strdup_printf("<b>%s</b>", _("Runtime Information"));
gtk_tree_store_append(about->build_info_store, &section, NULL);
gtk_tree_store_set(about->build_info_store, &section,
0, markup,
-1);
g_free(markup);
/* add the purple version */
pidgin_about_dialog_build_info_add_version(about->build_info_store,
&section, _("Purple Version"),
purple_major_version,
purple_minor_version,
purple_micro_version);
/* add the glib version */
pidgin_about_dialog_build_info_add_version(about->build_info_store,
&section, _("GLib Version"),
glib_major_version,
glib_minor_version,
glib_micro_version);
/* add the gtk version */
pidgin_about_dialog_build_info_add_version(about->build_info_store,
&section, _("GTK Version"),
gtk_major_version,
gtk_minor_version,
gtk_micro_version);
}
static void
pidgin_about_dialog_load_gtk_settings(PidginAboutDialog *about) {
GtkTreeIter section, iter;
gchar *markup = NULL;
gchar *cursor_theme_name = NULL, *theme_name = NULL;
gchar *icon_theme_name = NULL, *fallback_icon_theme = NULL;
gchar *im_module = NULL;
gchar *key_theme_name = NULL, *sound_theme_name = NULL;
gboolean enable_animations = FALSE;
gboolean shell_shows_app_menu = FALSE, shell_shows_menubar = FALSE;
/* create the section */
markup = g_strdup_printf("<b>%s</b>", _("GTK Settings"));
gtk_tree_store_append(about->build_info_store, &section, NULL);
gtk_tree_store_set(about->build_info_store, &section,
0, markup,
-1);
g_free(markup);
/* get the settings we're interested in */
g_object_get(
gtk_settings_get_default(),
"gtk-cursor-theme-name", &cursor_theme_name,
"gtk-enable-animations", &enable_animations,
"gtk-fallback-icon-theme", &fallback_icon_theme,
"gtk-icon-theme-name", &icon_theme_name,
"gtk-im-module", &im_module,
"gtk-key-theme-name", &key_theme_name,
"gtk-shell-shows-app-menu", &shell_shows_app_menu,
"gtk-shell-shows-menubar", &shell_shows_menubar,
"gtk-sound-theme-name", &sound_theme_name,
"gtk-theme-name", &theme_name,
NULL);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-cursor-theme-name",
1, (cursor_theme_name != NULL) ? cursor_theme_name : _("(not set)"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-enable-animations",
1, enable_animations ? _("yes") : _("no"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-fallback-icon-theme",
1, (fallback_icon_theme != NULL) ? fallback_icon_theme : _("(not set)"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-icon-theme-name",
1, (icon_theme_name != NULL) ? icon_theme_name : _("(not set)"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-im-module",
1, (im_module != NULL) ? im_module : _("(not set)"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-key-theme-name",
1, (key_theme_name != NULL) ? key_theme_name : _("(not set)"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-shell-shows-app-menu",
1, shell_shows_app_menu ? _("yes") : _("no"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-shell-shows-menubar",
1, shell_shows_menubar ? _("yes") : _("no"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-sound-theme-name",
1, (sound_theme_name != NULL) ? sound_theme_name : _("(not set)"),
-1);
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, "gtk-theme-name",
1, (theme_name != NULL) ? theme_name : _("(not set)"),
-1);
g_free(cursor_theme_name);
g_free(fallback_icon_theme);
g_free(icon_theme_name);
g_free(im_module);
g_free(key_theme_name);
g_free(sound_theme_name);
g_free(theme_name);
}
static void
pidgin_about_dialog_load_plugin_search_paths(PidginAboutDialog *about) {
GtkTreeIter section;
GList *path = NULL;
gchar *markup = NULL;
/* create the section */
markup = g_strdup_printf("<b>%s</b>", _("Plugin Search Paths"));
gtk_tree_store_append(about->build_info_store, &section, NULL);
gtk_tree_store_set(about->build_info_store, &section,
0, markup,
-1);
g_free(markup);
/* add the search paths */
for(path = gplugin_manager_get_paths(); path != NULL; path = path->next) {
GtkTreeIter iter;
gtk_tree_store_append(about->build_info_store, &iter, &section);
gtk_tree_store_set(about->build_info_store, &iter,
0, (gchar*)path->data,
-1);
}
}
static void
pidgin_about_dialog_load_build_configuration(PidginAboutDialog *about) {
#ifdef MESON_ARGS
pidgin_about_dialog_add_build_args(about, _("Meson Arguments"), MESON_ARGS);
#endif /* MESON_ARGS */
pidgin_about_dialog_load_build_info(about);
pidgin_about_dialog_load_runtime_info(about);
pidgin_about_dialog_load_gtk_settings(about);
pidgin_about_dialog_load_plugin_search_paths(about);
}
/******************************************************************************
* Callbacks
*****************************************************************************/
static void
pidgin_about_dialog_close(GtkWidget *b, gpointer data) {
gtk_widget_destroy(GTK_WIDGET(data));
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
G_DEFINE_TYPE(PidginAboutDialog, pidgin_about_dialog, GTK_TYPE_DIALOG);
static void
pidgin_about_dialog_class_init(PidginAboutDialogClass *klass) {
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
gtk_widget_class_set_template_from_resource(
widget_class,
"/im/pidgin/Pidgin/About/about.ui"
);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, close_button);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, application_name);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, stack);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, main_scrolled_window);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, main_buffer);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, developers_page);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, developers_store);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, developers_treeview);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, translators_page);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, translators_store);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, translators_treeview);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, build_info_page);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, build_info_store);
gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, build_info_treeview);
}
static void
pidgin_about_dialog_init(PidginAboutDialog *about) {
gtk_widget_init_template(GTK_WIDGET(about));
/* wire up the close button */
g_signal_connect(about->close_button, "clicked",
G_CALLBACK(pidgin_about_dialog_close), about);
/* setup the application name label */
pidgin_about_dialog_load_application_name(about);
/* setup the main page */
pidgin_about_dialog_load_main_page(about);
/* setup the developers stuff */
pidgin_about_dialog_load_developers(about);
gtk_tree_view_expand_all(GTK_TREE_VIEW(about->developers_treeview));
/* setup the translators stuff */
pidgin_about_dialog_load_translators(about);
gtk_tree_view_expand_all(GTK_TREE_VIEW(about->translators_treeview));
/* setup the build info page */
pidgin_about_dialog_load_build_configuration(about);
gtk_tree_view_expand_all(GTK_TREE_VIEW(about->build_info_treeview));
}
/******************************************************************************
* Public API
*****************************************************************************/
GtkWidget *
pidgin_about_dialog_new(void) {
return GTK_WIDGET(g_object_new(
PIDGIN_TYPE_ABOUT_DIALOG,
"title", "About Pidgin",
NULL
));
}