On 07/17/2012 11:30 AM, Jakub Filak wrote:
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
--- a/src/gtk-helpers/internal_libreport_gtk.h
+++ b/src/gtk-helpers/internal_libreport_gtk.h
@@ -27,19 +27,22 @@
extern "C" {
#endif
-extern bool g_keyring_available;
-
#define make_label_autowrap_on_resize libreport_make_label_autowrap_on_resize
void make_label_autowrap_on_resize(GtkLabel *label);
#define show_events_list_dialog libreport_show_events_list_dialog
void show_events_list_dialog(GtkWindow *parent);
-#define load_event_config_data_from_keyring
libreport_load_event_config_data_from_keyring
-void load_event_config_data_from_keyring(void);
+#define is_event_config_user_storage_available
libreport_is_event_config_user_storage_available
+bool is_event_config_user_storage_available();
+
+#define load_event_config_data_from_user_storage
libreport_load_event_config_data_from_user_storage
+void load_event_config_data_from_user_storage(GHashTable *event_config_list);
-#define find_keyring_item_id_for_event libreport_find_keyring_item_id_for_event
-guint32 find_keyring_item_id_for_event(const char *event_name);
+#define save_event_config_data_to_user_storage
libreport_save_event_config_data_to_user_storage
+void save_event_config_data_to_user_storage(const char *event_name,
+ const event_config_t *event_config,
+ bool store_password);
#define show_event_config_dialog libreport_show_event_config_dialog
int show_event_config_dialog(const char *event_name, GtkWindow *parent);
diff --git a/src/gtk-helpers/secrets.c b/src/gtk-helpers/secrets.c
new file mode 100644
index 0000000..89eb76c
--- /dev/null
+++ b/src/gtk-helpers/secrets.c
@@ -0,0 +1,1304 @@
+/*
+ Copyright (C) 2012 ABRT Team
+ Copyright (C) 2012 RedHat inc.
+
+ 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "internal_libreport.h"
+#include "internal_libreport_gtk.h"
+
+#include <gio/gio.h>
+
+#define SECRETS_SERVICE_BUS "org.freedesktop.secrets"
+#define SECRETS_NAME_SPACE(interface) "org.freedesktop.Secret."interface
+
+/* label and alias have to be same because of ksecrets */
+#define SECRETS_COLLECTION_LABEL SECRETS_COLLECTION_ALIAS
+#define SECRETS_COLLECTION_ALIAS "default"
+
+#define SECRETS_SEARCH_ATTRIBUTE "libreportEventConfig"
+
+/* 5s timeout*/
+#define SECRETS_CALL_DEFAULT_TIMEOUT 5000
+
+/* Well known errors for which we have workarounds */
+#define NOT_IMPLEMENTED_READ_ALIAS_ERROR_MESSAGE
"GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method
'ReadAlias' in interface 'org.freedesktop.Secret.Service' at object path
'/org/freedesktop/secrets' (signature 's')"
+#define INVALID_PROPERTIES_ARGUMENTS
"GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Invalid properties
argument"
+
+/*
+ Brief introduction of Secrets Service API
+ -----------------------------------------
+
+ "The Secret Service API allows client applications to store secrets securely in
+ a service running in the user's login session.
+
+ The secrets are usually stored in an encrypted manner by the service. The
+ service may need to be unlocked by the user before the secrets become
+ available for retrieval by client applications.
+
+ The Secret Service stores a secret along with a set of lookup attributes. The
+ attributes can be used to lookup and retrieve a secret at a later date. The
+ lookup attributes are not treated as secret material, and the service may
+ choose to not encrypt attributes when storing them to disk.
+
+ This API was desigened by GNOME and KDE developers with the goal of having a
+ common way to store secrets. Its predecessors are the desktop specific APIs
+ used by GNOME Keyring and KWallet."
+ [
http://standards.freedesktop.org/secret-service/ch01.html]
+
+ User's secrets are stored in Item objects that are aggregated by Collection
+ objects. An item or a collection can be Locked. If an object is locked it is
+ impossible to read some of it's properties. Before we start to read guarded
+ properties we have to unlock locked objects. Once unlocked object can be
+ locked by underlying service at anytime. Hence it is necessary to unlock an
+ object before every read of a guarded property. Unlocking of an object will
+ probably result to a prompt. Prompt objects are something like asynchronous
+ request. The object is not unlocked until a prompt is not successfully
+ performed. To perform a prompt we have to call the Prompt method of a prompt
+ object. A prompt is performed asynchronously thus the Prompt method returns
+ immediately. A prompt object announces the result of a prompt performance by
+ the Completed signal.
+
+ Communication between user's application and Secrets Service API
+ implementation is provided by Service object. On the start of communication a
+ session must be opened. A session sets a attributes of communication. For
+ example, it allows us to encrypt a communication between an app and the Secret
+ Service. We are using unencrypted communication.
+
+ The libreport items are stored in the default collection. The default
+ collection Alias property holds 'default'. The ReadAlias method helps us to
+ get the default collection. The default collection can be missing and any
+ application is allowed to create a new one.
+
+ The libreport stores the configuration options in item's search attributes. A
+ single item is created for every event configuration structure. Item's Label
+ property holds a name of an event and the Label property is used for searching
+ for a stored event configuration. Item Attributes are stored as
+ string-2-string dictionary.
+
+ Any single dismissed propmt will make the secrets service unavailable, hence a
+ user will not be asked again.
Data structure:
===============
Secret Service groups data into Collections. Each collection
has a label and may have alternative names (aliases).
There is a default collection, named "default".
Libreport uses it to store our data.
Collection is a set of Items identified by labels (label is a string).
Libreport uses event names as labels for items which hold event config data.
Item is a set of (name,value) pairs called attributes.
Libreport uses attributes to store config data.
For example, "report_Bugzilla" item will have some attributes,
like ("Bugzilla_BugzillaURL", "https://bugzilla.redhat.com").
DBus API:
=========
Secret Service requires user to first open a Session using OpenSession()
call over session dbus on "/org/freedesktop/secrets" object,
"org.freedesktop.Secret.Service" interface.
This creates a new object on "org.freedesktop.secrets" bus.
Apparently, this object is not used for anything except final Close() call
(those guys are Einsteins).
We obtain a dbus object name for the default collection by calling
ReadAlias() over session dbus on "/org/freedesktop/secrets" object,
"org.freedesktop.Secret.Service" interface. This object is accessible
on session dbus. It has "org.freedesktop.Secret.Collection" interface.
Before we can search for items on the collection, we need to unlock collection
(this usually causes a "gimme pwd NOW!" prompt to appear if we are in X).
This is done by Unlock() call. It returns a path to prompt dbus object
if unlocking is truly needed. In which case we need to call Prompt() on it,
and wait for dbus signal "Completed" from the prompt object.
We can SearchItems() and CreateItem() on the default collection.
SearchItems() returns the list of dbus object names of found items.
They are accessible on session dbus and have "org.freedesktop.Secret.Item"
interface. (We use the first found item, if there are more than one.)
We retrieve attributes from a found item (they are stored as dbus
object's properties).
[ CreateItem() short description ]
+
+ +-------------------+ +--------------+ +-----------+
+ |Service |<>---|Collection |<>---|Item |
+ +-------------------+ +--------------+ +-----------+
+ |ReadAlias() | |Label | |Label |
+ |OpenSession() | |CreateItem() | |Attributes |
+ |Unlock() | |SearchItems() | +-----------+
+ |CreateCollection() | +--------------+
+ +-------------------+
+ +-------------+
+ +-------------+ |Prompt |
+ |Session | +-------------+
+ +-------------+ |!Completed |
+ |Close() | |Prompt() |
+ +-------------+ +-------------+
+
+ Reload configuration:
+ Get the default collection
+ If the default collection was not found finish
+ Unlock the default collection
+ Perform a prompt if is required
+ Find an item for a requested event
+ -- Item's attributes are always readable thus we do not have to unlock the
item.
+ Reload configuration from item's attributes
+
+ Save configuration:
+ Get the default collection
+ If collection was found unlock the default collection
+ Perform a prompt if is required
+ Else create a collection and use it as the default collection
+ Perform a prompt if is required
+ Find an item for a requested event
+ If the item was not found create a new one with the config attributes
+ -- Item's attributes are always readable thus we do not have to unlock the
item.
+ Else update item's attributes
+ */
+
+enum secrets_service_state
+{
+ /* intial state */
+ SBS_INITIAL = 0,
+ /* after opening a secrets service session (different than D-Bus session) */
+ SBS_CONNECTED,
+ /* secrets service is not available do not bother user anymore */
+ SBS_UNAVAILABLE,
+};
+
+struct secrets_object
+{
+ GDBusProxy *proxy;
+ /*
http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfac... */
+ GDBusProxy *property_proxy;
+ const gchar *interface_name;
+};
+
+/* Glib DBus object org.freedesktop.secrets */
+static GDBusConnection *g_connection;
Looks like it's just a session dbus connection, has nothing to do
with org.freedesktop.secrets. Wrong comment?
+/* State of the service */
+static enum secrets_service_state g_state;
+
+/*
http://standards.freedesktop.org/secret-service/re01.html */
+/* xorg.freedesktop.Secret.Service interface object */
+static struct secrets_object *g_service;
Maybe
/* dbus object:/org/freedesktop/secrets, iface:xorg.freedesktop.Secret.Service */
static struct secrets_object *g_?????_service;
+/*
http://standards.freedesktop.org/secret-service/ch06.html */
+/* xorg.freedesktop.Secret.Session interface object */
+static GDBusProxy *g_session;
/* proxy for dbus object gotten by OpenSession(), iface:xorg.freedesktop.Secret.Session
*/
static GDBusProxy *g_?????_session;
+static GVariant *dbus_call_sync(GDBusProxy *proxy, const gchar
*method, GVariant *args)
+{
+ GError *error = NULL;
+ GVariant *const resp = g_dbus_proxy_call_sync(proxy,
+ method,
+ args,
+ G_DBUS_PROXY_FLAGS_NONE,
+ SECRETS_CALL_DEFAULT_TIMEOUT,
+ NULL,
/*what is it:*/ NULL,
+ &error);
+static bool secrets_object_change_path(struct secrets_object *obj,
+ const gchar *path)
+{
+ /* Do not set a new path if is the same as the current path */
+ if (!strcmp(path, g_dbus_proxy_get_object_path(obj->proxy)))
I usually use strcmp() == 0. It helps me to read the code correctly:
"if these strings are '==', ..."
+ return true;
+
+
+static void secrets_object_delete(struct secrets_object *obj)
+{
+ if (!obj)
+ return;
+
+ if (obj->proxy)
+ {
+ g_object_unref(obj->proxy);
+ obj->proxy = NULL;
+ }
+
+ if (obj->property_proxy)
+ {
+ g_object_unref(obj->property_proxy);
+ obj->property_proxy = NULL;
+ }
+
+ obj->interface_name = NULL;
+
+ free(obj);
+}
^^^^^^^^^^^
assignments of NULLs here are superfluous.
+/*******************************************************************************/
+/* struct secrets_service */
+/*******************************************************************************/
+
+static void secrets_service_set_unavailable(void);
+
+static void secrets_service_error(GError *error, const char *comment)
+{
+ if (comment)
+ {
+ log("%s: %s", comment, error->message);
+ }
+ else
+ {
+ log("%s", error->message);
+ }
+
+ g_error_free(error);
+}
Single caller. Maybe absorb it there...