2
Copyright 2014 Canonical Ltd.
3
Authors: James Henstridge <james.henstridge@canonical.com>
5
This program is free software: you can redistribute it and/or modify it
6
under the terms of the GNU General Public License version 3, as published
7
by the Free Software Foundation.
9
This program is distributed in the hope that it will be useful, but
10
WITHOUT ANY WARRANTY; without even the implied warranties of
11
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12
PURPOSE. See the GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License along
15
with this program. If not, see <http://www.gnu.org/licenses/>.
20
#include <libaccounts-glib/accounts-glib.h>
21
#include <libsignon-glib/signon-glib.h>
23
#include "account-watcher.h"
25
struct _AccountWatcher {
27
/* A hash table of the service names we are interested in */
28
GHashTable *services_to_watch;
29
/* A hash table of the enabled accounts we know of.
30
* Keys are account ID integers, and AccountInfo structs as values.
34
gulong enabled_event_signal_id;
35
gulong account_deleted_signal_id;
37
AccountEnabledCallback callback;
41
typedef struct _AccountInfo AccountInfo;
43
AccountWatcher *watcher;
44
/* Manage signin session for account */
45
AgAccountService *account_service;
46
SignonAuthSession *session;
47
GVariant *auth_params;
48
GVariant *session_data;
50
gulong enabled_signal_id;
51
AgAccountId account_id;
52
gboolean enabled; /* the last known state of the account */
55
static void account_info_clear_login(AccountInfo *info) {
56
if (info->session_data) {
57
g_variant_unref(info->session_data);
58
info->session_data = NULL;
60
if (info->auth_params) {
61
g_variant_unref(info->auth_params);
62
info->auth_params = NULL;
65
signon_auth_session_cancel(info->session);
66
g_object_unref(info->session);
71
static void account_info_free(AccountInfo *info) {
72
account_info_clear_login(info);
73
if (info->enabled_signal_id != 0) {
74
g_signal_handler_disconnect(
75
info->account_service, info->enabled_signal_id);
77
info->enabled_signal_id = 0;
78
if (info->account_service) {
79
g_object_unref(info->account_service);
80
info->account_service = NULL;
85
static void account_info_notify(AccountInfo *info) {
86
AgService *service = ag_account_service_get_service(info->account_service);
87
const char *service_name = ag_service_get_name(service);
88
char *client_id = NULL;
89
char *client_secret = NULL;
90
char *access_token = NULL;
91
char *token_secret = NULL;
94
/* Look up OAuth 2 parameters, falling back to OAuth 1 names */
95
g_variant_lookup(info->auth_params, "ClientId", "&s", &client_id);
96
g_variant_lookup(info->auth_params, "ClientSecret", "&s", &client_secret);
97
g_variant_lookup(info->session_data, "AccessToken", "&s", &access_token);
98
g_variant_lookup(info->session_data, "TokenSecret", "&s", &token_secret);
100
if (client_id == NULL) {
101
g_variant_lookup(info->auth_params, "ConsumerKey", "&s", &client_id);
103
if (client_secret == NULL) {
104
g_variant_lookup(info->auth_params, "ConsumerSecret", "&s", &client_secret);
108
info->watcher->callback(info->watcher,
116
info->watcher->user_data);
119
static void account_info_login_cb(GObject *source, GAsyncResult *result, void *user_data) {
120
SignonAuthSession *session = (SignonAuthSession *)source;
121
AccountInfo *info = (AccountInfo *)user_data;
123
fprintf(stderr, "Authentication for account %u complete\n", info->account_id);
125
GError *error = NULL;
126
info->session_data = signon_auth_session_process_finish(session, result, &error);
129
fprintf(stderr, "Authentication failed: %s\n", error->message);
133
account_info_notify(info);
136
static void account_info_enabled_cb(
137
AgAccountService *account_service, gboolean enabled, AccountInfo *info) {
138
fprintf(stderr, "account_info_enabled_cb for %u, enabled=%d\n", info->account_id, enabled);
139
if (info->enabled == enabled) {
143
info->enabled = enabled;
145
account_info_clear_login(info);
147
// Send notification that account has been disabled */
148
account_info_notify(info);
152
AgAuthData *auth_data = ag_account_service_get_auth_data(account_service);
153
GError *error = NULL;
154
fprintf(stderr, "Starting authentication session for account %u\n", info->account_id);
155
info->session = signon_auth_session_new(
156
ag_auth_data_get_credentials_id(auth_data),
157
ag_auth_data_get_method(auth_data), &error);
159
fprintf(stderr, "Could not set up auth session: %s\n", error->message);
161
g_object_unref(auth_data);
165
GVariantBuilder builder;
166
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
167
g_variant_builder_add(
169
SIGNON_SESSION_DATA_UI_POLICY,
170
g_variant_new_int32(SIGNON_POLICY_NO_USER_INTERACTION));
172
info->auth_params = g_variant_ref_sink(
173
ag_auth_data_get_login_parameters(
174
auth_data, g_variant_builder_end(&builder)));
176
signon_auth_session_process_async(
179
ag_auth_data_get_mechanism(auth_data),
180
NULL, /* cancellable */
181
account_info_login_cb, info);
182
ag_auth_data_unref(auth_data);
185
static AccountInfo *account_info_new(AccountWatcher *watcher, AgAccountService *account_service) {
186
AccountInfo *info = g_new0(AccountInfo, 1);
187
info->watcher = watcher;
188
info->account_service = g_object_ref(account_service);
190
AgAccount *account = ag_account_service_get_account(account_service);
191
g_object_get(account, "id", &info->account_id, NULL);
193
info->enabled_signal_id = g_signal_connect(
194
account_service, "enabled",
195
G_CALLBACK(account_info_enabled_cb), info);
197
account_info_enabled_cb(account_service, ag_account_service_get_enabled(account_service), info);
202
static void account_watcher_enabled_event_cb(
203
AgManager *manager, AgAccountId account_id, AccountWatcher *watcher) {
204
fprintf(stderr, "enabled-event for %u\n", account_id);
205
if (g_hash_table_contains(watcher->services, GUINT_TO_POINTER(account_id))) {
206
/* We are already tracking this account */
209
AgAccount *account = ag_manager_get_account(manager, account_id);
210
if (account == NULL) {
211
/* There was a problem looking up the account */
214
GList *services = ag_account_list_services(account);
216
for (l = services; l != NULL; l = l->next) {
217
AgService *service = l->data;
219
const char *name = ag_service_get_name(service);
220
if (g_hash_table_contains(watcher->services_to_watch, name)) {
221
AgAccountService *account_service = ag_account_service_new(
223
AccountInfo *info = account_info_new(watcher, account_service);
224
g_object_unref(account_service);
225
g_hash_table_insert(watcher->services, GUINT_TO_POINTER(account_id), info);
229
ag_service_list_free(services);
230
g_object_unref(account);
233
static void account_watcher_account_deleted_cb(
234
AgManager *manager, AgAccountId account_id, AccountWatcher *watcher) {
235
fprintf(stderr, "account-deleted for %u\n", account_id);
236
/* A disabled event should have been sent prior to this, so no
237
* need to send any notification. */
238
g_hash_table_remove(watcher->services, GUINT_TO_POINTER(account_id));
241
static gboolean account_watcher_setup(void *user_data) {
242
AccountWatcher *watcher = (AccountWatcher *)user_data;
244
/* Track changes to accounts */
245
watcher->enabled_event_signal_id = g_signal_connect(
246
watcher->manager, "enabled-event",
247
G_CALLBACK(account_watcher_enabled_event_cb), watcher);
248
watcher->account_deleted_signal_id = g_signal_connect(
249
watcher->manager, "account-deleted",
250
G_CALLBACK(account_watcher_account_deleted_cb), watcher);
252
/* Now check initial state */
253
GList *enabled_accounts = ag_manager_list(watcher->manager);
255
for (l = enabled_accounts; l != NULL; l = l->next) {
256
AgAccountId account_id = GPOINTER_TO_UINT(l->data);
257
account_watcher_enabled_event_cb(watcher->manager, account_id, watcher);
259
ag_manager_list_free(enabled_accounts);
264
AccountWatcher *account_watcher_new(GHashTable *services_to_watch,
265
AccountEnabledCallback callback,
267
AccountWatcher *watcher = g_new0(AccountWatcher, 1);
269
watcher->manager = ag_manager_new();
270
watcher->services_to_watch = g_hash_table_ref(services_to_watch);
271
watcher->services = g_hash_table_new_full(
272
g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)account_info_free);
273
watcher->callback = callback;
274
watcher->user_data = user_data;
276
/* Make sure main setup occurs within the mainloop thread */
277
g_idle_add(account_watcher_setup, watcher);