diff options
author | Ray Strode <rstrode@redhat.com> | 2012-06-21 22:46:43 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2012-06-21 22:47:15 -0400 |
commit | 0062544c0998e9f6544a79cf2ad0299ade785e3b (patch) | |
tree | 39a8866847dc50e9e6804b918115b4691873b196 | |
parent | d9785198a8366380d7393bd4fce188319f517518 (diff) | |
download | gnome-control-center-wip/identity.tar.gz |
update for latest changeswip/identity
I'm planning on scrapping a lot of the account dialog changes soon
though.
i'm just doing an unorganized push since code hoarding considered
harmful.
28 files changed, 5272 insertions, 635 deletions
diff --git a/panels/user-accounts/Makefile.am b/panels/user-accounts/Makefile.am index f470aae74..8a333fb6d 100644 --- a/panels/user-accounts/Makefile.am +++ b/panels/user-accounts/Makefile.am @@ -30,12 +30,12 @@ BUILT_SOURCES = \ libuser_accounts_la_SOURCES = \ um-account-type.h \ um-account-type.c \ - um-alarm.h \ - um-alarm.c \ - um-identity-manager.h \ - um-identity-manager.c \ - um-identity.h \ - um-identity.c \ + gsd-alarm.h \ + gsd-alarm.c \ + gsd-identity-manager.h \ + gsd-identity-manager.c \ + gsd-identity.h \ + gsd-identity.c \ $(kerberos_sources) \ um-user.h \ um-user.c \ @@ -71,10 +71,10 @@ libuser_accounts_la_SOURCES = \ if BUILD_KERBEROS libuser_accounts_la_SOURCES += \ - um-kerberos-identity-manager.h \ - um-kerberos-identity-manager.c \ - um-kerberos-identity.h \ - um-kerberos-identity.c + gsd-kerberos-identity-manager.h \ + gsd-kerberos-identity-manager.c \ + gsd-kerberos-identity.h \ + gsd-kerberos-identity.c endif diff --git a/panels/user-accounts/um-alarm.c b/panels/user-accounts/gsd-alarm.c index 29d82e50c..9214bfd0e 100644 --- a/panels/user-accounts/um-alarm.c +++ b/panels/user-accounts/gsd-alarm.c @@ -23,7 +23,7 @@ #include "config.h" -#include "um-alarm.h" +#include "gsd-alarm.h" #ifdef HAVE_TIMERFD #include <sys/timerfd.h> @@ -48,20 +48,21 @@ typedef struct { #define MAX_TIMEOUT_INTERVAL (10 * 1000) typedef enum { - UM_ALARM_TYPE_UNSCHEDULED, - UM_ALARM_TYPE_TIMER, - UM_ALARM_TYPE_TIMEOUT, -} UmAlarmType; + GSD_ALARM_TYPE_UNSCHEDULED, + GSD_ALARM_TYPE_TIMER, + GSD_ALARM_TYPE_TIMEOUT, +} GsdAlarmType; -struct _UmAlarmPrivate +struct _GsdAlarmPrivate { GCancellable *cancellable; GDateTime *time; GDateTime *previous_wakeup_time; GMainContext *context; GSource *immediate_wakeup_source; + GRecMutex lock; - UmAlarmType type; + GsdAlarmType type; union { Timer timer; Timeout timeout; @@ -74,34 +75,30 @@ enum { NUMBER_OF_SIGNALS, }; -static void schedule_wakeups (UmAlarm *self); -static void schedule_wakeups_with_timeout_source (UmAlarm *self); +static void schedule_wakeups (GsdAlarm *self); +static void schedule_wakeups_with_timeout_source (GsdAlarm *self); static guint signals[NUMBER_OF_SIGNALS] = { 0 }; -G_DEFINE_TYPE (UmAlarm, um_alarm, G_TYPE_OBJECT); +G_DEFINE_TYPE (GsdAlarm, gsd_alarm, G_TYPE_OBJECT); static void -clear_scheduled_immediate_wakeup (UmAlarm *self) +clear_scheduled_immediate_wakeup (GsdAlarm *self) { - if (self->priv->immediate_wakeup_source != NULL) { - g_source_destroy (self->priv->immediate_wakeup_source); - self->priv->immediate_wakeup_source = NULL; - } + g_clear_pointer (&self->priv->immediate_wakeup_source, + (GDestroyNotify) + g_source_destroy); } static void -clear_scheduled_timer_wakeups (UmAlarm *self) +clear_scheduled_timer_wakeups (GsdAlarm *self) { #ifdef HAVE_TIMERFD GError *error; gboolean is_closed; - if (self->priv->timer.stream == NULL) { - return; - } - - g_source_destroy (self->priv->timer.source); - self->priv->timer.source = NULL; + g_clear_pointer (&self->priv->timer.source, + (GDestroyNotify) + g_source_destroy); error = NULL; is_closed = g_input_stream_close (self->priv->timer.stream, @@ -109,35 +106,35 @@ clear_scheduled_timer_wakeups (UmAlarm *self) &error); if (!is_closed) { - g_warning ("UmAlarm: could not close timer stream: %s", + g_warning ("GsdAlarm: could not close timer stream: %s", error->message); g_error_free (error); } - g_object_unref (self->priv->timer.stream); - self->priv->timer.stream = NULL; - + g_clear_object (&self->priv->timer.stream); #endif } static void -clear_scheduled_timeout_wakeups (UmAlarm *self) +clear_scheduled_timeout_wakeups (GsdAlarm *self) { - g_source_destroy (self->priv->timeout.source); - self->priv->timeout.source = NULL; + g_clear_pointer (&self->priv->timeout.source, + (GDestroyNotify) + g_source_destroy); } static void -clear_scheduled_wakeups (UmAlarm *self) +clear_scheduled_wakeups (GsdAlarm *self) { + g_rec_mutex_lock (&self->priv->lock); clear_scheduled_immediate_wakeup (self); switch (self->priv->type) { - case UM_ALARM_TYPE_TIMER: + case GSD_ALARM_TYPE_TIMER: clear_scheduled_timer_wakeups (self); break; - case UM_ALARM_TYPE_TIMEOUT: + case GSD_ALARM_TYPE_TIMEOUT: clear_scheduled_timeout_wakeups (self); break; @@ -145,57 +142,46 @@ clear_scheduled_wakeups (UmAlarm *self) break; } - if (self->priv->cancellable != NULL) { - g_object_unref (self->priv->cancellable); - self->priv->cancellable = NULL; - } + g_clear_object (&self->priv->cancellable); - if (self->priv->context != NULL) { - g_main_context_unref (self->priv->context); - self->priv->context = NULL; - } + g_clear_pointer (&self->priv->context, + (GDestroyNotify) + g_main_context_unref); - if (self->priv->previous_wakeup_time != NULL) { - g_date_time_unref (self->priv->previous_wakeup_time); - self->priv->previous_wakeup_time = NULL; - } + g_clear_pointer (&self->priv->previous_wakeup_time, + (GDestroyNotify) + g_date_time_unref); + + g_clear_pointer (&self->priv->time, + (GDestroyNotify) + g_date_time_unref); - self->priv->type = UM_ALARM_TYPE_UNSCHEDULED; + g_assert (self->priv->timeout.source == NULL); + + self->priv->type = GSD_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_unlock (&self->priv->lock); } static void -um_alarm_finalize (GObject *object) +gsd_alarm_finalize (GObject *object) { - UmAlarm *self = UM_ALARM (object); - - if (self->priv->cancellable != NULL && - !g_cancellable_is_cancelled (self->priv->cancellable)) { - g_cancellable_cancel (self->priv->cancellable); - } + GsdAlarm *self = GSD_ALARM (object); clear_scheduled_wakeups (self); - if (self->priv->time != NULL) { - g_date_time_unref (self->priv->time); - } - - if (self->priv->previous_wakeup_time != NULL) { - g_date_time_unref (self->priv->previous_wakeup_time); - } - - G_OBJECT_CLASS (um_alarm_parent_class)->finalize (object); + G_OBJECT_CLASS (gsd_alarm_parent_class)->finalize (object); } static void -um_alarm_class_init (UmAlarmClass *klass) +gsd_alarm_class_init (GsdAlarmClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); - object_class->finalize = um_alarm_finalize; + object_class->finalize = gsd_alarm_finalize; - g_type_class_add_private (klass, sizeof (UmAlarmPrivate)); + g_type_class_add_private (klass, sizeof (GsdAlarmPrivate)); signals[FIRED] = g_signal_new ("fired", G_TYPE_FROM_CLASS (klass), @@ -213,37 +199,38 @@ um_alarm_class_init (UmAlarmClass *klass) } static void -um_alarm_init (UmAlarm *self) +gsd_alarm_init (GsdAlarm *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - UM_TYPE_ALARM, - UmAlarmPrivate); - self->priv->type = UM_ALARM_TYPE_UNSCHEDULED; + GSD_TYPE_ALARM, + GsdAlarmPrivate); + self->priv->type = GSD_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_init (&self->priv->lock); } static void on_cancelled (GCancellable *cancellable, gpointer user_data) { - UmAlarm *self = UM_ALARM (user_data); + GsdAlarm *self = GSD_ALARM (user_data); clear_scheduled_wakeups (self); } static void -fire_alarm (UmAlarm *self) +fire_alarm (GsdAlarm *self) { g_signal_emit (G_OBJECT (self), signals[FIRED], 0); } static void -rearm_alarm (UmAlarm *self) +rearm_alarm (GsdAlarm *self) { g_signal_emit (G_OBJECT (self), signals[REARMED], 0); } static void -fire_or_rearm_alarm (UmAlarm *self) +fire_or_rearm_alarm (GsdAlarm *self) { GTimeSpan time_until_fire; GTimeSpan previous_time_until_fire; @@ -287,23 +274,38 @@ fire_or_rearm_alarm (UmAlarm *self) } static gboolean -on_immediate_wakeup_source_ready (gpointer user_data) +on_immediate_wakeup_source_ready (GsdAlarm *self) { - UmAlarm *self = UM_ALARM (user_data); + g_return_val_if_fail (self->priv->type != GSD_ALARM_TYPE_UNSCHEDULED, FALSE); + + g_rec_mutex_lock (&self->priv->lock); + if (g_cancellable_is_cancelled (self->priv->cancellable)) { + goto out; + } fire_or_rearm_alarm (self); +out: + g_rec_mutex_unlock (&self->priv->lock); return FALSE; } #ifdef HAVE_TIMERFD static gboolean on_timer_source_ready (GObject *stream, - gpointer user_data) + GsdAlarm *self) { - UmAlarm *self = UM_ALARM (user_data); gint64 number_of_fires; gssize bytes_read; + gboolean run_again = FALSE; + + g_return_val_if_fail (GSD_IS_ALARM (self), FALSE); + g_return_val_if_fail (self->priv->type == GSD_ALARM_TYPE_TIMER, FALSE); + + g_rec_mutex_lock (&self->priv->lock); + if (g_cancellable_is_cancelled (self->priv->cancellable)) { + goto out; + } bytes_read = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream), &number_of_fires, @@ -313,31 +315,44 @@ on_timer_source_ready (GObject *stream, if (bytes_read == sizeof (gint64)) { if (number_of_fires < 0 || number_of_fires > 1) { - g_warning ("UmAlarm: expected timerfd to report firing once," + g_warning ("GsdAlarm: expected timerfd to report firing once," "but it reported firing %ld times\n", (long) number_of_fires); } } fire_or_rearm_alarm (self); - return TRUE; + run_again = TRUE; +out: + g_rec_mutex_unlock (&self->priv->lock); + return run_again; +} + +static void +clear_timer_source_pointer (GsdAlarm *self) +{ + self->priv->timer.source = NULL; } #endif static gboolean -schedule_wakeups_with_timerfd (UmAlarm *self) +schedule_wakeups_with_timerfd (GsdAlarm *self) { #ifdef HAVE_TIMERFD struct itimerspec timer_spec; int fd; int result; + static gboolean seen_before = FALSE; - g_debug ("UmAlarm: trying to use kernel timer"); + if (!seen_before) { + g_debug ("GsdAlarm: trying to use kernel timer"); + seen_before = TRUE; + } fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC | TFD_NONBLOCK); if (fd < 0) { - g_debug ("UmAlarm: could not create timer fd: %m"); + g_debug ("GsdAlarm: could not create timer fd: %m"); return FALSE; } @@ -350,11 +365,11 @@ schedule_wakeups_with_timerfd (UmAlarm *self) NULL); if (result < 0) { - g_debug ("UmAlarm: could not set timer: %m"); + g_debug ("GsdAlarm: could not set timer: %m"); return FALSE; } - self->priv->type = UM_ALARM_TYPE_TIMER; + self->priv->type = GSD_ALARM_TYPE_TIMER; self->priv->timer.stream = g_unix_input_stream_new (fd, TRUE); self->priv->timer.source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (self->priv->timer.stream), @@ -363,9 +378,11 @@ schedule_wakeups_with_timerfd (UmAlarm *self) (GSourceFunc) on_timer_source_ready, self, - NULL); + (GDestroyNotify) + clear_timer_source_pointer); g_source_attach (self->priv->timer.source, self->priv->context); + g_source_unref (self->priv->timer.source); return TRUE; @@ -375,31 +392,51 @@ schedule_wakeups_with_timerfd (UmAlarm *self) } static gboolean -on_timeout_source_ready (gpointer user_data) +on_timeout_source_ready (GsdAlarm *self) { - UmAlarm *self = UM_ALARM (user_data); + g_return_val_if_fail (GSD_IS_ALARM (self), FALSE); + + g_rec_mutex_lock (&self->priv->lock); + + if (g_cancellable_is_cancelled (self->priv->cancellable) || + self->priv->type == GSD_ALARM_TYPE_UNSCHEDULED) { + goto out; + } fire_or_rearm_alarm (self); + if (g_cancellable_is_cancelled (self->priv->cancellable)) { + goto out; + } + schedule_wakeups_with_timeout_source (self); +out: + g_rec_mutex_unlock (&self->priv->lock); return FALSE; } static void -schedule_wakeups_with_timeout_source (UmAlarm *self) +clear_timeout_source_pointer (GsdAlarm *self) +{ + self->priv->timeout.source = NULL; +} + +static void +schedule_wakeups_with_timeout_source (GsdAlarm *self) { GDateTime *now; GTimeSpan time_span; guint interval; - self->priv->type = UM_ALARM_TYPE_TIMEOUT; + + self->priv->type = GSD_ALARM_TYPE_TIMEOUT; now = g_date_time_new_now_local (); time_span = g_date_time_difference (self->priv->time, now); g_date_time_unref (now); time_span = CLAMP (time_span, 1000 * G_TIME_SPAN_MILLISECOND, G_MAXUINT * G_TIME_SPAN_MILLISECOND); - interval = time_span / G_TIME_SPAN_MILLISECOND; + interval = (guint) time_span / G_TIME_SPAN_MILLISECOND; /* We poll every 10 seconds or so because we want to catch time skew */ @@ -410,27 +447,40 @@ schedule_wakeups_with_timeout_source (UmAlarm *self) (GSourceFunc) on_timeout_source_ready, self, - NULL); + (GDestroyNotify) + clear_timeout_source_pointer); g_source_attach (self->priv->timeout.source, self->priv->context); + g_source_unref (self->priv->timeout.source); } static void -schedule_wakeups (UmAlarm *self) +schedule_wakeups (GsdAlarm *self) { gboolean wakeup_scheduled; wakeup_scheduled = schedule_wakeups_with_timerfd (self); if (!wakeup_scheduled) { - g_debug ("UmAlarm: falling back to polling timeout\n"); + static gboolean seen_before = FALSE; + + if (!seen_before) { + g_debug ("GsdAlarm: falling back to polling timeout"); + seen_before = TRUE; + } schedule_wakeups_with_timeout_source (self); } } static void -schedule_immediate_wakeup (UmAlarm *self) +clear_immediate_wakeup_source_pointer (GsdAlarm *self) +{ + self->priv->immediate_wakeup_source = NULL; +} + +static void +schedule_immediate_wakeup (GsdAlarm *self) { self->priv->immediate_wakeup_source = g_idle_source_new (); @@ -438,26 +488,29 @@ schedule_immediate_wakeup (UmAlarm *self) (GSourceFunc) on_immediate_wakeup_source_ready, self, - NULL); - + (GDestroyNotify) + clear_immediate_wakeup_source_pointer); g_source_attach (self->priv->immediate_wakeup_source, self->priv->context); + g_source_unref (self->priv->immediate_wakeup_source); } void -um_alarm_set (UmAlarm *self, - GDateTime *time, - GCancellable *cancellable) +gsd_alarm_set_time (GsdAlarm *self, + GDateTime *time, + GCancellable *cancellable) { + if (g_cancellable_is_cancelled (cancellable)) { + return; + } + if (self->priv->cancellable != NULL) { if (!g_cancellable_is_cancelled (self->priv->cancellable)) { g_cancellable_cancel (cancellable); } - if (self->priv->cancellable != NULL) { - g_object_unref (self->priv->cancellable); - self->priv->cancellable = NULL; - } + g_object_unref (self->priv->cancellable); + self->priv->cancellable = NULL; } if (cancellable == NULL) { @@ -479,12 +532,18 @@ um_alarm_set (UmAlarm *self, schedule_immediate_wakeup (self); } -UmAlarm * -um_alarm_new (void) +GDateTime * +gsd_alarm_get_time (GsdAlarm *self) +{ + return self->priv->time; +} + +GsdAlarm * +gsd_alarm_new (void) { - UmAlarm *self; + GsdAlarm *self; - self = UM_ALARM (g_object_new (UM_TYPE_ALARM, NULL)); + self = GSD_ALARM (g_object_new (GSD_TYPE_ALARM, NULL)); - return UM_ALARM (self); + return GSD_ALARM (self); } diff --git a/panels/user-accounts/gsd-alarm.h b/panels/user-accounts/gsd-alarm.h new file mode 100644 index 000000000..da17d4ca2 --- /dev/null +++ b/panels/user-accounts/gsd-alarm.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_ALARM_H__ +#define __GSD_ALARM_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define GSD_TYPE_ALARM (gsd_alarm_get_type ()) +#define GSD_ALARM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_ALARM, GsdAlarm)) +#define GSD_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_ALARM, GsdAlarmClass)) +#define GSD_IS_ALARM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_ALARM)) +#define GSD_IS_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_ALARM)) +#define GSD_ALARM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSD_TYPE_ALARM, GsdAlarmClass)) + +typedef struct _GsdAlarm GsdAlarm; +typedef struct _GsdAlarmClass GsdAlarmClass; +typedef struct _GsdAlarmPrivate GsdAlarmPrivate; + +struct _GsdAlarm +{ + GObject parent; + + GsdAlarmPrivate *priv; +}; + +struct _GsdAlarmClass +{ + GObjectClass parent_class; + + void (* fired) (GsdAlarm *alarm); + void (* rearmed) (GsdAlarm *alarm); +}; + +GType gsd_alarm_get_type (void); + +GsdAlarm *gsd_alarm_new (void); +void gsd_alarm_set_time (GsdAlarm *alarm, + GDateTime *time, + GCancellable *cancellable); +GDateTime *gsd_alarm_get_time (GsdAlarm *alarm); +G_END_DECLS + +#endif /* __GSD_ALARM_H__ */ diff --git a/panels/user-accounts/gsd-identity-inquiry-private.h b/panels/user-accounts/gsd-identity-inquiry-private.h new file mode 100644 index 000000000..e4df1bf15 --- /dev/null +++ b/panels/user-accounts/gsd-identity-inquiry-private.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_IDENTITY_INQUIRY_PRIVATE_H__ +#define __GSD_IDENTITY_INQUIRY_PRIVATE_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "gsd-identity-inquiry.h" + +G_BEGIN_DECLS + +void _gsd_identity_inquiry_emit_complete (GsdIdentityInquiry *inquiry); +G_END_DECLS + +#endif /* __GSD_IDENTITY_INQUIRY_PRIVATE_H__ */ diff --git a/panels/user-accounts/gsd-identity-inquiry.c b/panels/user-accounts/gsd-identity-inquiry.c new file mode 100644 index 000000000..fa1646c06 --- /dev/null +++ b/panels/user-accounts/gsd-identity-inquiry.c @@ -0,0 +1,146 @@ +/* -*- Mode: C; tab-width: 8; ident-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Ray Strode + */ + +#include "config.h" + +#include "gsd-identity-inquiry.h" +#include "gsd-identity-inquiry-private.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +enum { + COMPLETE, + NUMBER_OF_SIGNALS, +}; + +static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + +G_DEFINE_INTERFACE (GsdIdentityInquiry, + gsd_identity_inquiry, + G_TYPE_OBJECT); + +static void +gsd_identity_inquiry_default_init (GsdIdentityInquiryInterface *interface) +{ + signals[COMPLETE] = g_signal_new ("complete", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +void +_gsd_identity_inquiry_emit_complete (GsdIdentityInquiry *self) +{ + g_signal_emit (G_OBJECT (self), signals[COMPLETE], 0); +} + +char * +gsd_identity_inquiry_get_name (GsdIdentityInquiry *self) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (self), NULL); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (self)->get_name (self); +} + +char * +gsd_identity_inquiry_get_banner (GsdIdentityInquiry *self) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (self), NULL); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (self)->get_banner (self); +} + +gboolean +gsd_identity_inquiry_is_complete (GsdIdentityInquiry *self) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (self), TRUE); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (self)->is_complete (self); +} + +void +gsd_identity_inquiry_iter_init (GsdIdentityInquiryIter *iter, + GsdIdentityInquiry *inquiry) +{ + g_return_if_fail (GSD_IS_IDENTITY_INQUIRY (inquiry)); + + GSD_IDENTITY_INQUIRY_GET_IFACE (inquiry)->iter_init (iter, inquiry); +} + +GsdIdentityQuery * +gsd_identity_inquiry_iter_next (GsdIdentityInquiryIter *iter, + GsdIdentityInquiry *inquiry) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (inquiry), NULL); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (inquiry)->iter_next (iter, inquiry); +} + +GsdIdentity * +gsd_identity_inquiry_get_identity (GsdIdentityInquiry *self) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (self), NULL); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (self)->get_identity (self); +} + +GsdIdentityQueryMode +gsd_identity_query_get_mode (GsdIdentityInquiry *self, + GsdIdentityQuery *query) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (self), + GSD_IDENTITY_QUERY_MODE_INVISIBLE); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (self)->get_mode (self, query); +} + +char * +gsd_identity_query_get_prompt (GsdIdentityInquiry *self, + GsdIdentityQuery *query) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (self), NULL); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (self)->get_prompt (self, query); +} + +void +gsd_identity_inquiry_answer_query (GsdIdentityInquiry *self, + GsdIdentityQuery *query, + const char *answer) +{ + g_return_if_fail (GSD_IS_IDENTITY_INQUIRY (self)); + + GSD_IDENTITY_INQUIRY_GET_IFACE (self)->answer_query (self, query, answer); +} + +gboolean +gsd_identity_query_is_answered (GsdIdentityInquiry *self, + GsdIdentityQuery *query) +{ + g_return_val_if_fail (GSD_IS_IDENTITY_INQUIRY (self), FALSE); + + return GSD_IDENTITY_INQUIRY_GET_IFACE (self)->is_answered (self, query); +} diff --git a/panels/user-accounts/gsd-identity-inquiry.h b/panels/user-accounts/gsd-identity-inquiry.h new file mode 100644 index 000000000..771ec10c3 --- /dev/null +++ b/panels/user-accounts/gsd-identity-inquiry.h @@ -0,0 +1,108 @@ +/* -*- Mode: C; tab-width: 8; ident-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_IDENTITY_INQUIRY_H__ +#define __GSD_IDENTITY_INQUIRY_H__ + +#include <stdint.h> + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include "gsd-identity.h" + +G_BEGIN_DECLS + +#define GSD_TYPE_IDENTITY_INQUIRY (gsd_identity_inquiry_get_type ()) +#define GSD_IDENTITY_INQUIRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_IDENTITY_INQUIRY, GsdIdentityInquiry)) +#define GSD_IDENTITY_INQUIRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_IDENTITY_INQUIRY, GsdIdentityInquiryClass)) +#define GSD_IS_IDENTITY_INQUIRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_IDENTITY_INQUIRY)) +#define GSD_IDENTITY_INQUIRY_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), GSD_TYPE_IDENTITY_INQUIRY, GsdIdentityInquiryInterface)) +typedef struct _GsdIdentityInquiry GsdIdentityInquiry; +typedef struct _GsdIdentityInquiryInterface GsdIdentityInquiryInterface; +typedef struct _GsdIdentityInquiryIter GsdIdentityInquiryIter; + +typedef struct _GsdIdentityQuery GsdIdentityQuery; + +typedef void (* GsdIdentityInquiryFunc) (GsdIdentityInquiry *inquiry, + GCancellable *cancellable, + gpointer user_data); + +typedef enum +{ + GSD_IDENTITY_QUERY_MODE_INVISIBLE, + GSD_IDENTITY_QUERY_MODE_VISIBLE +} GsdIdentityQueryMode; + +struct _GsdIdentityInquiryIter +{ + gpointer data; +}; + +struct _GsdIdentityInquiryInterface +{ + GTypeInterface base_interface; + + GsdIdentity * (* get_identity) (GsdIdentityInquiry *inquiry); + char * (* get_name) (GsdIdentityInquiry *inquiry); + char * (* get_banner) (GsdIdentityInquiry *inquiry); + gboolean (* is_complete) (GsdIdentityInquiry *inquiry); + void (* answer_query) (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query, + const char *answer); + + void (* iter_init) (GsdIdentityInquiryIter *iter, + GsdIdentityInquiry *inquiry); + GsdIdentityQuery * (* iter_next) (GsdIdentityInquiryIter *iter, + GsdIdentityInquiry *inquiry); + + GsdIdentityQueryMode (* get_mode) (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query); + char * (* get_prompt) (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query); + gboolean (* is_answered) (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query); +}; + +GType gsd_identity_inquiry_get_type (void); + +GsdIdentity *gsd_identity_inquiry_get_identity (GsdIdentityInquiry *inquiry); +char *gsd_identity_inquiry_get_name (GsdIdentityInquiry *inquiry); +char *gsd_identity_inquiry_get_banner (GsdIdentityInquiry *inquiry); +gboolean gsd_identity_inquiry_is_complete (GsdIdentityInquiry *inquiry); +void gsd_identity_inquiry_answer_query (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query, + const char *answer); + +void gsd_identity_inquiry_iter_init (GsdIdentityInquiryIter *iter, + GsdIdentityInquiry *inquiry); +GsdIdentityQuery *gsd_identity_inquiry_iter_next (GsdIdentityInquiryIter *iter, GsdIdentityInquiry *inquiry); + +GsdIdentityQueryMode gsd_identity_query_get_mode (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query); +char *gsd_identity_query_get_prompt (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query); +gboolean gsd_identity_query_is_answered (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query); + +#endif /* __GSD_IDENTITY_INQUIRY_H__ */ diff --git a/panels/user-accounts/gsd-identity-manager-private.h b/panels/user-accounts/gsd-identity-manager-private.h new file mode 100644 index 000000000..13b851279 --- /dev/null +++ b/panels/user-accounts/gsd-identity-manager-private.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_IDENTITY_MANAGER_PRIVATE_H__ +#define __GSD_IDENTITY_MANAGER_PRIVATE_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "gsd-identity-manager.h" + +G_BEGIN_DECLS + +void _gsd_identity_manager_emit_identity_added (GsdIdentityManager *identity_manager, + GsdIdentity *identity); +void _gsd_identity_manager_emit_identity_removed (GsdIdentityManager *identity_manager, + GsdIdentity *identity); +void _gsd_identity_manager_emit_identity_refreshed (GsdIdentityManager *identity_manager, + GsdIdentity *identity); +void _gsd_identity_manager_emit_identity_renamed (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + +void _gsd_identity_manager_emit_identity_expiring (GsdIdentityManager *self, + GsdIdentity *identity); + +void _gsd_identity_manager_emit_identity_needs_renewal (GsdIdentityManager *identity_manager, + GsdIdentity *identity); +void _gsd_identity_manager_emit_identity_expired (GsdIdentityManager *identity_manager, + GsdIdentity *identity); +G_END_DECLS + +#endif /* __GSD_IDENTITY_MANAGER_PRIVATE_H__ */ diff --git a/panels/user-accounts/gsd-identity-manager.c b/panels/user-accounts/gsd-identity-manager.c new file mode 100644 index 000000000..699767614 --- /dev/null +++ b/panels/user-accounts/gsd-identity-manager.c @@ -0,0 +1,238 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include <glib-object.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "gsd-identity-manager.h" +#include "gsd-identity-manager-private.h" + +enum { + IDENTITY_ADDED, + IDENTITY_REMOVED, + IDENTITY_RENAMED, + IDENTITY_REFRESHED, + IDENTITY_NEEDS_RENEWAL, + IDENTITY_EXPIRING, + IDENTITY_EXPIRED, + NUMBER_OF_SIGNALS, +}; + +static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + +G_DEFINE_INTERFACE (GsdIdentityManager, gsd_identity_manager, G_TYPE_OBJECT); + +static void +gsd_identity_manager_default_init (GsdIdentityManagerInterface *interface) +{ + signals[IDENTITY_ADDED] = g_signal_new ("identity-added", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdIdentityManagerInterface, identity_added), + NULL, NULL, NULL, + G_TYPE_NONE, 1, GSD_TYPE_IDENTITY); + signals[IDENTITY_REMOVED] = g_signal_new ("identity-removed", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdIdentityManagerInterface, identity_removed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, GSD_TYPE_IDENTITY); + signals[IDENTITY_REFRESHED] = g_signal_new ("identity-refreshed", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdIdentityManagerInterface, identity_refreshed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, GSD_TYPE_IDENTITY); + signals[IDENTITY_RENAMED] = g_signal_new ("identity-renamed", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdIdentityManagerInterface, identity_renamed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, GSD_TYPE_IDENTITY); + signals[IDENTITY_NEEDS_RENEWAL] = g_signal_new ("identity-needs-renewal", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdIdentityManagerInterface, identity_needs_renewal), + NULL, NULL, NULL, + G_TYPE_NONE, 1, GSD_TYPE_IDENTITY); + signals[IDENTITY_EXPIRING] = g_signal_new ("identity-expiring", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdIdentityManagerInterface, identity_expiring), + NULL, NULL, NULL, + G_TYPE_NONE, 1, GSD_TYPE_IDENTITY); + signals[IDENTITY_EXPIRED] = g_signal_new ("identity-expired", + G_TYPE_FROM_INTERFACE (interface), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdIdentityManagerInterface, identity_expired), + NULL, NULL, NULL, + G_TYPE_NONE, 1, GSD_TYPE_IDENTITY); +} + +GQuark +gsd_identity_manager_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) { + error_quark = g_quark_from_static_string ("gsd-identity-manager-error"); + } + + return error_quark; +} + +void +gsd_identity_manager_list_identities (GsdIdentityManager *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSD_IDENTITY_MANAGER_GET_IFACE (self)->list_identities (self, + cancellable, + callback, + user_data); +} + +GList * +gsd_identity_manager_list_identities_finish (GsdIdentityManager *self, + GAsyncResult *result, + GError **error) +{ + return GSD_IDENTITY_MANAGER_GET_IFACE (self)->list_identities_finish (self, + result, + error); +} + +void +gsd_identity_manager_renew_identity (GsdIdentityManager *self, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSD_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity (self, identity, cancellable, callback, user_data); +} + +void +gsd_identity_manager_renew_identity_finish (GsdIdentityManager *self, + GAsyncResult *result, + GError **error) +{ + GSD_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity_finish (self, result, error); +} + +void +gsd_identity_manager_sign_identity_in (GsdIdentityManager *self, + const char *identifier, + GsdIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSD_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in (self, identifier, inquiry_func, inquiry_data, cancellable, callback, user_data); +} + +GsdIdentity * +gsd_identity_manager_sign_identity_in_finish (GsdIdentityManager *self, + GAsyncResult *result, + GError **error) +{ + return GSD_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in_finish (self, result, error); +} + +void +gsd_identity_manager_sign_identity_out (GsdIdentityManager *self, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSD_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out (self, identity, cancellable, callback, user_data); +} + +void +gsd_identity_manager_sign_identity_out_finish (GsdIdentityManager *self, + GAsyncResult *result, + GError **error) +{ + GSD_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out_finish (self, result, error); +} + +char * +gsd_identity_manager_name_identity (GsdIdentityManager *self, + GsdIdentity *identity) +{ + return GSD_IDENTITY_MANAGER_GET_IFACE (self)->name_identity (self, + identity); +} + +void +_gsd_identity_manager_emit_identity_added (GsdIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_emit (G_OBJECT (self), signals[IDENTITY_ADDED], 0, identity); +} + +void +_gsd_identity_manager_emit_identity_removed (GsdIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_emit (G_OBJECT (self), signals[IDENTITY_REMOVED], 0, identity); +} + +void +_gsd_identity_manager_emit_identity_renamed (GsdIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_emit (G_OBJECT (self), signals[IDENTITY_RENAMED], 0, identity); +} + +void +_gsd_identity_manager_emit_identity_refreshed (GsdIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_emit (G_OBJECT (self), signals[IDENTITY_REFRESHED], 0, identity); +} + +void +_gsd_identity_manager_emit_identity_needs_renewal (GsdIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_emit (G_OBJECT (self), signals[IDENTITY_NEEDS_RENEWAL], 0, identity); +} + +void +_gsd_identity_manager_emit_identity_expiring (GsdIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_emit (G_OBJECT (self), signals[IDENTITY_EXPIRING], 0, identity); +} + +void +_gsd_identity_manager_emit_identity_expired (GsdIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_emit (G_OBJECT (self), signals[IDENTITY_EXPIRED], 0, identity); +} + diff --git a/panels/user-accounts/gsd-identity-manager.h b/panels/user-accounts/gsd-identity-manager.h new file mode 100644 index 000000000..d80a592aa --- /dev/null +++ b/panels/user-accounts/gsd-identity-manager.h @@ -0,0 +1,162 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_IDENTITY_MANAGER_H__ +#define __GSD_IDENTITY_MANAGER_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include "gsd-identity.h" +#include "gsd-identity-inquiry.h" + +G_BEGIN_DECLS + +#define GSD_TYPE_IDENTITY_MANAGER (gsd_identity_manager_get_type ()) +#define GSD_IDENTITY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_IDENTITY_MANAGER, GsdIdentityManager)) +#define GSD_IDENTITY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_IDENTITY_MANAGER, GsdIdentityManagerInterface)) +#define GSD_IS_IDENTITY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_IDENTITY_MANAGER)) +#define GSD_IDENTITY_MANAGER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), GSD_TYPE_IDENTITY_MANAGER, GsdIdentityManagerInterface)) +#define GSD_IDENTITY_MANAGER_ERROR (gsd_identity_manager_error_quark ()) + +typedef struct _GsdIdentityManager GsdIdentityManager; +typedef struct _GsdIdentityManagerInterface GsdIdentityManagerInterface; +typedef enum _GsdIdentityManagerError GsdIdentityManagerError; + +struct _GsdIdentityManagerInterface +{ + GTypeInterface base_interface; + + /* Signals */ + void (* identity_added) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + + void (* identity_removed) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + void (* identity_renamed) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + void (* identity_refreshed) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + void (* identity_needs_renewal) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + void (* identity_expiring) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + void (* identity_expired) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + + /* Virtual Functions */ + void (* list_identities) (GsdIdentityManager *identity_manager, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GList * (* list_identities_finish) (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void (* sign_identity_in) (GsdIdentityManager *identity_manager, + const char *identifier, + GsdIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GsdIdentity * (* sign_identity_in_finish) (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void (* sign_identity_out) (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + void (* sign_identity_out_finish) (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void (* renew_identity) (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + void (* renew_identity_finish) (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + char * (* name_identity) (GsdIdentityManager *identity_manager, + GsdIdentity *identity); +}; + +enum _GsdIdentityManagerError +{ + GSD_IDENTITY_MANAGER_ERROR_INITIALIZING, + GSD_IDENTITY_MANAGER_ERROR_MONITORING, + GSD_IDENTITY_MANAGER_ERROR_SIGNING_IN, + GSD_IDENTITY_MANAGER_ERROR_SIGNING_OUT +}; + +GType gsd_identity_manager_get_type (void); +GQuark gsd_identity_manager_error_quark (void); + +void gsd_identity_manager_list_identities (GsdIdentityManager *identity_manager, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GList * gsd_identity_manager_list_identities_finish (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + +void gsd_identity_manager_sign_identity_in (GsdIdentityManager *identity_manager, + const char *identifier, + GsdIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GsdIdentity * gsd_identity_manager_sign_identity_in_finish (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + +void gsd_identity_manager_sign_identity_out (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +void gsd_identity_manager_sign_identity_out_finish (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + +void gsd_identity_manager_renew_identity (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +void gsd_identity_manager_renew_identity_finish (GsdIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + +char *gsd_identity_manager_name_identity (GsdIdentityManager *identity_manager, + GsdIdentity *identity); + +G_END_DECLS + +#endif /* __GSD_IDENTITY_MANAGER_H__ */ diff --git a/panels/user-accounts/gsd-identity-plugin.c b/panels/user-accounts/gsd-identity-plugin.c new file mode 100644 index 000000000..297f5d099 --- /dev/null +++ b/panels/user-accounts/gsd-identity-plugin.c @@ -0,0 +1,607 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> +#include <gmodule.h> + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include <libnotify/notify.h> +#include <gcr/gcr.h> + +#include "gnome-settings-plugin.h" +#include "gsd-identity-plugin.h" +#include "gsd-identity-manager.h" +#include "gsd-kerberos-identity-manager.h" +#include "gsd-kerberos-identity.h" + +struct GsdIdentityPluginPrivate { + GsdIdentityManager *identity_manager; +}; + +#define GSD_IDENTITY_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), GSD_TYPE_IDENTITY_PLUGIN, GsdIdentityPluginPrivate)) + +GNOME_SETTINGS_PLUGIN_REGISTER (GsdIdentityPlugin, gsd_identity_plugin); + +static void +gsd_identity_plugin_init (GsdIdentityPlugin *self) +{ + self->priv = GSD_IDENTITY_PLUGIN_GET_PRIVATE (self); + + g_debug ("GsdIdentityPlugin initializing"); +} + +static void +gsd_identity_plugin_finalize (GObject *object) +{ + GsdIdentityPlugin *self; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSD_IS_IDENTITY_PLUGIN (object)); + + g_debug ("GsdIdentityPlugin: finalizing"); + + self = GSD_IDENTITY_PLUGIN (object); + + g_return_if_fail (self->priv != NULL); + + g_clear_object (&self->priv->identity_manager); + + G_OBJECT_CLASS (gsd_identity_plugin_parent_class)->finalize (object); +} + +static void +on_identity_renewed (GsdIdentityManager *manager, + GAsyncResult *result, + GnomeSettingsPlugin *self) +{ + GError *error; + + error = NULL; + gsd_identity_manager_renew_identity_finish (manager, + result, + &error); + + if (error != NULL) { + g_debug ("GsdIdentityPlugin: could not renew identity: %s", + error->message); + g_error_free (error); + return; + } + + g_debug ("GsdIdentityPlugin: identity renewed"); +} + +static void +on_identity_needs_renewal (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + GsdIdentityPlugin *self) +{ + g_debug ("GsdIdentityPlugin: identity needs renewal"); + gsd_identity_manager_renew_identity (GSD_IDENTITY_MANAGER (self->priv->identity_manager), + identity, + NULL, + (GAsyncReadyCallback) + on_identity_renewed, + self); +} + +static void +on_identity_signed_in (GsdIdentityManager *manager, + GAsyncResult *result, + GnomeSettingsPlugin *self) +{ + GError *error; + + error = NULL; + gsd_identity_manager_sign_identity_in_finish (manager, + result, + &error); + + if (error != NULL) { + g_debug ("GsdIdentityPlugin: could not sign in identity: %s", + error->message); + g_error_free (error); + return; + } + + g_debug ("GsdIdentityPlugin: identity signed in"); +} + +typedef struct { + GsdIdentityPlugin *plugin; + GsdIdentity *identity; + NotifyNotification *notification; + GCancellable *cancellable; + gulong refreshed_signal_id; +} SignInRequest; + +static SignInRequest * +sign_in_request_new (GsdIdentityPlugin *plugin, + GsdIdentity *identity, + NotifyNotification *notification, + GCancellable *cancellable) +{ + SignInRequest *request; + + request = g_slice_new0 (SignInRequest); + + request->plugin = plugin; + request->identity = g_object_ref (identity); + request->notification = notification; + request->cancellable = g_object_ref (cancellable); + + return request; +} + +static void +sign_in_request_free (SignInRequest *data) +{ + GsdIdentityPlugin *plugin = data->plugin; + + g_signal_handler_disconnect (plugin->priv->identity_manager, + data->refreshed_signal_id); + g_object_set_data (G_OBJECT (data->identity), "sign-in-request", NULL); + g_clear_object (&data->identity); + g_clear_object (&data->cancellable); + g_slice_free (SignInRequest, data); +} + +typedef struct { + GsdIdentityPlugin *plugin; + GsdIdentity *identity; + GsdIdentityInquiry *inquiry; + GsdIdentityQuery *query; + GcrSystemPrompt *prompt; + GCancellable *cancellable; +} SystemPromptRequest; + +static SystemPromptRequest * +system_prompt_request_new (GsdIdentityPlugin *plugin, + GcrSystemPrompt *prompt, + GsdIdentity *identity, + GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query, + GCancellable *cancellable) +{ + SystemPromptRequest *data; + + data = g_slice_new0 (SystemPromptRequest); + + data->plugin = plugin; + data->prompt = prompt; + data->identity = g_object_ref (identity); + data->inquiry = g_object_ref (inquiry); + data->query = query; + data->cancellable = g_object_ref (cancellable); + + return data; +} + +static void +system_prompt_request_free (SystemPromptRequest *data) +{ + g_clear_object (&data->identity); + g_clear_object (&data->inquiry); + g_clear_object (&data->cancellable); + g_slice_free (SystemPromptRequest, data); +} + +static void +close_system_prompt (GsdIdentityManager *manager, + GsdIdentity *identity, + SystemPromptRequest *data) +{ + GError *error; + + /* Only close the prompt if the identity we're + * waiting on got refreshed + */ + if (data->identity != identity) { + return; + } + + g_signal_handlers_disconnect_by_func (G_OBJECT (manager), + G_CALLBACK (close_system_prompt), + data); + error = NULL; + if (!gcr_system_prompt_close (data->prompt, + NULL, + &error)) { + if (error != NULL) { + g_debug ("GsdIdentityPlugin: could not close system prompt: %s", + error->message); + g_error_free (error); + } + } +} + +static void +on_password_system_prompt_answered (GcrPrompt *prompt, + GAsyncResult *result, + SystemPromptRequest *request) +{ + GsdIdentityPlugin *self = request->plugin; + GsdIdentityInquiry *inquiry = request->inquiry; + GsdIdentity *identity = request->identity; + GsdIdentityQuery *query = request->query; + GCancellable *cancellable = request->cancellable; + GError *error; + const char *password; + + error = NULL; + password = gcr_prompt_password_finish (prompt, result, &error); + + if (password == NULL) { + if (error != NULL) { + g_debug ("GsdIdentityPlugin: could not get password from user: %s", + error->message); + g_error_free (error); + } else { + g_cancellable_cancel (cancellable); + } + } else if (!g_cancellable_is_cancelled (cancellable)) { + gsd_identity_inquiry_answer_query (inquiry, + query, + password); + } + + close_system_prompt (self->priv->identity_manager, identity, request); + system_prompt_request_free (request); +} + +static void +query_user (GsdIdentityPlugin *self, + GsdIdentity *identity, + GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query, + GcrPrompt *prompt, + GCancellable *cancellable) +{ + SystemPromptRequest *request; + char *prompt_text; + GsdIdentityQueryMode query_mode; + char *description; + char *name; + + g_assert (GSD_IS_KERBEROS_IDENTITY (identity)); + + gcr_prompt_set_title (prompt, _("Sign In to Realm")); + + name = gsd_identity_manager_name_identity (self->priv->identity_manager, + identity); + + description = g_strdup_printf (_("The network realm %s needs some information to sign you in."), name); + g_free (name); + + gcr_prompt_set_description (prompt, description); + g_free (description); + + prompt_text = gsd_identity_query_get_prompt (inquiry, query); + gcr_prompt_set_message (prompt, prompt_text); + g_free (prompt_text); + + request = system_prompt_request_new (self, + GCR_SYSTEM_PROMPT (prompt), + identity, + inquiry, + query, + cancellable); + + g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-refreshed", + G_CALLBACK (close_system_prompt), + request); + + query_mode = gsd_identity_query_get_mode (inquiry, query); + + switch (query_mode) { + case GSD_IDENTITY_QUERY_MODE_INVISIBLE: + gcr_prompt_password_async (prompt, + cancellable, + (GAsyncReadyCallback) + on_password_system_prompt_answered, + request); + break; + case GSD_IDENTITY_QUERY_MODE_VISIBLE: + gcr_prompt_password_async (prompt, + cancellable, + (GAsyncReadyCallback) + on_password_system_prompt_answered, + request); + break; + } +} + +typedef struct { + GsdIdentityPlugin *plugin; + GsdIdentityInquiry *inquiry; + GCancellable *cancellable; +} SystemPromptOpenRequest; + +static SystemPromptOpenRequest * +system_prompt_open_request_new (GsdIdentityPlugin *plugin, + GsdIdentityInquiry *inquiry, + GCancellable *cancellable) +{ + SystemPromptOpenRequest *data; + + data = g_slice_new0 (SystemPromptOpenRequest); + + data->plugin = plugin; + data->inquiry = g_object_ref (inquiry); + data->cancellable = g_object_ref (cancellable); + + return data; +} + +static void +system_prompt_open_request_free (SystemPromptOpenRequest *data) +{ + g_clear_object (&data->inquiry); + g_clear_object (&data->cancellable); + g_slice_free (SystemPromptOpenRequest, data); +} + +static void +on_system_prompt_open (GcrSystemPrompt *system_prompt, + GAsyncResult *result, + SystemPromptOpenRequest *request) +{ + GsdIdentityPlugin *self = request->plugin; + GsdIdentityInquiry *inquiry = request->inquiry; + GCancellable *cancellable = request->cancellable; + GsdIdentity *identity; + GsdIdentityQuery *query; + GcrPrompt *prompt; + GError *error; + GsdIdentityInquiryIter iter; + + error = NULL; + prompt = gcr_system_prompt_open_finish (result, &error); + + if (prompt == NULL) { + if (error != NULL) { + g_debug ("GsdIdentityPlugin: could not open system prompt: %s", + error->message); + g_error_free (error); + } + return; + } + + identity = gsd_identity_inquiry_get_identity (inquiry); + gsd_identity_inquiry_iter_init (&iter, inquiry); + while ((query = gsd_identity_inquiry_iter_next (&iter, inquiry)) != NULL) { + query_user (self, identity, inquiry, query, prompt, cancellable); + } + + system_prompt_open_request_free (request); +} + +static void +on_identity_inquiry (GsdIdentityInquiry *inquiry, + GCancellable *cancellable, + GsdIdentityPlugin *self) +{ + SystemPromptOpenRequest *request; + + request = system_prompt_open_request_new (self, inquiry, cancellable); + gcr_system_prompt_open_async (-1, + cancellable, + (GAsyncReadyCallback) + on_system_prompt_open, + request); +} + +static void +on_sign_in_clicked (NotifyNotification *notification, + const char *acition_id, + SignInRequest *request) +{ + GsdIdentityPlugin *self = request->plugin; + GsdIdentity *identity = request->identity; + const char *identifier; + + identifier = gsd_identity_get_identifier (identity); + gsd_identity_manager_sign_identity_in (self->priv->identity_manager, + identifier, + (GsdIdentityInquiryFunc) + on_identity_inquiry, + self, + request->cancellable, + (GAsyncReadyCallback) + on_identity_signed_in, + self); +} + +static void +close_notification (GCancellable *cancellable, + NotifyNotification *notification) +{ + notify_notification_close (notification, NULL); +} + +static void +cancel_sign_in (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + SignInRequest *data) +{ + if (data->cancellable == NULL) { + return; + } + + if (!g_cancellable_is_cancelled (data->cancellable)) { + g_cancellable_cancel (data->cancellable); + } + + g_clear_object (&data->cancellable); +} + +static void +ask_to_sign_in (GsdIdentityPlugin *self, + GsdIdentity *identity) +{ + NotifyNotification *notification; + char *name; + char *description; + SignInRequest *request; + GCancellable *cancellable; + + request = g_object_get_data (G_OBJECT (identity), "sign-in-request"); + + if (request != NULL) { + if (!g_cancellable_is_cancelled (request->cancellable)) { + g_cancellable_cancel (request->cancellable); + } + } + + g_debug ("GsdIdentityPlugin: asking to sign back in"); + + name = gsd_identity_manager_name_identity (self->priv->identity_manager, + identity); + if (gsd_identity_is_signed_in (identity)) { + description = g_strdup_printf (_("The network realm %s will soon be inaccessible."), + name); + } else { + description = g_strdup_printf (_("The network realm %s is now inaccessible."), + name); + } + g_free (name); + + notification = notify_notification_new (_("Realm Access"), + description, + "dialog-password-symbolic"); + g_free (description); + notify_notification_set_app_name (notification, _("Network Realm")); + + cancellable = g_cancellable_new (); + + request = sign_in_request_new (self, identity, notification, cancellable); + + g_object_set_data (G_OBJECT (identity), "sign-in-request", request); + + g_cancellable_connect (cancellable, + G_CALLBACK (close_notification), + notification, + NULL); + g_signal_connect_swapped (G_OBJECT (notification), + "closed", + G_CALLBACK (sign_in_request_free), + request); + + request->refreshed_signal_id = g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-refreshed", + G_CALLBACK (cancel_sign_in), + request); + + notify_notification_add_action (notification, + "sign-in", + _("Sign In"), + (NotifyActionCallback) + on_sign_in_clicked, + request, + NULL); + + notify_notification_show (notification, NULL); +} + +static void +on_identity_expiring (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + GsdIdentityPlugin *self) +{ + g_debug ("GsdIdentityPlugin: identity expiring"); + ask_to_sign_in (self, identity); +} + +static void +on_identity_expired (GsdIdentityManager *identity_manager, + GsdIdentity *identity, + GsdIdentityPlugin *self) +{ + g_debug ("GsdIdentityPlugin: identity expired"); + ask_to_sign_in (self, identity); +} + +static void +impl_activate (GnomeSettingsPlugin *plugin) +{ + GsdIdentityPlugin *self = GSD_IDENTITY_PLUGIN (plugin); + + if (self->priv->identity_manager != NULL) { + g_debug ("GsdIdentityPlugin: Not activating identity plugin, because it's " + "already active"); + return; + } + + g_debug ("GsdIdentityPlugin: Activating identity plugin"); + self->priv->identity_manager = gsd_kerberos_identity_manager_new (); + g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-needs-renewal", + G_CALLBACK (on_identity_needs_renewal), + self); + g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-expiring", + G_CALLBACK (on_identity_expiring), + self); + g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-expired", + G_CALLBACK (on_identity_expired), + self); +} + +static void +impl_deactivate (GnomeSettingsPlugin *plugin) +{ + GsdIdentityPlugin *self = GSD_IDENTITY_PLUGIN (plugin); + + if (self->priv->identity_manager == NULL) { + g_debug ("GsdIdentityPlugin: Not deactivating identity plugin, " + "because it's already inactive"); + return; + } + + g_debug ("GsdIdentityPlugin: Deactivating identity plugin"); + g_signal_handlers_disconnect_by_func (self, on_identity_needs_renewal, self); + g_clear_object (&self->priv->identity_manager); +} + +static void +gsd_identity_plugin_class_init (GsdIdentityPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GnomeSettingsPluginClass *plugin_class = GNOME_SETTINGS_PLUGIN_CLASS (klass); + + object_class->finalize = gsd_identity_plugin_finalize; + + plugin_class->activate = impl_activate; + plugin_class->deactivate = impl_deactivate; + + g_type_class_add_private (klass, sizeof (GsdIdentityPluginPrivate)); +} + +static void +gsd_identity_plugin_class_finalize (GsdIdentityPluginClass *klass) +{ +} diff --git a/panels/user-accounts/gsd-identity-plugin.h b/panels/user-accounts/gsd-identity-plugin.h new file mode 100644 index 000000000..ddfd9d4bc --- /dev/null +++ b/panels/user-accounts/gsd-identity-plugin.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __GSD_IDENTITY_PLUGIN_H__ +#define __GSD_IDENTITY_PLUGIN_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gmodule.h> + +#include "gnome-settings-plugin.h" + +G_BEGIN_DECLS + +#define GSD_TYPE_IDENTITY_PLUGIN (gsd_identity_plugin_get_type ()) +#define GSD_IDENTITY_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_IDENTITY_PLUGIN, GsdIdentityPlugin)) +#define GSD_IDENTITY_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSD_TYPE_IDENTITY_PLUGIN, GsdIdentityPluginClass)) +#define GSD_IS_IDENTITY_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_IDENTITY_PLUGIN)) +#define GSD_IS_IDENTITY_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_IDENTITY_PLUGIN)) +#define GSD_IDENTITY_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_IDENTITY_PLUGIN, GsdIdentityPluginClass)) + +typedef struct GsdIdentityPluginPrivate GsdIdentityPluginPrivate; + +typedef struct +{ + GnomeSettingsPlugin parent; + GsdIdentityPluginPrivate *priv; +} GsdIdentityPlugin; + +typedef struct +{ + GnomeSettingsPluginClass parent_class; +} GsdIdentityPluginClass; + +GType gsd_identity_plugin_get_type (void) G_GNUC_CONST; + +/* All the plugins must implement this function */ +G_MODULE_EXPORT GType register_gnome_settings_plugin (GTypeModule *module); + +G_END_DECLS + +#endif /* __GSD_IDENTITY_PLUGIN_H__ */ diff --git a/panels/user-accounts/um-identity.c b/panels/user-accounts/gsd-identity.c index abd010794..2a03d3738 100644 --- a/panels/user-accounts/um-identity.c +++ b/panels/user-accounts/gsd-identity.c @@ -23,37 +23,35 @@ #include <glib-object.h> #include <glib/gi18n.h> -#include "um-identity.h" +#include "gsd-identity.h" -G_DEFINE_INTERFACE (UmIdentity, um_identity, G_TYPE_OBJECT); +G_DEFINE_INTERFACE (GsdIdentity, gsd_identity, G_TYPE_OBJECT); static void -um_identity_default_init (UmIdentityInterface *interface) +gsd_identity_default_init (GsdIdentityInterface *interface) { } GQuark -um_identity_error_quark (void) +gsd_identity_error_quark (void) { static GQuark error_quark = 0; if (error_quark == 0) { - error_quark = g_quark_from_static_string ("um-identity-error"); + error_quark = g_quark_from_static_string ("gsd-identity-error"); } return error_quark; } const char * -um_identity_get_identifier (UmIdentity *self) +gsd_identity_get_identifier (GsdIdentity *self) { - g_return_val_if_fail (UM_IDENTITY_GET_IFACE (self)->get_identifier, NULL); - return UM_IDENTITY_GET_IFACE (self)->get_identifier (self); + return GSD_IDENTITY_GET_IFACE (self)->get_identifier (self); } gboolean -um_identity_is_signed_in (UmIdentity *self) +gsd_identity_is_signed_in (GsdIdentity *self) { - g_return_val_if_fail (UM_IDENTITY_GET_IFACE (self)->is_signed_in, FALSE); - return UM_IDENTITY_GET_IFACE (self)->is_signed_in (self); + return GSD_IDENTITY_GET_IFACE (self)->is_signed_in (self); } diff --git a/panels/user-accounts/gsd-identity.h b/panels/user-accounts/gsd-identity.h new file mode 100644 index 000000000..906afc29c --- /dev/null +++ b/panels/user-accounts/gsd-identity.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode <rstrode@redhat.com> + */ + +#ifndef __GSD_IDENTITY_H__ +#define __GSD_IDENTITY_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GSD_TYPE_IDENTITY (gsd_identity_get_type ()) +#define GSD_IDENTITY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_IDENTITY, GsdIdentity)) +#define GSD_IDENTITY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_IDENTITY, GsdIdentityInterface)) +#define GSD_IS_IDENTITY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_IDENTITY)) +#define GSD_IDENTITY_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), GSD_TYPE_IDENTITY, GsdIdentityInterface)) +#define GSD_IDENTITY_ERROR (gsd_identity_error_quark ()) + +typedef struct _GsdIdentity GsdIdentity; +typedef struct _GsdIdentityInterface GsdIdentityInterface; +typedef enum _GsdIdentityError GsdIdentityError; + +struct _GsdIdentityInterface +{ + GTypeInterface base_interface; + + const char * (* get_identifier) (GsdIdentity *identity); + gboolean (* is_signed_in) (GsdIdentity *identity); +}; + +enum _GsdIdentityError +{ + GSD_IDENTITY_ERROR_VERIFYING, + GSD_IDENTITY_ERROR_SIGNING_IN, + GSD_IDENTITY_ERROR_RENEWING, + GSD_IDENTITY_ERROR_ERASING +}; + +GType gsd_identity_get_type (void); +GQuark gsd_identity_error_quark (void); + +const char *gsd_identity_get_identifier (GsdIdentity *identity); +gboolean gsd_identity_is_signed_in (GsdIdentity *identity); + +G_END_DECLS + +#endif /* __GSD_IDENTITY_H__ */ diff --git a/panels/user-accounts/gsd-kerberos-identity-inquiry.c b/panels/user-accounts/gsd-kerberos-identity-inquiry.c new file mode 100644 index 000000000..a6a263140 --- /dev/null +++ b/panels/user-accounts/gsd-kerberos-identity-inquiry.c @@ -0,0 +1,363 @@ +/* -*- Mode: C; tab-width: 8; ident-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Ray Strode + */ + +#include "config.h" + +#include "gsd-kerberos-identity-inquiry.h" +#include "gsd-identity-inquiry-private.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +struct _GsdKerberosIdentityInquiryPrivate +{ + GsdIdentity *identity; + char *name; + char *banner; + GList *queries; + int number_of_queries; + int number_of_unanswered_queries; +}; + +typedef struct +{ + GsdIdentityInquiry *inquiry; + krb5_prompt *kerberos_prompt; + gboolean is_answered; +} GsdKerberosIdentityQuery; + +static void identity_inquiry_interface_init (GsdIdentityInquiryInterface *interface); +static void initable_interface_init (GInitableIface *interface); + +G_DEFINE_TYPE_WITH_CODE (GsdKerberosIdentityInquiry, + gsd_kerberos_identity_inquiry, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_interface_init) + G_IMPLEMENT_INTERFACE (GSD_TYPE_IDENTITY_INQUIRY, + identity_inquiry_interface_init)); + +static gboolean +gsd_kerberos_identity_inquiry_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + return FALSE; + } + + return TRUE; +} + +static void +initable_interface_init (GInitableIface *interface) +{ + interface->init = gsd_kerberos_identity_inquiry_initable_init; +} + +static GsdKerberosIdentityQuery * +gsd_kerberos_identity_query_new (GsdIdentityInquiry *inquiry, + krb5_prompt *kerberos_prompt) +{ + GsdKerberosIdentityQuery *query; + + query = g_slice_new (GsdKerberosIdentityQuery); + query->inquiry = inquiry; + query->kerberos_prompt = kerberos_prompt; + query->is_answered = FALSE; + + return query; +} + +static void +gsd_kerberos_identity_query_free (GsdKerberosIdentityQuery *query) +{ + g_slice_free (GsdKerberosIdentityQuery, query); +} + +static void +gsd_kerberos_identity_inquiry_dispose (GObject *object) +{ + GsdKerberosIdentityInquiry *self = GSD_KERBEROS_IDENTITY_INQUIRY (object); + + g_clear_object (&self->priv->identity); + g_clear_pointer (&self->priv->name, (GDestroyNotify) g_free); + g_clear_pointer (&self->priv->banner, (GDestroyNotify) g_free); + + g_list_foreach (self->priv->queries, + (GFunc) + gsd_kerberos_identity_query_free, + NULL); + g_clear_pointer (&self->priv->queries, (GDestroyNotify) g_list_free); +} + +static void +gsd_kerberos_identity_inquiry_finalize (GObject *object) +{ + G_OBJECT_CLASS (gsd_kerberos_identity_inquiry_parent_class)->finalize (object); +} + +static void +gsd_kerberos_identity_inquiry_class_init (GsdKerberosIdentityInquiryClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gsd_kerberos_identity_inquiry_dispose; + object_class->finalize = gsd_kerberos_identity_inquiry_finalize; + + g_type_class_add_private (klass, sizeof (GsdKerberosIdentityInquiryPrivate)); +} + +static void +gsd_kerberos_identity_inquiry_init (GsdKerberosIdentityInquiry *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GSD_TYPE_KERBEROS_IDENTITY_INQUIRY, + GsdKerberosIdentityInquiryPrivate); +} + +GsdIdentityInquiry * +gsd_kerberos_identity_inquiry_new (GsdKerberosIdentity *identity, + const char *name, + const char *banner, + krb5_prompt prompts[], + int number_of_prompts) +{ + GObject *object; + GsdIdentityInquiry *inquiry; + GsdKerberosIdentityInquiry *self; + GError *error; + int i; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY (identity), NULL); + g_return_val_if_fail (number_of_prompts > 0, NULL); + + object = g_object_new (GSD_TYPE_KERBEROS_IDENTITY_INQUIRY, NULL); + + inquiry = GSD_IDENTITY_INQUIRY (object); + self = GSD_KERBEROS_IDENTITY_INQUIRY (object); + + /* FIXME: make these construct properties */ + self->priv->identity = g_object_ref (identity); + self->priv->name = g_strdup (name); + self->priv->banner = g_strdup (banner); + + self->priv->number_of_queries = 0; + for (i = 0; i < number_of_prompts; i++) { + GsdKerberosIdentityQuery *query; + + query = gsd_kerberos_identity_query_new (inquiry, &prompts[i]); + + self->priv->queries = g_list_prepend (self->priv->queries, query); + self->priv->number_of_queries++; + } + self->priv->queries = g_list_reverse (self->priv->queries); + + self->priv->number_of_unanswered_queries = self->priv->number_of_queries; + + error = NULL; + if (!g_initable_init (G_INITABLE (self), NULL, &error)) { + g_debug ("%s", error->message); + g_error_free (error); + g_object_unref (self); + return NULL; + } + + return inquiry; +} + +static GsdIdentity * +gsd_kerberos_identity_inquiry_get_identity (GsdIdentityInquiry *inquiry) +{ + GsdKerberosIdentityInquiry *self; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry), NULL); + + self = GSD_KERBEROS_IDENTITY_INQUIRY (inquiry); + + return self->priv->identity; +} + +static char * +gsd_kerberos_identity_inquiry_get_name (GsdIdentityInquiry *inquiry) +{ + GsdKerberosIdentityInquiry *self; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry), NULL); + + self = GSD_KERBEROS_IDENTITY_INQUIRY (inquiry); + + return g_strdup (self->priv->name); +} + +static char * +gsd_kerberos_identity_inquiry_get_banner (GsdIdentityInquiry *inquiry) +{ + GsdKerberosIdentityInquiry *self; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry), NULL); + + self = GSD_KERBEROS_IDENTITY_INQUIRY (inquiry); + + return g_strdup (self->priv->banner); +} + +static gboolean +gsd_kerberos_identity_inquiry_is_complete (GsdIdentityInquiry *inquiry) +{ + GsdKerberosIdentityInquiry *self; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry), FALSE); + + self = GSD_KERBEROS_IDENTITY_INQUIRY (inquiry); + + return self->priv->number_of_unanswered_queries == 0; +} + +static void +gsd_kerberos_identity_inquiry_mark_query_answered (GsdKerberosIdentityInquiry *self, + GsdKerberosIdentityQuery *query) +{ + if (query->is_answered) { + return; + } + + query->is_answered = TRUE; + self->priv->number_of_unanswered_queries--; + + if (self->priv->number_of_unanswered_queries == 0) { + _gsd_identity_inquiry_emit_complete (GSD_IDENTITY_INQUIRY (self)); + } +} + +static void +gsd_kerberos_identity_inquiry_answer_query (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query, + const char *answer) +{ + GsdKerberosIdentityInquiry *self; + GsdKerberosIdentityQuery *kerberos_query = (GsdKerberosIdentityQuery *) query; + + g_return_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry)); + g_return_if_fail (inquiry == kerberos_query->inquiry); + g_return_if_fail (!gsd_kerberos_identity_inquiry_is_complete (inquiry)); + + self = GSD_KERBEROS_IDENTITY_INQUIRY (inquiry); + + inquiry = kerberos_query->inquiry; + + strncpy (kerberos_query->kerberos_prompt->reply->data, + answer, + kerberos_query->kerberos_prompt->reply->length); + kerberos_query->kerberos_prompt->reply->length = (unsigned int) strlen (kerberos_query->kerberos_prompt->reply->data); + + gsd_kerberos_identity_inquiry_mark_query_answered (self, kerberos_query); +} + +static void +gsd_kerberos_identity_inquiry_iter_init (GsdIdentityInquiryIter *iter, + GsdIdentityInquiry *inquiry) +{ + GsdKerberosIdentityInquiry *self = GSD_KERBEROS_IDENTITY_INQUIRY (inquiry); + + iter->data = self->priv->queries; +} + +static GsdIdentityQuery * +gsd_kerberos_identity_inquiry_iter_next (GsdIdentityInquiryIter *iter, + GsdIdentityInquiry *inquiry) +{ + GsdIdentityQuery *query; + GList *node; + + node = iter->data; + + if (node == NULL) { + return NULL; + } + + query = (GsdIdentityQuery *) node->data; + + node = node->next; + + iter->data = node; + + return query; +} + +static GsdIdentityQueryMode +gsd_kerberos_identity_query_get_mode (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query) +{ + GsdKerberosIdentityQuery *kerberos_query = (GsdKerberosIdentityQuery *) query; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry), GSD_KERBEROS_IDENTITY_QUERY_MODE_INVISIBLE); + g_return_val_if_fail (inquiry == kerberos_query->inquiry, GSD_KERBEROS_IDENTITY_QUERY_MODE_INVISIBLE); + + if (kerberos_query->kerberos_prompt->hidden) { + return GSD_KERBEROS_IDENTITY_QUERY_MODE_INVISIBLE; + } else { + return GSD_KERBEROS_IDENTITY_QUERY_MODE_VISIBLE; + } +} + +static char * +gsd_kerberos_identity_query_get_prompt (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query) +{ + GsdKerberosIdentityQuery *kerberos_query = (GsdKerberosIdentityQuery *) query; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry), GSD_KERBEROS_IDENTITY_QUERY_MODE_INVISIBLE); + g_return_val_if_fail (inquiry == kerberos_query->inquiry, NULL); + + return g_strdup (kerberos_query->kerberos_prompt->prompt); +} + +static gboolean +gsd_kerberos_identity_query_is_answered (GsdIdentityInquiry *inquiry, + GsdIdentityQuery *query) +{ + GsdKerberosIdentityQuery *kerberos_query = (GsdKerberosIdentityQuery *) query; + + g_return_val_if_fail (GSD_IS_KERBEROS_IDENTITY_INQUIRY (inquiry), GSD_KERBEROS_IDENTITY_QUERY_MODE_INVISIBLE); + g_return_val_if_fail (inquiry == kerberos_query->inquiry, FALSE); + + return kerberos_query->is_answered; +} + +static void +identity_inquiry_interface_init (GsdIdentityInquiryInterface *interface) +{ + interface->get_identity = gsd_kerberos_identity_inquiry_get_identity; + interface->get_name = gsd_kerberos_identity_inquiry_get_name; + interface->get_banner = gsd_kerberos_identity_inquiry_get_banner; + interface->is_complete = gsd_kerberos_identity_inquiry_is_complete; + interface->answer_query = gsd_kerberos_identity_inquiry_answer_query; + interface->iter_init = gsd_kerberos_identity_inquiry_iter_init; + interface->iter_next = gsd_kerberos_identity_inquiry_iter_next; + interface->get_mode = gsd_kerberos_identity_query_get_mode; + interface->get_prompt = gsd_kerberos_identity_query_get_prompt; + interface->is_answered = gsd_kerberos_identity_query_is_answered; +} diff --git a/panels/user-accounts/gsd-kerberos-identity-inquiry.h b/panels/user-accounts/gsd-kerberos-identity-inquiry.h new file mode 100644 index 000000000..30c0956ad --- /dev/null +++ b/panels/user-accounts/gsd-kerberos-identity-inquiry.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; ident-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_KERBEROS_IDENTITY_INQUIRY_H__ +#define __GSD_KERBEROS_IDENTITY_INQUIRY_H__ + +#include <stdint.h> + +#include <glib.h> +#include <glib-object.h> + +#include "gsd-identity-inquiry.h" +#include "gsd-kerberos-identity.h" + +G_BEGIN_DECLS + +#define GSD_TYPE_KERBEROS_IDENTITY_INQUIRY (gsd_kerberos_identity_inquiry_get_type ()) +#define GSD_KERBEROS_IDENTITY_INQUIRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_KERBEROS_IDENTITY_INQUIRY, GsdKerberosIdentityInquiry)) +#define GSD_KERBEROS_IDENTITY_INQUIRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_KERBEROS_IDENTITY_INQUIRY, GsdKerberosIdentityInquiryClass)) +#define GSD_IS_KERBEROS_IDENTITY_INQUIRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_KERBEROS_IDENTITY_INQUIRY)) +#define GSD_IS_KERBEROS_IDENTITY_INQUIRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_KERBEROS_IDENTITY_INQUIRY)) +#define GSD_KERBEROS_IDENTITY_INQUIRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSD_TYPE_KERBEROS_IDENTITY_INQUIRY, GsdKerberosIdentityInquiryClass)) +typedef struct _GsdKerberosIdentity GsdKerberosIdentity; +typedef struct _GsdKerberosIdentityInquiry GsdKerberosIdentityInquiry; +typedef struct _GsdKerberosIdentityInquiryClass GsdKerberosIdentityInquiryClass; +typedef struct _GsdKerberosIdentityInquiryPrivate GsdKerberosIdentityInquiryPrivate; +typedef struct _GsdKerberosIdentityInquiryIter GsdKerberosIdentityInquiryIter; + +typedef enum +{ + GSD_KERBEROS_IDENTITY_QUERY_MODE_INVISIBLE, + GSD_KERBEROS_IDENTITY_QUERY_MODE_VISIBLE +} GsdKerberosIdentityQueryMode; + +struct _GsdKerberosIdentityInquiry +{ + GObject parent; + + GsdKerberosIdentityInquiryPrivate *priv; +}; + +struct _GsdKerberosIdentityInquiryClass +{ + GObjectClass parent_class; +}; + +GType gsd_kerberos_identity_inquiry_get_type (void); + +GsdIdentityInquiry *gsd_kerberos_identity_inquiry_new (GsdKerberosIdentity *identity, + const char *name, + const char *banner, + krb5_prompt prompts[], + int number_of_prompts); + +#endif /* __GSD_KERBEROS_IDENTITY_INQUIRY_H__ */ diff --git a/panels/user-accounts/gsd-kerberos-identity-manager.c b/panels/user-accounts/gsd-kerberos-identity-manager.c new file mode 100644 index 000000000..68c74198b --- /dev/null +++ b/panels/user-accounts/gsd-kerberos-identity-manager.c @@ -0,0 +1,1708 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#include "config.h" + +#include "gsd-kerberos-identity-manager.h" +#include "gsd-identity-manager.h" +#include "gsd-identity-manager-private.h" +#include "gsd-kerberos-identity.h" + +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#include <krb5.h> + +struct _GsdKerberosIdentityManagerPrivate +{ + GHashTable *identities; + GHashTable *expired_identities; + GHashTable *identities_by_realm; + GAsyncQueue *pending_operations; + GCancellable *scheduler_cancellable; + + krb5_context kerberos_context; + GFileMonitor *credentials_cache_monitor; + gulong credentials_cache_changed_signal_id; + char *credentials_cache_type; + + GMutex scheduler_job_lock; + GCond scheduler_job_unblocked; + gboolean is_blocking_scheduler_job; + + volatile int pending_refresh_count; +}; + +typedef enum +{ + OPERATION_TYPE_REFRESH, + OPERATION_TYPE_LIST, + OPERATION_TYPE_RENEW, + OPERATION_TYPE_SIGN_IN, + OPERATION_TYPE_SIGN_OUT, + OPERATION_TYPE_STOP_JOB +} OperationType; + +typedef struct +{ + GCancellable *cancellable; + GsdKerberosIdentityManager *manager; + OperationType type; + GSimpleAsyncResult *result; + GIOSchedulerJob *job; + union { + GsdIdentity *identity; + struct { + const char *identifier; + GsdIdentityInquiry *inquiry; + GsdIdentityInquiryFunc inquiry_func; + gpointer inquiry_data; + GMutex inquiry_lock; + GCond inquiry_finished_condition; + volatile gboolean is_inquiring; + }; + }; +} Operation; + +typedef struct +{ + GsdKerberosIdentityManager *manager; + GsdIdentity *identity; +} IdentitySignalWork; + +static void identity_manager_interface_init (GsdIdentityManagerInterface *interface); +static void initable_interface_init (GInitableIface *interface); + +static void on_identity_expired (GsdIdentity *identity, + GsdKerberosIdentityManager *self); + +void gsd_kerberos_identity_manager_start_test (GsdKerberosIdentityManager *manager, + GError **error); +void gsd_kerberos_identity_manager_stop_test (GsdKerberosIdentityManager *manager, + GError **error); + +G_DEFINE_TYPE_WITH_CODE (GsdKerberosIdentityManager, + gsd_kerberos_identity_manager, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GSD_TYPE_IDENTITY_MANAGER, + identity_manager_interface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_interface_init)); + +static Operation * +operation_new (GsdKerberosIdentityManager *self, + GCancellable *cancellable, + OperationType type, + GSimpleAsyncResult *result) +{ + Operation *operation; + + operation = g_slice_new (Operation); + + operation->manager = self; + operation->type = type; + + if (cancellable == NULL) { + cancellable = g_cancellable_new (); + } else { + g_object_ref (cancellable); + } + operation->cancellable = cancellable; + + if (result != NULL) { + g_object_ref (result); + } + operation->result = result; + + operation->identity = NULL; + + return operation; +} + +static void +operation_free (Operation *operation) +{ + g_clear_object (&operation->cancellable); + + if (operation->type != OPERATION_TYPE_SIGN_IN) { + g_clear_object (&operation->identity); + } else { + g_clear_pointer (&operation->identifier, g_free); + } + + g_clear_object (&operation->result); + + g_slice_free (Operation, operation); +} + +static void +schedule_refresh (GsdKerberosIdentityManager *self) +{ + Operation *operation; + + g_atomic_int_inc (&self->priv->pending_refresh_count); + + operation = operation_new (self, NULL, OPERATION_TYPE_REFRESH, NULL); + g_async_queue_push (self->priv->pending_operations, operation); +} + +static IdentitySignalWork * +identity_signal_work_new (GsdKerberosIdentityManager *self, + GsdIdentity *identity) +{ + IdentitySignalWork *work; + + work = g_slice_new (IdentitySignalWork); + work->manager = self; + work->identity = g_object_ref (identity); + + return work; +} + +static void +identity_signal_work_free (IdentitySignalWork *work) +{ + g_object_unref (work->identity); + g_slice_free (IdentitySignalWork, work); +} + +static void +on_identity_expired (GsdIdentity *identity, + GsdKerberosIdentityManager *self) +{ + _gsd_identity_manager_emit_identity_expired (GSD_IDENTITY_MANAGER (self), identity); +} + +static void +on_identity_unexpired (GsdIdentity *identity, + GsdKerberosIdentityManager *self) +{ + g_debug ("GsdKerberosIdentityManager: identity unexpired"); + /* If an identity is now unexpired, that means some sort of weird + * clock skew happened and we should just do a full refresh, since it's + * probably affected more than one identity + */ + schedule_refresh (self); +} + +static void +on_identity_expiring (GsdIdentity *identity, + GsdKerberosIdentityManager *self) +{ + g_debug ("GsdKerberosIdentityManager: identity about to expire"); + _gsd_identity_manager_emit_identity_expiring (GSD_IDENTITY_MANAGER (self), identity); +} + +static void +on_identity_needs_renewal (GsdIdentity *identity, + GsdKerberosIdentityManager *self) +{ + g_debug ("GsdKerberosIdentityManager: identity needs renewal"); + _gsd_identity_manager_emit_identity_needs_renewal (GSD_IDENTITY_MANAGER (self), identity); +} + +static void +on_identity_needs_refresh (GsdIdentity *identity, + GsdKerberosIdentityManager *self) +{ + g_debug ("GsdKerberosIdentityManager: needs refresh"); + schedule_refresh (self); +} + +static void +watch_for_identity_expiration (GsdKerberosIdentityManager *self, + GsdIdentity *identity) +{ + g_signal_handlers_disconnect_by_func (G_OBJECT (identity), + G_CALLBACK (on_identity_expired), + self); + g_signal_connect (G_OBJECT (identity), + "expired", + G_CALLBACK (on_identity_expired), + self); + + g_signal_handlers_disconnect_by_func (G_OBJECT (identity), + G_CALLBACK (on_identity_unexpired), + self); + g_signal_connect (G_OBJECT (identity), + "unexpired", + G_CALLBACK (on_identity_unexpired), + self); + + g_signal_handlers_disconnect_by_func (G_OBJECT (identity), + G_CALLBACK (on_identity_expiring), + self); + g_signal_connect (G_OBJECT (identity), + "expiring", + G_CALLBACK (on_identity_expiring), + self); + + g_signal_handlers_disconnect_by_func (G_OBJECT (identity), + G_CALLBACK (on_identity_needs_renewal), + self); + g_signal_connect (G_OBJECT (identity), + "needs-renewal", + G_CALLBACK (on_identity_needs_renewal), + self); + + g_signal_handlers_disconnect_by_func (G_OBJECT (identity), + G_CALLBACK (on_identity_needs_refresh), + self); + g_signal_connect (G_OBJECT (identity), + "needs-refresh", + G_CALLBACK (on_identity_needs_refresh), + self); +} + +static void +do_identity_signal_added_work (IdentitySignalWork *work) +{ + GsdKerberosIdentityManager *self = work->manager; + GsdIdentity *identity = work->identity; + + watch_for_identity_expiration (self, identity); + _gsd_identity_manager_emit_identity_added (GSD_IDENTITY_MANAGER (self), identity); +} + +static void +do_identity_signal_removed_work (IdentitySignalWork *work) +{ + GsdKerberosIdentityManager *self = work->manager; + GsdIdentity *identity = work->identity; + + _gsd_identity_manager_emit_identity_removed (GSD_IDENTITY_MANAGER (self), identity); +} + +static void +do_identity_signal_renamed_work (IdentitySignalWork *work) +{ + GsdKerberosIdentityManager *self = work->manager; + GsdIdentity *identity = work->identity; + + _gsd_identity_manager_emit_identity_renamed (GSD_IDENTITY_MANAGER (self), identity); +} + +static void +do_identity_signal_refreshed_work (IdentitySignalWork *work) +{ + GsdKerberosIdentityManager *self = work->manager; + GsdIdentity *identity = work->identity; + + watch_for_identity_expiration (self, identity); + _gsd_identity_manager_emit_identity_refreshed (GSD_IDENTITY_MANAGER (self), identity); +} + +static void +remove_identity (GsdKerberosIdentityManager *self, + Operation *operation, + GsdIdentity *identity) +{ + + IdentitySignalWork *work; + const char *identifier; + char *name; + GList *other_identities = NULL; + + identifier = gsd_identity_get_identifier (identity); + name = gsd_kerberos_identity_get_realm_name (GSD_KERBEROS_IDENTITY (identity)); + + if (name != NULL) { + other_identities = g_hash_table_lookup (self->priv->identities_by_realm, + name); + g_hash_table_remove (self->priv->identities_by_realm, name); + + other_identities = g_list_remove (other_identities, identity); + } + + + if (other_identities != NULL) { + g_hash_table_replace (self->priv->identities_by_realm, + g_strdup (name), + other_identities); + } + g_free (name); + + work = identity_signal_work_new (self, identity); + g_hash_table_remove (self->priv->expired_identities, + identifier); + g_hash_table_remove (self->priv->identities, + identifier); + + g_io_scheduler_job_send_to_mainloop (operation->job, + (GSourceFunc) + do_identity_signal_removed_work, + work, + (GDestroyNotify) + identity_signal_work_free); + /* If there's only one identity for this realm now, then we can + * rename that identity to just the realm name + */ + if (other_identities != NULL && other_identities->next == NULL) { + GsdIdentity *other_identity = other_identities->data; + + work = identity_signal_work_new (self, other_identity); + + g_io_scheduler_job_send_to_mainloop (operation->job, + (GSourceFunc) + do_identity_signal_renamed_work, + work, + (GDestroyNotify) + identity_signal_work_free); + } +} + +static void +drop_stale_identities (GsdKerberosIdentityManager *self, + Operation *operation, + GHashTable *known_identities) +{ + GList *stale_identity_ids; + GList *node; + + stale_identity_ids = g_hash_table_get_keys (self->priv->identities); + + node = stale_identity_ids; + while (node != NULL) { + GsdIdentity *identity; + const char *identifier = node->data; + + identity = g_hash_table_lookup (known_identities, identifier); + if (identity == NULL) { + identity = g_hash_table_lookup (self->priv->identities, + identifier); + + if (identity != NULL) { + remove_identity (self, operation, identity); + } + } + node = node->next; + } + g_list_free (stale_identity_ids); +} + +static void +update_identity (GsdKerberosIdentityManager *self, + Operation *operation, + GsdIdentity *identity, + GsdIdentity *new_identity) +{ + + gsd_kerberos_identity_update (GSD_KERBEROS_IDENTITY (identity), + GSD_KERBEROS_IDENTITY (new_identity)); + + if (gsd_identity_is_signed_in (identity)) { + IdentitySignalWork *work; + + /* if it's not expired, send out a refresh signal */ + g_debug ("GsdKerberosIdentityManager: identity '%s' refreshed", + gsd_identity_get_identifier (identity)); + + work = identity_signal_work_new (self, identity); + g_io_scheduler_job_send_to_mainloop (operation->job, + (GSourceFunc) + do_identity_signal_refreshed_work, + work, + (GDestroyNotify) + identity_signal_work_free); + } +} + +static void +add_identity (GsdKerberosIdentityManager *self, + Operation *operation, + GsdIdentity *identity, + const char *identifier) +{ + IdentitySignalWork *work; + + g_hash_table_replace (self->priv->identities, + g_strdup (identifier), + g_object_ref (identity)); + + if (!gsd_identity_is_signed_in (identity)) { + g_hash_table_replace (self->priv->expired_identities, + g_strdup (identifier), + identity); + } + + work = identity_signal_work_new (self, identity); + g_io_scheduler_job_send_to_mainloop (operation->job, + (GSourceFunc) + do_identity_signal_added_work, + work, + (GDestroyNotify) + identity_signal_work_free); +} + +static void +refresh_identity (GsdKerberosIdentityManager *self, + Operation *operation, + GHashTable *refreshed_identities, + GsdIdentity *identity) +{ + const char *identifier; + GsdIdentity *old_identity; + + identifier = gsd_identity_get_identifier (identity); + + if (identifier == NULL) { + return; + } + old_identity = g_hash_table_lookup (self->priv->identities, identifier); + + if (old_identity != NULL) { + g_debug ("GsdKerberosIdentityManager: refreshing identity '%s'", identifier); + update_identity (self, operation, old_identity, identity); + + /* Reuse the old identity, so any object data set up on it doesn't + * disappear spurriously + */ + identifier = gsd_identity_get_identifier (old_identity); + identity = old_identity; + } else { + g_debug ("GsdKerberosIdentityManager: adding new identity '%s'", identifier); + add_identity (self, operation, identity, identifier); + } + + /* Track refreshed identities so we can emit removals when we're done fully + * enumerating the collection of credential caches + */ + g_hash_table_replace (refreshed_identities, + g_strdup (identifier), + g_object_ref (identity)); +} + +static gboolean +refresh_identities (GsdKerberosIdentityManager *self, + Operation *operation) +{ + krb5_error_code error_code; + krb5_ccache cache; + krb5_cccol_cursor cursor; + const char *error_message; + GHashTable *refreshed_identities; + + /* If we have more refreshes queued up, don't bother doing this one + */ + if (!g_atomic_int_dec_and_test (&self->priv->pending_refresh_count)) { + return FALSE; + } + + g_debug ("GsdKerberosIdentityManager: Refreshing identities"); + refreshed_identities = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) + g_free, + (GDestroyNotify) + g_object_unref); + error_code = krb5_cccol_cursor_new (self->priv->kerberos_context, &cursor); + + if (error_code != 0) { + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentityManager: Error looking up available credential caches: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + goto done; + } + + error_code = krb5_cccol_cursor_next (self->priv->kerberos_context, + cursor, + &cache); + + while (error_code == 0 && cache != NULL) { + GsdIdentity *identity; + + identity = gsd_kerberos_identity_new (self->priv->kerberos_context, + cache); + + if (identity != NULL) { + refresh_identity (self, operation, refreshed_identities, identity); + } + + krb5_cc_close (self->priv->kerberos_context, cache); + error_code = krb5_cccol_cursor_next (self->priv->kerberos_context, + cursor, + &cache); + } + + if (error_code != 0) { + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentityManager: Error iterating over available credential caches: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + } + + krb5_cccol_cursor_free (self->priv->kerberos_context, &cursor); +done: + drop_stale_identities (self, operation, refreshed_identities); + g_hash_table_unref (refreshed_identities); + + return TRUE; +} + +static int +identity_sort_func (GsdIdentity *a, + GsdIdentity *b) +{ + return g_strcmp0 (gsd_identity_get_identifier (a), + gsd_identity_get_identifier (b)); +} + +static void +free_identity_list (GList *list) +{ + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); +} + +static void +list_identities (GsdKerberosIdentityManager *self, + Operation *operation) +{ + GList *identities; + + g_debug ("GsdKerberosIdentityManager: Listing identities"); + identities = g_hash_table_get_values (self->priv->identities); + + identities = g_list_sort (identities, + (GCompareFunc) + identity_sort_func); + + g_list_foreach (identities, (GFunc) g_object_ref, NULL); + g_simple_async_result_set_op_res_gpointer (operation->result, + identities, + (GDestroyNotify) + free_identity_list); +} + +static void +renew_identity (GsdKerberosIdentityManager *self, + Operation *operation) +{ + GError *error; + gboolean was_renewed; + char *identity_name; + + identity_name = gsd_kerberos_identity_get_principal_name (GSD_KERBEROS_IDENTITY (operation->identity)); + g_debug ("GsdKerberosIdentityManager: renewing identity %s", identity_name); + g_free (identity_name); + + error = NULL; + was_renewed = gsd_kerberos_identity_renew (GSD_KERBEROS_IDENTITY (operation->identity), + &error); + + if (!was_renewed) { + g_debug ("GsdKerberosIdentityManager: could not renew identity: %s", + error->message); + + g_simple_async_result_set_from_error (operation->result, + error); + } + + g_simple_async_result_set_op_res_gboolean (operation->result, + was_renewed); +} + +static void +do_identity_inquiry (Operation *operation) +{ + if (operation->inquiry_func == NULL) { + return; + } + + operation->inquiry_func (operation->inquiry, + operation->cancellable, + operation->inquiry_data); +} + +static void +stop_waiting_on_inquiry (Operation *operation) +{ + g_mutex_lock (&operation->inquiry_lock); + if (operation->is_inquiring) { + operation->is_inquiring = FALSE; + g_cond_signal (&operation->inquiry_finished_condition); + } + g_mutex_unlock (&operation->inquiry_lock); +} + +static void +on_kerberos_identity_inquiry_complete (GsdIdentityInquiry *inquiry, + Operation *operation) +{ + stop_waiting_on_inquiry (operation); +} + +static void +start_inquiry (Operation *operation, + GsdIdentityInquiry *inquiry) +{ + operation->is_inquiring = TRUE; + + g_signal_connect (G_OBJECT (inquiry), + "complete", + G_CALLBACK (on_kerberos_identity_inquiry_complete), + operation); + + operation->inquiry = inquiry; + g_io_scheduler_job_send_to_mainloop (operation->job, + (GSourceFunc) + do_identity_inquiry, + operation, + (GDestroyNotify) + NULL); +} + +static void +wait_for_inquiry_to_complete (Operation *operation, + GsdKerberosIdentityInquiry *inquiry) +{ + g_mutex_lock (&operation->inquiry_lock); + while (operation->is_inquiring) { + g_cond_wait (&operation->inquiry_finished_condition, + &operation->inquiry_lock); + } + g_mutex_unlock (&operation->inquiry_lock); +} + +static void +on_sign_in_operation_cancelled (GCancellable *cancellable, + Operation *operation) +{ + stop_waiting_on_inquiry (operation); +} + +static void +on_kerberos_identity_inquiry (GsdKerberosIdentityInquiry *inquiry, + GCancellable *cancellable, + Operation *operation) +{ + gulong handler_id; + + start_inquiry (operation, GSD_IDENTITY_INQUIRY (inquiry)); + + handler_id = g_cancellable_connect (cancellable, + G_CALLBACK (on_sign_in_operation_cancelled), + operation, + NULL); + + if ((operation->cancellable == NULL) || + !g_cancellable_is_cancelled (operation->cancellable)) { + wait_for_inquiry_to_complete (operation, inquiry); + } + + g_cancellable_disconnect (cancellable, handler_id); +} + +static void +sign_in_identity (GsdKerberosIdentityManager *self, + Operation *operation) +{ + GsdIdentity *identity; + GError *error; + krb5_error_code error_code; + + g_debug ("GsdKerberosIdentityManager: signing in identity %s", operation->identifier); + identity = g_hash_table_lookup (self->priv->identities, + operation->identifier); + if (identity == NULL) { + krb5_ccache credentials_cache; + error_code = krb5_cc_new_unique (self->priv->kerberos_context, + self->priv->credentials_cache_type, + NULL, + &credentials_cache); + + if (error_code != 0) { + const char *error_message; + + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentityManager: Error creating new cache for identity credentials: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + } else { + identity = gsd_kerberos_identity_new (self->priv->kerberos_context, + credentials_cache); + if (identity == NULL) { + krb5_cc_close (self->priv->kerberos_context, credentials_cache); + } + } + } + + if (identity == NULL) { + g_simple_async_result_set_error (operation->result, + GSD_IDENTITY_MANAGER_ERROR, + GSD_IDENTITY_MANAGER_ERROR_SIGNING_IN, + _("Could not create identity")); + g_simple_async_result_set_op_res_gpointer (operation->result, + NULL, NULL); + + return; + } + + g_hash_table_replace (self->priv->identities, + g_strdup (operation->identifier), + g_object_ref (identity)); + + error = NULL; + if (!gsd_kerberos_identity_sign_in (GSD_KERBEROS_IDENTITY (identity), + operation->identifier, + (GsdIdentityInquiryFunc) + on_kerberos_identity_inquiry, + operation, + NULL, + operation->cancellable, + &error)) { + g_simple_async_result_set_from_error (operation->result, + error); + g_simple_async_result_set_op_res_gpointer (operation->result, + NULL, NULL); + + } else { + g_simple_async_result_set_op_res_gpointer (operation->result, + g_object_ref (identity), + (GDestroyNotify) + g_object_unref); + } +} + +static void +sign_out_identity (GsdKerberosIdentityManager *self, + Operation *operation) +{ + GError *error; + gboolean was_signed_out; + char *identity_name; + + identity_name = gsd_kerberos_identity_get_principal_name (GSD_KERBEROS_IDENTITY (operation->identity)); + g_debug ("GsdKerberosIdentityManager: signing out identity %s", identity_name); + g_free (identity_name); + + error = NULL; + was_signed_out = gsd_kerberos_identity_erase (GSD_KERBEROS_IDENTITY (operation->identity), + &error); + + if (!was_signed_out) { + g_debug ("GsdKerberosIdentityManager: could not sign out identity: %s", + error->message); + g_error_free (error); + } +} + +static void +block_scheduler_job (GsdKerberosIdentityManager *self) +{ + g_mutex_lock (&self->priv->scheduler_job_lock); + while (self->priv->is_blocking_scheduler_job) { + g_cond_wait (&self->priv->scheduler_job_unblocked, &self->priv->scheduler_job_lock); + } + self->priv->is_blocking_scheduler_job = TRUE; + g_mutex_unlock (&self->priv->scheduler_job_lock); +} + +static void +stop_blocking_scheduler_job (GsdKerberosIdentityManager *self) +{ + g_mutex_lock (&self->priv->scheduler_job_lock); + self->priv->is_blocking_scheduler_job = FALSE; + g_cond_signal (&self->priv->scheduler_job_unblocked); + g_mutex_unlock (&self->priv->scheduler_job_lock); +} + +static void +wait_for_scheduler_job_to_become_unblocked (GsdKerberosIdentityManager *self) +{ + g_mutex_lock (&self->priv->scheduler_job_lock); + while (self->priv->is_blocking_scheduler_job) { + g_cond_wait (&self->priv->scheduler_job_unblocked, &self->priv->scheduler_job_lock); + } + g_mutex_unlock (&self->priv->scheduler_job_lock); +} + +static void +on_job_cancelled (GCancellable *cancellable, + GsdKerberosIdentityManager *self) +{ + Operation *operation; + operation = operation_new (self, + cancellable, + OPERATION_TYPE_STOP_JOB, + NULL); + g_async_queue_push (self->priv->pending_operations, operation); + + stop_blocking_scheduler_job (self); +} + +static gboolean +on_job_scheduled (GIOSchedulerJob *job, + GCancellable *cancellable, + GsdKerberosIdentityManager *self) +{ + GAsyncQueue *pending_operations; + + g_assert (cancellable != NULL); + + g_cancellable_connect (cancellable, + G_CALLBACK (on_job_cancelled), + self, + NULL); + + /* Take ownership of queue, since we may out live the identity manager */ + pending_operations = g_async_queue_ref (self->priv->pending_operations); + while (!g_cancellable_is_cancelled (cancellable)) { + Operation *operation; + gboolean processed_operation; + GError *error = NULL; + + operation = g_async_queue_pop (pending_operations); + + if (operation->result != NULL && + g_cancellable_set_error_if_cancelled (operation->cancellable, &error)) { + g_simple_async_result_take_error (operation->result, + error); + g_simple_async_result_complete_in_idle (operation->result); + g_object_unref (operation->result); + operation->result = NULL; + continue; + } + + operation->job = job; + + switch (operation->type) { + case OPERATION_TYPE_STOP_JOB: + /* do nothing, loop will exit next iteration since cancellable + * is cancelled + */ + g_assert (g_cancellable_is_cancelled (cancellable)); + operation_free (operation); + continue; + case OPERATION_TYPE_REFRESH: + processed_operation = refresh_identities (operation->manager, operation); + break; + case OPERATION_TYPE_LIST: + list_identities (operation->manager, operation); + processed_operation = TRUE; + + /* We want to block refreshes (and their associated "added" + * and "removed" signals) until the caller has had + * a chance to look at the batch of + * results we already processed + */ + g_assert (operation->result != NULL); + + g_debug ("GsdKerberosIdentityManager: Blocking until identities list processed"); + block_scheduler_job (self); + g_object_weak_ref (G_OBJECT (operation->result), + (GWeakNotify) + stop_blocking_scheduler_job, + self); + g_debug ("GsdKerberosIdentityManager: Continuing"); + break; + case OPERATION_TYPE_SIGN_IN: + sign_in_identity (operation->manager, operation); + processed_operation = TRUE; + break; + case OPERATION_TYPE_SIGN_OUT: + sign_out_identity (operation->manager, operation); + processed_operation = TRUE; + break; + case OPERATION_TYPE_RENEW: + renew_identity (operation->manager, operation); + processed_operation = TRUE; + break; + } + + operation->job = NULL; + + if (operation->result != NULL) { + g_simple_async_result_complete_in_idle (operation->result); + g_object_unref (operation->result); + operation->result = NULL; + } + operation_free (operation); + + wait_for_scheduler_job_to_become_unblocked (self); + + /* Don't bother saying "Waiting for next operation" if this operation + * was a no-op, since the debug spew probably already says the message + */ + if (processed_operation) { + g_debug ("GsdKerberosIdentityManager: Waiting for next operation"); + } + } + + g_async_queue_unref (pending_operations); + + return FALSE; +} + +static void +gsd_kerberos_identity_manager_list_identities (GsdIdentityManager *manager, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (manager); + GSimpleAsyncResult *result; + Operation *operation; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + gsd_kerberos_identity_manager_list_identities); + + operation = operation_new (self, + cancellable, + OPERATION_TYPE_LIST, + result); + g_object_unref (result); + + g_async_queue_push (self->priv->pending_operations, operation); +} + +static GList * +gsd_kerberos_identity_manager_list_identities_finish (GsdIdentityManager *manager, + GAsyncResult *result, + GError **error) +{ + GList *identities; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) { + return NULL; + } + + identities = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); + + return identities; + +} + +static void +gsd_kerberos_identity_manager_renew_identity (GsdIdentityManager *manager, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (manager); + GSimpleAsyncResult *result; + Operation *operation; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + gsd_kerberos_identity_manager_renew_identity); + operation = operation_new (self, + cancellable, + OPERATION_TYPE_RENEW, + result); + g_object_unref (result); + + operation->identity = g_object_ref (identity); + + g_async_queue_push (self->priv->pending_operations, operation); +} + +static void +gsd_kerberos_identity_manager_renew_identity_finish (GsdIdentityManager *self, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) { + return; + } + + return; +} + +static void +gsd_kerberos_identity_manager_sign_identity_in (GsdIdentityManager *manager, + const char *identifier, + GsdIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (manager); + GSimpleAsyncResult *result; + Operation *operation; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + gsd_kerberos_identity_manager_sign_identity_in); + operation = operation_new (self, + cancellable, + OPERATION_TYPE_SIGN_IN, + result); + g_object_unref (result); + + operation->identifier = g_strdup (identifier); + operation->inquiry_func = inquiry_func; + operation->inquiry_data = inquiry_data; + g_mutex_init (&operation->inquiry_lock); + g_cond_init (&operation->inquiry_finished_condition); + operation->is_inquiring = FALSE; + + g_async_queue_push (self->priv->pending_operations, operation); +} + +static GsdIdentity * +gsd_kerberos_identity_manager_sign_identity_in_finish (GsdIdentityManager *self, + GAsyncResult *result, + GError **error) +{ + GsdIdentity *identity; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) { + return NULL; + } + + identity = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); + + return identity; +} + +static void +gsd_kerberos_identity_manager_sign_identity_out (GsdIdentityManager *manager, + GsdIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (manager); + GSimpleAsyncResult *result; + Operation *operation; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + gsd_kerberos_identity_manager_sign_identity_out); + operation = operation_new (self, + cancellable, + OPERATION_TYPE_SIGN_OUT, + result); + g_object_unref (result); + + operation->identity = g_object_ref (identity); + + g_async_queue_push (self->priv->pending_operations, operation); +} + +static void +gsd_kerberos_identity_manager_sign_identity_out_finish (GsdIdentityManager *self, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) { + return; + } + + return; +} + +static char * +gsd_kerberos_identity_manager_name_identity (GsdIdentityManager *manager, + GsdIdentity *identity) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (manager); + char *name; + GList *other_identities; + gboolean other_identity_needs_rename; + + name = gsd_kerberos_identity_get_realm_name (GSD_KERBEROS_IDENTITY (identity)); + + if (name == NULL) { + return NULL; + } + + other_identities = g_hash_table_lookup (self->priv->identities_by_realm, + name); + + /* If there was already exactly one identity for this realm before, + * then it was going by just the realm name, so we need to rename it + * to use the full principle name + */ + if (other_identities != NULL && + other_identities->next == NULL && + other_identities->data != identity) { + other_identity_needs_rename = TRUE; + } + + other_identities = g_list_remove (other_identities, identity); + other_identities = g_list_prepend (other_identities, identity); + + g_hash_table_replace (self->priv->identities_by_realm, + g_strdup (name), + other_identities); + + if (other_identities->next != NULL) { + g_free (name); + name = gsd_kerberos_identity_get_principal_name (GSD_KERBEROS_IDENTITY (identity)); + if (other_identity_needs_rename) { + GsdIdentity *other_identity = other_identities->next->data; + + _gsd_identity_manager_emit_identity_renamed (GSD_IDENTITY_MANAGER (self), + other_identity); + } + } + + return name; +} + +static void +identity_manager_interface_init (GsdIdentityManagerInterface *interface) +{ + interface->sign_identity_in = gsd_kerberos_identity_manager_sign_identity_in; + interface->sign_identity_in_finish = gsd_kerberos_identity_manager_sign_identity_in_finish; + interface->sign_identity_out = gsd_kerberos_identity_manager_sign_identity_out; + interface->sign_identity_out_finish = gsd_kerberos_identity_manager_sign_identity_out_finish; + interface->renew_identity = gsd_kerberos_identity_manager_renew_identity; + interface->renew_identity_finish = gsd_kerberos_identity_manager_renew_identity_finish; + interface->list_identities = gsd_kerberos_identity_manager_list_identities; + interface->list_identities_finish = gsd_kerberos_identity_manager_list_identities_finish; + interface->name_identity = gsd_kerberos_identity_manager_name_identity; +} + +static void +on_credentials_cache_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent *event_type, + GsdKerberosIdentityManager *self) +{ + schedule_refresh (self); +} + +static gboolean +monitor_credentials_cache (GsdKerberosIdentityManager *self, + GError **error) +{ + krb5_ccache default_cache; + const char *cache_type; + const char *cache_path; + GFile *file; + GFileMonitor *monitor; + krb5_error_code error_code; + GError *monitoring_error; + + error_code = krb5_cc_default (self->priv->kerberos_context, + &default_cache); + + if (error_code != 0) { + const char *error_message; + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + + g_set_error_literal (error, + GSD_IDENTITY_MANAGER_ERROR, + GSD_IDENTITY_MANAGER_ERROR_MONITORING, + error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + + return FALSE; + } + + cache_type = krb5_cc_get_type (self->priv->kerberos_context, + default_cache); + g_assert (cache_type != NULL); + + if (strcmp (cache_type, "FILE") != 0 && + strcmp (cache_type, "DIR") != 0) { + g_set_error (error, + GSD_IDENTITY_MANAGER_ERROR, + GSD_IDENTITY_MANAGER_ERROR_MONITORING, + "Only 'FILE' and 'DIR' credential cache types are really supported, not '%s'", + cache_type); + return FALSE; + } + + g_free (self->priv->credentials_cache_type); + self->priv->credentials_cache_type = g_strdup (cache_type); + + /* If we're using a FILE type credential cache, then the + * default cache file is the only cache we care about, + * and its path is what we want to monitor. + * + * If we're using a DIR type credential cache, then the default + * cache file is one of many possible cache files, all in the + * same directory. We want to monitor that directory. + */ + cache_path = krb5_cc_get_name (self->priv->kerberos_context, + default_cache); + + /* The cache name might have a : in front of it. + * FIXME: figure out if that behavior is by design, or some + * odd bug. + */ + if (cache_path[0] == ':') { + cache_path++; + } + + file = g_file_new_for_path (cache_path); + + monitoring_error = NULL; + if (strcmp (cache_type, "FILE") == 0) { + monitor = g_file_monitor_file (file, + G_FILE_MONITOR_NONE, + NULL, + &monitoring_error); + } else if (strcmp (cache_type, "DIR") == 0) { + GFile *directory; + + directory = g_file_get_parent (file); + monitor = g_file_monitor_directory (directory, + G_FILE_MONITOR_NONE, + NULL, + &monitoring_error); + g_object_unref (directory); + + } else { + g_assert_not_reached (); + } + g_object_unref (file); + + if (monitor == NULL) { + g_propagate_error (error, monitoring_error); + return FALSE; + } + + self->priv->credentials_cache_changed_signal_id = g_signal_connect (G_OBJECT (monitor), + "changed", + G_CALLBACK (on_credentials_cache_changed), + self); + self->priv->credentials_cache_monitor = monitor; + + return TRUE; +} + +static void +stop_watching_credentials_cache (GsdKerberosIdentityManager *self) +{ + if (!g_file_monitor_is_cancelled (self->priv->credentials_cache_monitor)) { + g_file_monitor_cancel (self->priv->credentials_cache_monitor); + } + g_object_unref (self->priv->credentials_cache_monitor); + self->priv->credentials_cache_monitor = NULL; +} + +static gboolean +gsd_kerberos_identity_manager_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (initable); + krb5_error_code error_code; + GError *monitoring_error; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + return FALSE; + } + + error_code = krb5_init_context (&self->priv->kerberos_context); + + if (error_code != 0) { + const char *error_message; + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + + g_set_error_literal (error, + GSD_IDENTITY_MANAGER_ERROR, + GSD_IDENTITY_MANAGER_ERROR_INITIALIZING, + error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + + return FALSE; + } + + monitoring_error = NULL; + if (!monitor_credentials_cache (self, &monitoring_error)) { + g_warning ("GsdKerberosIdentityManager: Could not monitor credentials: %s", + monitoring_error->message); + g_error_free (monitoring_error); + } + + schedule_refresh (self); + + return TRUE; +} + +static void +initable_interface_init (GInitableIface *interface) +{ + interface->init = gsd_kerberos_identity_manager_initable_init; +} + +static void +gsd_kerberos_identity_manager_init (GsdKerberosIdentityManager *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GSD_TYPE_KERBEROS_IDENTITY_MANAGER, + GsdKerberosIdentityManagerPrivate); + self->priv->identities = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) + g_free, + (GDestroyNotify) + g_object_unref); + self->priv->expired_identities = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) + g_free, + NULL); + + self->priv->identities_by_realm = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) + g_free, + NULL); + self->priv->pending_operations = g_async_queue_new (); + + g_mutex_init (&self->priv->scheduler_job_lock); + g_cond_init (&self->priv->scheduler_job_unblocked); + + self->priv->scheduler_cancellable = g_cancellable_new (); + g_io_scheduler_push_job ((GIOSchedulerJobFunc) + on_job_scheduled, + self, + NULL, + G_PRIORITY_DEFAULT, + self->priv->scheduler_cancellable); + +} + +static void +cancel_pending_operations (GsdKerberosIdentityManager *self) +{ + Operation *operation; + + operation = g_async_queue_try_pop (self->priv->pending_operations); + while (operation != NULL) { + if (!g_cancellable_is_cancelled (operation->cancellable)) { + g_cancellable_cancel (operation->cancellable); + } + operation_free (operation); + operation = g_async_queue_try_pop (self->priv->pending_operations); + } +} + +static void +gsd_kerberos_identity_manager_dispose (GObject *object) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (object); + + if (self->priv->identities_by_realm != NULL) { + g_hash_table_unref (self->priv->identities_by_realm); + self->priv->identities_by_realm = NULL; + } + + if (self->priv->expired_identities != NULL) { + g_hash_table_unref (self->priv->expired_identities); + self->priv->expired_identities = NULL; + } + + if (self->priv->identities != NULL) { + g_hash_table_unref (self->priv->identities); + self->priv->identities = NULL; + } + + if (self->priv->credentials_cache_monitor != NULL) { + stop_watching_credentials_cache (self); + } + + if (self->priv->pending_operations != NULL) { + cancel_pending_operations (self); + } + + if (self->priv->scheduler_cancellable != NULL) { + if (!g_cancellable_is_cancelled (self->priv->scheduler_cancellable)) { + g_cancellable_cancel (self->priv->scheduler_cancellable); + } + + g_clear_object (&self->priv->scheduler_cancellable); + } + + /* Note, other thread may still be holding a local reference to queue + * while it shuts down from cancelled scheduler_cancellable above + */ + if (self->priv->pending_operations != NULL) { + g_async_queue_unref (self->priv->pending_operations); + self->priv->pending_operations = NULL; + } + + G_OBJECT_CLASS (gsd_kerberos_identity_manager_parent_class)->dispose (object); +} + +static void +gsd_kerberos_identity_manager_finalize (GObject *object) +{ + GsdKerberosIdentityManager *self = GSD_KERBEROS_IDENTITY_MANAGER (object); + + g_free (self->priv->credentials_cache_type); + + g_cond_clear (&self->priv->scheduler_job_unblocked); + krb5_free_context (self->priv->kerberos_context); + + G_OBJECT_CLASS (gsd_kerberos_identity_manager_parent_class)->finalize (object); +} + +static void +gsd_kerberos_identity_manager_class_init (GsdKerberosIdentityManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gsd_kerberos_identity_manager_dispose; + object_class->finalize = gsd_kerberos_identity_manager_finalize; + + g_type_class_add_private (klass, sizeof (GsdKerberosIdentityManagerPrivate)); +} + +GsdIdentityManager * +gsd_kerberos_identity_manager_new (void) +{ + GObject *object; + GError *error; + object = g_object_new (GSD_TYPE_KERBEROS_IDENTITY_MANAGER, NULL); + + error = NULL; + if (!g_initable_init (G_INITABLE (object), NULL, &error)) { + g_warning ("Could not create kerberos identity manager: %s", + error->message); + g_error_free (error); + g_object_unref (object); + return NULL; + } + + return GSD_IDENTITY_MANAGER (object); +} + +static void +test_on_identity_renewed (GsdIdentityManager *manager, + GAsyncResult *result) +{ + GError *error; + + error = NULL; + gsd_identity_manager_renew_identity_finish (manager, + result, + &error); + + if (error != NULL) { + g_warning ("Could not renew identity: %s", + error->message); + g_error_free (error); + return; + } + + g_message ("identity renewed"); +} + +static void +test_on_identity_needs_renewal (GsdIdentityManager *identity_manager, + GsdIdentity *identity) +{ + g_message ("identity needs renewal"); + gsd_identity_manager_renew_identity (identity_manager, + identity, + NULL, + (GAsyncReadyCallback) + test_on_identity_renewed, + NULL); +} + +static void +test_on_identity_inquiry (GsdIdentityInquiry *inquiry, + GCancellable *cancellable) +{ + GsdIdentityInquiryIter iter; + GsdIdentityQuery *query; + char *name, *banner; + int fd; + + name = gsd_identity_inquiry_get_name (inquiry); + g_message ("name: %s", name); + g_free (name); + + banner = gsd_identity_inquiry_get_banner (inquiry); + g_message ("banner: %s", banner); + g_free (banner); + + fd = g_open ("/dev/tty", O_RDWR); + gsd_identity_inquiry_iter_init (&iter, inquiry); + while ((query = gsd_identity_inquiry_iter_next (&iter, inquiry)) != NULL) { + char *prompt; + char answer[256] = ""; + ssize_t bytes_read; + + prompt = gsd_identity_query_get_prompt (inquiry, query); + g_message ("prompt: %s", prompt); + g_free (prompt); + + bytes_read = read (fd, answer, sizeof (answer) - 1); + + if (bytes_read > 0) { + /* Trim off \n */ + answer[bytes_read - 1] = '\0'; + g_message ("using password '%s'", answer); + gsd_identity_inquiry_answer_query (inquiry, query, answer); + } + } + close (fd); +} + +static void +test_on_identity_signed_in (GsdIdentityManager *manager, + GAsyncResult *result, + const char *principal_name) +{ + GError *error; + + error = NULL; + gsd_identity_manager_sign_identity_in_finish (manager, + result, + &error); + + if (error != NULL) { + g_warning ("Could not sign-in identity %s: %s", + principal_name, error->message); + g_error_free (error); + return; + } + + g_message ("identity %s signed in", principal_name); +} + +static void +test_on_identity_expiring (GsdIdentityManager *identity_manager, + GsdIdentity *identity) +{ + const char *identifier; + + g_message ("identity about to expire"); + + identifier = gsd_identity_get_identifier (identity); + + gsd_kerberos_identity_manager_sign_identity_in (GSD_IDENTITY_MANAGER (identity_manager), + identifier, + (GsdIdentityInquiryFunc) + test_on_identity_inquiry, + NULL, + NULL, + (GAsyncReadyCallback) + test_on_identity_signed_in, + (gpointer) + identifier); +} + +static void +test_on_identity_expired (GsdIdentityManager *identity_manager, + GsdIdentity *identity) +{ + const char *identifier; + + g_message ("identity expired"); + + identifier = gsd_identity_get_identifier (identity); + + gsd_kerberos_identity_manager_sign_identity_in (GSD_IDENTITY_MANAGER (identity_manager), + identifier, + (GsdIdentityInquiryFunc) + test_on_identity_inquiry, + NULL, + NULL, + (GAsyncReadyCallback) + test_on_identity_signed_in, + (gpointer) + identifier); +} + +void +gsd_kerberos_identity_manager_start_test (GsdKerberosIdentityManager *manager, + GError **error) +{ + const char *principal_name; + + g_signal_connect (G_OBJECT (manager), + "identity-needs-renewal", + G_CALLBACK (test_on_identity_needs_renewal), + NULL); + g_signal_connect (G_OBJECT (manager), + "identity-expiring", + G_CALLBACK (test_on_identity_expiring), + NULL); + g_signal_connect (G_OBJECT (manager), + "identity-expired", + G_CALLBACK (test_on_identity_expired), + NULL); + + principal_name = g_getenv ("GSD_KERBEROS_IDENTITY_MANAGER_TEST_PRINCIPAL"); + if (principal_name != NULL) { + GCancellable *cancellable; + + cancellable = g_cancellable_new (); + + gsd_kerberos_identity_manager_sign_identity_in (GSD_IDENTITY_MANAGER (manager), + principal_name, + (GsdIdentityInquiryFunc) + test_on_identity_inquiry, + NULL, + cancellable, + (GAsyncReadyCallback) + test_on_identity_signed_in, + (gpointer) + principal_name); + g_object_set_data_full (G_OBJECT (manager), + "test-cancellable", + cancellable, + (GDestroyNotify) + g_object_unref); + } +} + +void +gsd_kerberos_identity_manager_stop_test (GsdKerberosIdentityManager *manager, + GError **error) +{ + GCancellable *cancellable; + + g_signal_handlers_disconnect_by_func (G_OBJECT (manager), + G_CALLBACK (test_on_identity_needs_renewal), + NULL); + g_signal_handlers_disconnect_by_func (G_OBJECT (manager), + G_CALLBACK (test_on_identity_expired), + NULL); + + cancellable = g_object_get_data (G_OBJECT (manager), "test-cancellable"); + + if (cancellable != NULL) { + if (!g_cancellable_is_cancelled (cancellable)) { + g_cancellable_cancel (cancellable); + } + + g_object_set_data (G_OBJECT (manager), "test-cancellable", NULL); + } +} diff --git a/panels/user-accounts/gsd-kerberos-identity-manager.h b/panels/user-accounts/gsd-kerberos-identity-manager.h new file mode 100644 index 000000000..67271ed3c --- /dev/null +++ b/panels/user-accounts/gsd-kerberos-identity-manager.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_KERBEROS_IDENTITY_MANAGER_H__ +#define __GSD_KERBEROS_IDENTITY_MANAGER_H__ + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include "gsd-identity-manager.h" + +G_BEGIN_DECLS + +#define GSD_TYPE_KERBEROS_IDENTITY_MANAGER (gsd_kerberos_identity_manager_get_type ()) +#define GSD_KERBEROS_IDENTITY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GSD_TYPE_KERBEROS_IDENTITY_MANAGER, GsdKerberosIdentityManager)) +#define GSD_KERBEROS_IDENTITY_MANAGER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GSD_TYPE_KERBEROS_IDENTITY_MANAGER, GsdKerberosIdentityManagerClass)) +#define GSD_IS_KERBEROS_IDENTITY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GSD_TYPE_KERBEROS_IDENTITY_MANAGER)) +#define GSD_IS_KERBEROS_IDENTITY_MANAGER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GSD_TYPE_KERBEROS_IDENTITY_MANAGER)) +#define GSD_KERBEROS_IDENTITY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSD_TYPE_KERBEROS_IDENTITY_MANAGER, GsdKerberosIdentityManagerClass)) + +typedef struct _GsdKerberosIdentityManager GsdKerberosIdentityManager; +typedef struct _GsdKerberosIdentityManagerClass GsdKerberosIdentityManagerClass; +typedef struct _GsdKerberosIdentityManagerPrivate GsdKerberosIdentityManagerPrivate; struct _GsdKerberosIdentityManager +{ + GObject parent_instance; + GsdKerberosIdentityManagerPrivate *priv; +}; + +struct _GsdKerberosIdentityManagerClass +{ + GObjectClass parent_class; +}; + +GType gsd_kerberos_identity_manager_get_type (void); +GsdIdentityManager* gsd_kerberos_identity_manager_new (void); + +void gsd_kerberos_identity_manager_start_test (GsdKerberosIdentityManager *manager, + GError **error); +G_END_DECLS + +#endif /* __GSD_KERBEROS_IDENTITY_MANAGER_H__ */ diff --git a/panels/user-accounts/gsd-kerberos-identity.c b/panels/user-accounts/gsd-kerberos-identity.c new file mode 100644 index 000000000..c8b2862ab --- /dev/null +++ b/panels/user-accounts/gsd-kerberos-identity.c @@ -0,0 +1,1199 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Ray Strode + */ + +#include "config.h" + +#include "gsd-identity.h" +#include "gsd-kerberos-identity.h" +#include "gsd-alarm.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +typedef enum +{ + VERIFICATION_LEVEL_UNVERIFIED, + VERIFICATION_LEVEL_ERROR, + VERIFICATION_LEVEL_EXISTS, + VERIFICATION_LEVEL_SIGNED_IN +} VerificationLevel; + +struct _GsdKerberosIdentityPrivate +{ + krb5_context kerberos_context; + krb5_ccache credentials_cache; + + char *identifier; + + krb5_timestamp expiration_time; + + GsdAlarm *expiration_alarm; + GCancellable *expiration_alarm_cancellable; + + GsdAlarm *expiring_alarm; + GCancellable *expiring_alarm_cancellable; + + GsdAlarm *renewal_alarm; + GCancellable *renewal_alarm_cancellable; + + VerificationLevel cached_verification_level; +}; + +enum { + EXPIRING, + EXPIRED, + UNEXPIRED, + NEEDS_RENEWAL, + NEEDS_REFRESH, + NUMBER_OF_SIGNALS, +}; + +static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + +static void identity_interface_init (GsdIdentityInterface *interface); +static void initable_interface_init (GInitableIface *interface); +static void reset_alarms (GsdKerberosIdentity *self); +static void clear_alarms (GsdKerberosIdentity *self); + +G_DEFINE_TYPE_WITH_CODE (GsdKerberosIdentity, + gsd_kerberos_identity, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_interface_init) + G_IMPLEMENT_INTERFACE (GSD_TYPE_IDENTITY, + identity_interface_init)); + +static void +gsd_kerberos_identity_dispose (GObject *object) +{ + GsdKerberosIdentity *self = GSD_KERBEROS_IDENTITY (object); + + clear_alarms (self); + + g_clear_object (&self->priv->renewal_alarm); + g_clear_object (&self->priv->expiring_alarm); + g_clear_object (&self->priv->expiration_alarm); +} + +static void +gsd_kerberos_identity_finalize (GObject *object) +{ + GsdKerberosIdentity *self = GSD_KERBEROS_IDENTITY (object); + + g_free (self->priv->identifier); + + if (self->priv->credentials_cache != NULL) { + krb5_cc_close (self->priv->kerberos_context, + self->priv->credentials_cache); + } + + G_OBJECT_CLASS (gsd_kerberos_identity_parent_class)->finalize (object); +} + +static void +gsd_kerberos_identity_class_init (GsdKerberosIdentityClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gsd_kerberos_identity_dispose; + object_class->finalize = gsd_kerberos_identity_finalize; + + g_type_class_add_private (klass, sizeof (GsdKerberosIdentityPrivate)); + + signals[EXPIRING] = g_signal_new ("expiring", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[EXPIRED] = g_signal_new ("expired", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[UNEXPIRED] = g_signal_new ("unexpired", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[NEEDS_RENEWAL] = g_signal_new ("needs-renewal", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[NEEDS_REFRESH] = g_signal_new ("needs-refresh", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static char * +get_identifier (GsdKerberosIdentity *self) +{ + krb5_principal principal; + krb5_error_code error_code; + char *unparsed_name; + char *identifier; + + if (self->priv->credentials_cache == NULL) { + return NULL; + } + + error_code = krb5_cc_get_principal (self->priv->kerberos_context, + self->priv->credentials_cache, + &principal); + + if (error_code != 0) { + const char *error_message; + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentity: Error looking up principal identity in credential cache: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + return NULL; + } + + error_code = krb5_unparse_name_flags (self->priv->kerberos_context, + principal, + 0, + &unparsed_name); + + if (error_code != 0) { + const char *error_message; + + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentity: Error parsing principal identity name: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + return NULL; + } + + identifier = g_strdup (unparsed_name); + krb5_free_unparsed_name (self->priv->kerberos_context, unparsed_name); + + return identifier; +} + +static void +gsd_kerberos_identity_init (GsdKerberosIdentity *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GSD_TYPE_KERBEROS_IDENTITY, + GsdKerberosIdentityPrivate); + self->priv->expiration_alarm = gsd_alarm_new (); + self->priv->expiring_alarm = gsd_alarm_new (); + self->priv->renewal_alarm = gsd_alarm_new (); +} + +static void +set_error_from_krb5_error_code (GsdKerberosIdentity *self, + GError **error, + gint code, + krb5_error_code error_code, + const char *format, + ...) +{ + const char *error_message; + char *literal_message; + char *expanded_format; + va_list args; + char **chunks; + + error_message = krb5_get_error_message (self->priv->kerberos_context, + error_code); + chunks = g_strsplit (format, "%k", -1); + expanded_format = g_strjoinv (error_message , chunks); + g_strfreev (chunks); + krb5_free_error_message (self->priv->kerberos_context, error_message); + + va_start (args, format); + literal_message = g_strdup_vprintf (expanded_format, args); + va_end (args); + + g_set_error_literal (error, + GSD_IDENTITY_ERROR, + code, + literal_message); + g_free (literal_message); +} + +char * +gsd_kerberos_identity_get_principal_name (GsdKerberosIdentity *self) +{ + krb5_principal principal; + krb5_error_code error_code; + char *unparsed_name; + char *principal_name; + int flags; + + if (self->priv->identifier == NULL) { + return NULL; + } + + error_code = krb5_parse_name (self->priv->kerberos_context, + self->priv->identifier, + &principal); + + if (error_code != 0) { + const char *error_message; + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentity: Error parsing identity %s into kerberos principal: %s", + self->priv->identifier, + error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + return NULL; + } + + flags = KRB5_PRINCIPAL_UNPARSE_DISPLAY; + error_code = krb5_unparse_name_flags (self->priv->kerberos_context, + principal, + flags, + &unparsed_name); + + if (error_code != 0) { + const char *error_message; + + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentity: Error parsing principal identity name: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + return NULL; + } + + principal_name = g_strdup (unparsed_name); + krb5_free_unparsed_name (self->priv->kerberos_context, unparsed_name); + + return principal_name; +} + +char * +gsd_kerberos_identity_get_realm_name (GsdKerberosIdentity *self) +{ + krb5_principal principal; + krb5_error_code error_code; + krb5_data *realm; + char *realm_name; + + if (self->priv->identifier == NULL) { + return NULL; + } + + error_code = krb5_parse_name (self->priv->kerberos_context, + self->priv->identifier, + &principal); + + if (error_code != 0) { + const char *error_message; + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentity: Error parsing identity %s into kerberos principal: %s", + self->priv->identifier, + error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + return NULL; + } + + realm = krb5_princ_realm (self->priv->kerberos_context, + principal); + realm_name = g_strndup (realm->data, realm->length); + krb5_free_principal (self->priv->kerberos_context, principal); + + return realm_name; +} + +static const char * +gsd_kerberos_identity_get_identifier (GsdIdentity *identity) +{ + GsdKerberosIdentity *self = GSD_KERBEROS_IDENTITY (identity); + + return self->priv->identifier; +} + +static gboolean +credentials_validate_existence (GsdKerberosIdentity *self, + krb5_principal principal, + krb5_creds *credentials) +{ + /* Checks if default principal associated with the cache has a valid + * ticket granting ticket in the passed in credentials + */ + + if (krb5_is_config_principal (self->priv->kerberos_context, + credentials->server)) { + return FALSE; + } + + /* looking for the krbtgt / REALM pair, so it should be exactly 2 items */ + if (krb5_princ_size (self->priv->kerberos_context, + credentials->server) != 2) { + return FALSE; + } + + if (!krb5_realm_compare (self->priv->kerberos_context, + credentials->server, + principal)) { + /* credentials are from some other realm */ + return FALSE; + } + + if (strncmp (credentials->server->data[0].data, + KRB5_TGS_NAME, + credentials->server->data[0].length) != 0) { + /* credentials aren't for ticket granting */ + return FALSE; + } + + if (credentials->server->data[1].length != principal->realm.length || + memcmp (credentials->server->data[1].data, + principal->realm.data, + principal->realm.length) != 0) { + /* credentials are for some other realm */ + return FALSE; + } + + return TRUE; +} + +static krb5_timestamp +get_current_time (GsdKerberosIdentity *self) +{ + krb5_timestamp current_time; + krb5_error_code error_code; + + error_code = krb5_timeofday (self->priv->kerberos_context, + ¤t_time); + + if (error_code != 0) { + const char *error_message; + + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + g_debug ("GsdKerberosIdentity: Error getting current time: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + return 0; + } + + return current_time; +} + +static gboolean +credentials_are_expired (GsdKerberosIdentity *self, + krb5_creds *credentials) +{ + krb5_timestamp current_time; + + current_time = get_current_time (self); + + self->priv->expiration_time = MAX (credentials->times.endtime, + self->priv->expiration_time); + + if (credentials->times.endtime <= current_time) { + return TRUE; + } + + return FALSE; +} + +static VerificationLevel +verify_identity (GsdKerberosIdentity *self, + GError **error) +{ + krb5_principal principal; + krb5_cc_cursor cursor; + krb5_creds credentials; + krb5_error_code error_code; + VerificationLevel verification_level; + + self->priv->expiration_time = 0; + + if (self->priv->credentials_cache == NULL) { + return VERIFICATION_LEVEL_UNVERIFIED; + } + + error_code = krb5_cc_get_principal (self->priv->kerberos_context, + self->priv->credentials_cache, + &principal); + + if (error_code != 0) { + if (error_code == KRB5_CC_END) { + return VERIFICATION_LEVEL_UNVERIFIED; + } + + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_VERIFYING, + error_code, + _("Could not find identity in credential cache: %k")); + return VERIFICATION_LEVEL_ERROR; + } + + error_code = krb5_cc_start_seq_get (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor); + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_VERIFYING, + error_code, + _("Could not find identity credentials in cache: %k")); + + verification_level = VERIFICATION_LEVEL_ERROR; + goto out; + } + + verification_level = VERIFICATION_LEVEL_UNVERIFIED; + + error_code = krb5_cc_next_cred (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor, + &credentials); + + while (error_code == 0) { + if (credentials_validate_existence (self, principal, &credentials)) { + if (!credentials_are_expired (self, &credentials)) { + verification_level = VERIFICATION_LEVEL_SIGNED_IN; + } else { + verification_level = VERIFICATION_LEVEL_EXISTS; + } + } + + error_code = krb5_cc_next_cred (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor, + &credentials); + } + + if (error_code != KRB5_CC_END) { + verification_level = VERIFICATION_LEVEL_ERROR; + + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_VERIFYING, + error_code, + _("Could not sift through identity credentials in cache: %k")); + goto out; + } + + error_code = krb5_cc_end_seq_get (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor); + + if (error_code != 0) { + verification_level = VERIFICATION_LEVEL_ERROR; + + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_VERIFYING, + error_code, + _("Could not finish up sifting through identity credentials in cache: %k")); + goto out; + } +out: + krb5_free_principal (self->priv->kerberos_context, principal); + return verification_level; +} + +static gboolean +gsd_kerberos_identity_is_signed_in (GsdIdentity *identity) +{ + GsdKerberosIdentity *self = GSD_KERBEROS_IDENTITY (identity); + VerificationLevel verification_level; + + verification_level = verify_identity (self, NULL); + + return verification_level == VERIFICATION_LEVEL_SIGNED_IN; +} + +static void +identity_interface_init (GsdIdentityInterface *interface) +{ + interface->get_identifier = gsd_kerberos_identity_get_identifier; + interface->is_signed_in = gsd_kerberos_identity_is_signed_in; +} + +static void +on_expiration_alarm_fired (GsdAlarm *alarm, + GsdKerberosIdentity *self) +{ + g_return_if_fail (GSD_IS_ALARM (alarm)); + g_return_if_fail (GSD_IS_KERBEROS_IDENTITY (self)); + + g_debug ("GsdKerberosIdentity: expiration alarm fired for identity %s", + gsd_identity_get_identifier (GSD_IDENTITY (self))); + g_signal_emit (G_OBJECT (self), signals[NEEDS_REFRESH], 0); +} + +static void +on_expiration_alarm_rearmed (GsdAlarm *alarm, + GsdKerberosIdentity *self) +{ + g_return_if_fail (GSD_IS_ALARM (alarm)); + g_return_if_fail (GSD_IS_KERBEROS_IDENTITY (self)); + + g_debug ("GsdKerberosIdentity: expiration alarm rearmed"); + g_signal_emit (G_OBJECT (self), signals[NEEDS_REFRESH], 0); +} + +static void +on_renewal_alarm_rearmed (GsdAlarm *alarm, + GsdKerberosIdentity *self) +{ + g_return_if_fail (GSD_IS_ALARM (alarm)); + g_return_if_fail (GSD_IS_KERBEROS_IDENTITY (self)); + + g_debug ("GsdKerberosIdentity: renewal alarm rearmed"); +} + +static void +on_renewal_alarm_fired (GsdAlarm *alarm, + GsdKerberosIdentity *self) +{ + g_return_if_fail (GSD_IS_ALARM (alarm)); + g_return_if_fail (GSD_IS_KERBEROS_IDENTITY (self)); + + g_clear_object (&self->priv->renewal_alarm_cancellable); + + if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) { + g_debug ("GsdKerberosIdentity: renewal alarm fired for signed-in identity"); + g_signal_emit (G_OBJECT (self), signals[NEEDS_RENEWAL], 0); + } +} + +static void +on_expiring_alarm_rearmed (GsdAlarm *alarm, + GsdKerberosIdentity *self) +{ + g_return_if_fail (GSD_IS_ALARM (alarm)); + g_return_if_fail (GSD_IS_KERBEROS_IDENTITY (self)); + + g_debug ("GsdKerberosIdentity: expiring alarm rearmed"); +} + +static void +on_expiring_alarm_fired (GsdAlarm *alarm, + GsdKerberosIdentity *self) +{ + g_return_if_fail (GSD_IS_ALARM (alarm)); + g_return_if_fail (GSD_IS_KERBEROS_IDENTITY (self)); + + g_clear_object (&self->priv->expiring_alarm_cancellable); + + if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) { + g_debug ("GsdKerberosIdentity: expiring alarm fired for signed-in identity"); + g_signal_emit (G_OBJECT (self), signals[EXPIRING], 0); + } +} + +static void +set_alarm (GsdKerberosIdentity *self, + GsdAlarm *alarm, + GDateTime *alarm_time, + GCancellable **cancellable) +{ + GDateTime *old_alarm_time; + + old_alarm_time = gsd_alarm_get_time (alarm); + if (old_alarm_time == NULL || + !g_date_time_equal (alarm_time, old_alarm_time)) { + GCancellable *new_cancellable; + + new_cancellable = g_cancellable_new (); + gsd_alarm_set_time (alarm, + alarm_time, + new_cancellable); + g_date_time_unref (alarm_time); + + g_clear_object (cancellable); + *cancellable = new_cancellable; + } + +} + +static void +disconnect_alarm_signals (GsdKerberosIdentity *self) +{ + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), + G_CALLBACK (on_renewal_alarm_fired), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), + G_CALLBACK (on_renewal_alarm_rearmed), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), + G_CALLBACK (on_expiring_alarm_fired), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), + G_CALLBACK (on_expiration_alarm_rearmed), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), + G_CALLBACK (on_expiration_alarm_fired), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), + G_CALLBACK (on_expiring_alarm_rearmed), + self); +} + +static void +connect_alarm_signals (GsdKerberosIdentity *self) +{ + g_signal_connect (G_OBJECT (self->priv->renewal_alarm), + "fired", + G_CALLBACK (on_renewal_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->renewal_alarm), + "rearmed", + G_CALLBACK (on_renewal_alarm_rearmed), + self); + g_signal_connect (G_OBJECT (self->priv->expiring_alarm), + "fired", + G_CALLBACK (on_expiring_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->expiring_alarm), + "rearmed", + G_CALLBACK (on_expiring_alarm_rearmed), + self); + g_signal_connect (G_OBJECT (self->priv->expiration_alarm), + "fired", + G_CALLBACK (on_expiration_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->expiration_alarm), + "rearmed", + G_CALLBACK (on_expiration_alarm_rearmed), + self); +} + +static void +reset_alarms (GsdKerberosIdentity *self) +{ + GDateTime *now; + GDateTime *expiration_time; + GDateTime *expiring_time; + GDateTime *renewal_time; + GTimeSpan time_span_until_expiration; + + now = g_date_time_new_now_local (); + expiration_time = g_date_time_new_from_unix_local (self->priv->expiration_time); + time_span_until_expiration = g_date_time_difference (expiration_time, now); + + /* Let the user reauthenticate 10 min before expiration */ + expiring_time = g_date_time_add_minutes (expiration_time, -10); + + /* Try to quietly auto-renew halfway through so in ideal configurations + * the ticket is never more than halfway to expired + */ + renewal_time = g_date_time_add (expiration_time, + - (time_span_until_expiration / 2)); + + disconnect_alarm_signals (self); + + set_alarm (self, + self->priv->renewal_alarm, + renewal_time, + &self->priv->renewal_alarm_cancellable); + set_alarm (self, + self->priv->expiring_alarm, + expiring_time, + &self->priv->expiring_alarm_cancellable); + set_alarm (self, + self->priv->expiration_alarm, + expiration_time, + &self->priv->expiration_alarm_cancellable); + + connect_alarm_signals (self); +} + +static void +cancel_and_clear_cancellable (GCancellable **cancellable) +{ + if (cancellable == NULL) { + return; + } + + if (!g_cancellable_is_cancelled (*cancellable)) { + g_cancellable_cancel (*cancellable); + } + + g_clear_object (cancellable); +} + +static void +clear_alarms (GsdKerberosIdentity *self) +{ + cancel_and_clear_cancellable (&self->priv->renewal_alarm_cancellable); + cancel_and_clear_cancellable (&self->priv->expiring_alarm_cancellable); + cancel_and_clear_cancellable (&self->priv->expiration_alarm_cancellable); +} + +static gboolean +gsd_kerberos_identity_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GsdKerberosIdentity *self = GSD_KERBEROS_IDENTITY (initable); + GError *verification_error; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + return FALSE; + } + + if (self->priv->identifier == NULL) { + self->priv->identifier = get_identifier (self); + } + + verification_error = NULL; + self->priv->cached_verification_level = verify_identity (self, &verification_error); + + switch (self->priv->cached_verification_level) { + case VERIFICATION_LEVEL_EXISTS: + case VERIFICATION_LEVEL_SIGNED_IN: + reset_alarms (self); + return TRUE; + + case VERIFICATION_LEVEL_UNVERIFIED: + return TRUE; + + case VERIFICATION_LEVEL_ERROR: + g_propagate_error (error, verification_error); + return FALSE; + default: + g_set_error (error, + GSD_IDENTITY_ERROR, + GSD_IDENTITY_ERROR_VERIFYING, + _("No associated identification found")); + return FALSE; + + } +} + +static void +initable_interface_init (GInitableIface *interface) +{ + interface->init = gsd_kerberos_identity_initable_init; +} + +typedef struct +{ + GsdKerberosIdentity *identity; + GsdIdentityInquiryFunc inquiry_func; + gpointer inquiry_data; + GDestroyNotify destroy_notify; + GCancellable *cancellable; +} SignInOperation; + +static krb5_error_code +on_kerberos_inquiry (krb5_context kerberos_context, + SignInOperation *operation, + const char *name, + const char *banner, + int number_of_prompts, + krb5_prompt prompts[]) +{ + GsdIdentityInquiry *inquiry; + krb5_error_code error_code; + + inquiry = gsd_kerberos_identity_inquiry_new (operation->identity, + name, + banner, + prompts, + number_of_prompts); + + operation->inquiry_func (inquiry, + operation->cancellable, + operation->inquiry_data); + + if (g_cancellable_is_cancelled (operation->cancellable)) { + error_code = KRB5_LIBOS_PWDINTR; + } else if (!gsd_identity_inquiry_is_complete (inquiry)) { + error_code = KRB5_LIBOS_PWDINTR; + } else { + error_code = 0; + } + + g_object_unref (inquiry); + + return error_code; +} + +static gboolean +gsd_kerberos_identity_update_credentials (GsdKerberosIdentity *self, + krb5_principal principal, + krb5_creds *new_credentials, + GError **error) +{ + krb5_error_code error_code; + + error_code = krb5_cc_initialize (self->priv->kerberos_context, + self->priv->credentials_cache, + principal); + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_SIGNING_IN, + error_code, + _("Could not initialize credentials cache: %k")); + + krb5_free_cred_contents (self->priv->kerberos_context, new_credentials); + goto out; + } + + error_code = krb5_cc_store_cred (self->priv->kerberos_context, + self->priv->credentials_cache, + new_credentials); + + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_RENEWING, + error_code, + _("Could not store new credentials in credentials cache: %k")); + + krb5_free_cred_contents (self->priv->kerberos_context, new_credentials); + goto out; + } + krb5_free_cred_contents (self->priv->kerberos_context, new_credentials); + + return TRUE; +out: + return FALSE; +} + +static SignInOperation * +sign_in_operation_new (GsdKerberosIdentity *identity, + GsdIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GDestroyNotify destroy_notify, + GCancellable *cancellable) +{ + SignInOperation *operation; + + operation = g_slice_new0 (SignInOperation); + operation->identity = g_object_ref (identity); + operation->inquiry_func = inquiry_func; + operation->inquiry_data = inquiry_data; + operation->destroy_notify = destroy_notify; + + if (cancellable == NULL) { + operation->cancellable = g_cancellable_new (); + } else { + operation->cancellable = g_object_ref (cancellable); + } + + return operation; +} + +static void +sign_in_operation_free (SignInOperation *operation) +{ + g_object_unref (operation->identity); + g_object_unref (operation->cancellable); + + g_slice_free (SignInOperation, operation); +} + +gboolean +gsd_kerberos_identity_sign_in (GsdKerberosIdentity *self, + const char *principal_name, + GsdIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GDestroyNotify destroy_notify, + GCancellable *cancellable, + GError **error) +{ + SignInOperation *operation; + krb5_principal principal; + krb5_error_code error_code; + krb5_creds new_credentials; + krb5_get_init_creds_opt *options; + krb5_deltat start_time; + char *service_name; + char *password; + gboolean signed_in; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + return FALSE; + } + + error_code = krb5_get_init_creds_opt_alloc (self->priv->kerberos_context, + &options); + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_SIGNING_IN, + error_code, + "%k"); + if (destroy_notify) { + destroy_notify (inquiry_data); + } + return FALSE; + } + + signed_in = FALSE; + + operation = sign_in_operation_new (self, + inquiry_func, + inquiry_data, + destroy_notify, + cancellable); + + if (g_strcmp0 (self->priv->identifier, principal_name) != 0) { + g_free (self->priv->identifier); + self->priv->identifier = g_strdup (principal_name); + } + + error_code = krb5_parse_name (self->priv->kerberos_context, + principal_name, + &principal); + + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_SIGNING_IN, + error_code, + "%k"); + if (destroy_notify) { + destroy_notify (inquiry_data); + } + return FALSE; + } + + /* FIXME: get from keyring if so configured */ + password = NULL; + + krb5_get_init_creds_opt_set_forwardable (options, TRUE); + krb5_get_init_creds_opt_set_proxiable (options, TRUE); + krb5_get_init_creds_opt_set_renew_life (options, G_MAXINT); + + start_time = 0; + service_name = NULL; + error_code = krb5_get_init_creds_password (self->priv->kerberos_context, + &new_credentials, + principal, + password, + (krb5_prompter_fct) + on_kerberos_inquiry, + operation, + start_time, + service_name, + options); + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_SIGNING_IN, + error_code, + "%k"); + if (destroy_notify) { + destroy_notify (inquiry_data); + } + sign_in_operation_free (operation); + + krb5_free_principal (self->priv->kerberos_context, principal); + goto done; + } + + if (destroy_notify) { + destroy_notify (inquiry_data); + } + sign_in_operation_free (operation); + + if (!gsd_kerberos_identity_update_credentials (self, + principal, + &new_credentials, + error)) { + krb5_free_principal (self->priv->kerberos_context, principal); + goto done; + } + krb5_free_principal (self->priv->kerberos_context, principal); + + g_debug ("GsdKerberosIdentity: identity signed in"); + signed_in = TRUE; +done: + + return signed_in; +} + +void +gsd_kerberos_identity_update (GsdKerberosIdentity *self, + GsdKerberosIdentity *new_identity) +{ + char *new_identifier; + VerificationLevel verification_level; + + if (self->priv->credentials_cache != NULL) { + krb5_cc_close (self->priv->kerberos_context, self->priv->credentials_cache); + } + + krb5_cc_dup (new_identity->priv->kerberos_context, + new_identity->priv->credentials_cache, + &self->priv->credentials_cache); + + new_identifier = get_identifier (self); + if (g_strcmp0 (self->priv->identifier, new_identifier) != 0) { + g_free (self->priv->identifier); + self->priv->identifier = new_identifier; + } else { + g_free (new_identifier); + } + + verification_level = verify_identity (self, NULL); + + if (verification_level == VERIFICATION_LEVEL_SIGNED_IN) { + reset_alarms (self); + } else { + clear_alarms (self); + } + + if (verification_level != self->priv->cached_verification_level) { + if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN && + verification_level == VERIFICATION_LEVEL_EXISTS) { + + self->priv->cached_verification_level = verification_level; + g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0); + } if (self->priv->cached_verification_level == VERIFICATION_LEVEL_EXISTS && + verification_level == VERIFICATION_LEVEL_SIGNED_IN) { + + self->priv->cached_verification_level = verification_level; + g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0); + } + } +} + +gboolean +gsd_kerberos_identity_renew (GsdKerberosIdentity *self, + GError **error) +{ + krb5_error_code error_code = 0; + krb5_principal principal; + krb5_creds new_credentials; + gboolean renewed = FALSE; + char *name = NULL; + + if (self->priv->credentials_cache == NULL) { + g_set_error (error, + GSD_IDENTITY_ERROR, + GSD_IDENTITY_ERROR_RENEWING, + _("Could not renew identitys: Not signed in")); + goto out; + } + + error_code = krb5_cc_get_principal (self->priv->kerberos_context, + self->priv->credentials_cache, + &principal); + + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_RENEWING, + error_code, + _("Could not renew identity: %k")); + goto out; + } + + name = gsd_kerberos_identity_get_principal_name (self); + + error_code = krb5_get_renewed_creds (self->priv->kerberos_context, + &new_credentials, + principal, + self->priv->credentials_cache, + NULL); + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_RENEWING, + error_code, + _("Could not get new credentials to renew identity %s: %k"), + name); + krb5_free_principal (self->priv->kerberos_context, principal); + goto out; + } + + if (!gsd_kerberos_identity_update_credentials (self, + principal, + &new_credentials, + error)) { + krb5_free_principal (self->priv->kerberos_context, principal); + goto out; + } + + g_debug ("GsdKerberosIdentity: identity %s renewed", name); + renewed = TRUE; +out: + g_free (name); + + return renewed; +} + +gboolean +gsd_kerberos_identity_erase (GsdKerberosIdentity *self, + GError **error) +{ + krb5_error_code error_code = 0; + + if (self->priv->credentials_cache != NULL) { + error_code = krb5_cc_destroy (self->priv->kerberos_context, + self->priv->credentials_cache); + self->priv->credentials_cache = NULL; + } + + if (error_code != 0) { + set_error_from_krb5_error_code (self, + error, + GSD_IDENTITY_ERROR_ERASING, + error_code, + _("Could not erase identity: %k")); + return FALSE; + } + + return TRUE; +} + +GsdIdentity * +gsd_kerberos_identity_new (krb5_context context, + krb5_ccache cache) +{ + GsdKerberosIdentity *self; + GError *error; + + self = GSD_KERBEROS_IDENTITY (g_object_new (GSD_TYPE_KERBEROS_IDENTITY, NULL)); + + krb5_cc_dup (context, + cache, + &self->priv->credentials_cache); + self->priv->kerberos_context = context; + + error = NULL; + if (!g_initable_init (G_INITABLE (self), NULL, &error)) { + const char *name; + + name = krb5_cc_get_name (context, + cache); + g_debug ("Could not build identity%s%s: %s", + name != NULL? " from credentials cache " : "", + name != NULL? name : "", + error->message); + g_error_free (error); + g_object_unref (self); + return NULL; + } + + return GSD_IDENTITY (self); +} diff --git a/panels/user-accounts/gsd-kerberos-identity.h b/panels/user-accounts/gsd-kerberos-identity.h new file mode 100644 index 000000000..abe6df1e5 --- /dev/null +++ b/panels/user-accounts/gsd-kerberos-identity.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: Ray Strode + */ + +#ifndef __GSD_KERBEROS_IDENTITY_H__ +#define __GSD_KERBEROS_IDENTITY_H__ + +#include <glib.h> +#include <glib-object.h> + +#include <krb5.h> +#include "gsd-kerberos-identity-inquiry.h" + +G_BEGIN_DECLS + +#define GSD_TYPE_KERBEROS_IDENTITY (gsd_kerberos_identity_get_type ()) +#define GSD_KERBEROS_IDENTITY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_KERBEROS_IDENTITY, GsdKerberosIdentity)) +#define GSD_KERBEROS_IDENTITY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_KERBEROS_IDENTITY, GsdKerberosIdentityClass)) +#define GSD_IS_KERBEROS_IDENTITY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_KERBEROS_IDENTITY)) +#define GSD_IS_KERBEROS_IDENTITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_KERBEROS_IDENTITY)) +#define GSD_KERBEROS_IDENTITY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSD_TYPE_KERBEROS_IDENTITY, GsdKerberosIdentityClass)) + +typedef struct _GsdKerberosIdentity GsdKerberosIdentity; +typedef struct _GsdKerberosIdentityClass GsdKerberosIdentityClass; +typedef struct _GsdKerberosIdentityPrivate GsdKerberosIdentityPrivate; +typedef enum _GsdKerberosIdentityDescriptionLevel GsdKerberosIdentityDescriptionLevel; + +enum _GsdKerberosIdentityDescriptionLevel +{ + GSD_KERBEROS_IDENTITY_DESCRIPTION_REALM, + GSD_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_AND_REALM, + GSD_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_ROLE_AND_REALM +}; + +struct _GsdKerberosIdentity +{ + GObject parent; + + GsdKerberosIdentityPrivate *priv; +}; + +struct _GsdKerberosIdentityClass +{ + GObjectClass parent_class; +}; + +GType gsd_kerberos_identity_get_type (void); + +GsdIdentity *gsd_kerberos_identity_new (krb5_context kerberos_context, + krb5_ccache cache); + +gboolean gsd_kerberos_identity_sign_in (GsdKerberosIdentity *self, + const char *principal_name, + GsdIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GDestroyNotify destroy_notify, + GCancellable *cancellable, + GError **error); +void gsd_kerberos_identity_update (GsdKerberosIdentity *identity, + GsdKerberosIdentity *new_identity); +gboolean gsd_kerberos_identity_renew (GsdKerberosIdentity *self, + GError **error); +gboolean gsd_kerberos_identity_erase (GsdKerberosIdentity *self, + GError **error); + +char *gsd_kerberos_identity_get_principal_name (GsdKerberosIdentity *self); +char *gsd_kerberos_identity_get_realm_name (GsdKerberosIdentity *self); +G_END_DECLS + +#endif /* __GSD_KERBEROS_IDENTITY_H__ */ diff --git a/panels/user-accounts/um-account-dialog.c b/panels/user-accounts/um-account-dialog.c index 985bb1012..aca41b9e1 100644 --- a/panels/user-accounts/um-account-dialog.c +++ b/panels/user-accounts/um-account-dialog.c @@ -544,6 +544,11 @@ on_realm_joined (GObject *source, um_realm_join_finish (self->selected_realm, result, &error); + /* If we're already enrolled treat that as a success */ + if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_ALREADY_ENROLLED)) { + g_clear_error (&error); + } + /* Yay, joined the domain, register the user locally */ if (error == NULL) { enterprise_permit_user_login (self); diff --git a/panels/user-accounts/um-alarm.h b/panels/user-accounts/um-alarm.h deleted file mode 100644 index 6eeb8d763..000000000 --- a/panels/user-accounts/um-alarm.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Authors: Ray Strode - */ - -#ifndef __UM_ALARM_H__ -#define __UM_ALARM_H__ - -#include <glib.h> -#include <glib-object.h> -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define UM_TYPE_ALARM (um_alarm_get_type ()) -#define UM_ALARM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UM_TYPE_ALARM, UmAlarm)) -#define UM_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_ALARM, UmAlarmClass)) -#define UM_IS_ALARM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UM_TYPE_ALARM)) -#define UM_IS_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UM_TYPE_ALARM)) -#define UM_ALARM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), UM_TYPE_ALARM, UmAlarmClass)) - -typedef struct _UmAlarm UmAlarm; -typedef struct _UmAlarmClass UmAlarmClass; -typedef struct _UmAlarmPrivate UmAlarmPrivate; - -struct _UmAlarm -{ - GObject parent; - - UmAlarmPrivate *priv; -}; - -struct _UmAlarmClass -{ - GObjectClass parent_class; - - void (* fired) (UmAlarm *alarm); - void (* rearmed) (UmAlarm *alarm); -}; - -GType um_alarm_get_type (void); - -UmAlarm *um_alarm_new (void); -void um_alarm_set (UmAlarm *alarm, - GDateTime *time, - GCancellable *cancellable); -G_END_DECLS - -#endif /* __UM_ALARM_H__ */ diff --git a/panels/user-accounts/um-identity-manager-private.h b/panels/user-accounts/um-identity-manager-private.h deleted file mode 100644 index aba123ce0..000000000 --- a/panels/user-accounts/um-identity-manager-private.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Authors: Ray Strode - */ - -#ifndef __UM_IDENTITY_MANAGER_PRIVATE_H__ -#define __UM_IDENTITY_MANAGER_PRIVATE_H__ - -#include <glib.h> -#include <glib-object.h> - -#include "um-identity-manager.h" - -G_BEGIN_DECLS - -void _um_identity_manager_emit_identity_added (UmIdentityManager *identity_manager, - UmIdentity *identity); -void _um_identity_manager_emit_identity_removed (UmIdentityManager *identity_manager, - UmIdentity *identity); -void _um_identity_manager_emit_identity_expired (UmIdentityManager *identity_manager, - UmIdentity *identity); -void _um_identity_manager_emit_identity_renewed (UmIdentityManager *identity_manager, - UmIdentity *identity); -void _um_identity_manager_emit_identity_renamed (UmIdentityManager *identity_manager, - UmIdentity *identity); -G_END_DECLS - -#endif /* __UM_IDENTITY_MANAGER_PRIVATE_H__ */ diff --git a/panels/user-accounts/um-identity-manager.c b/panels/user-accounts/um-identity-manager.c deleted file mode 100644 index 8b8d9e073..000000000 --- a/panels/user-accounts/um-identity-manager.c +++ /dev/null @@ -1,176 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2012 Red Hat, 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, 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., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include "config.h" - -#include <glib-object.h> -#include <glib/gi18n.h> -#include <gio/gio.h> - -#include "um-identity-manager.h" -#include "um-identity-manager-private.h" - -enum { - IDENTITY_ADDED, - IDENTITY_REMOVED, - IDENTITY_EXPIRED, - IDENTITY_RENEWED, - IDENTITY_RENAMED, - NUMBER_OF_SIGNALS, -}; - -static guint signals[NUMBER_OF_SIGNALS] = { 0 }; - -G_DEFINE_INTERFACE (UmIdentityManager, um_identity_manager, G_TYPE_OBJECT); - -static void -um_identity_manager_default_init (UmIdentityManagerInterface *interface) -{ - signals[IDENTITY_ADDED] = g_signal_new ("identity-added", - G_TYPE_FROM_INTERFACE (interface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UmIdentityManagerInterface, identity_added), - NULL, NULL, NULL, - G_TYPE_NONE, 1, UM_TYPE_IDENTITY); - signals[IDENTITY_REMOVED] = g_signal_new ("identity-removed", - G_TYPE_FROM_INTERFACE (interface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UmIdentityManagerInterface, identity_removed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, UM_TYPE_IDENTITY); - signals[IDENTITY_EXPIRED] = g_signal_new ("identity-expired", - G_TYPE_FROM_INTERFACE (interface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UmIdentityManagerInterface, identity_expired), - NULL, NULL, NULL, - G_TYPE_NONE, 1, UM_TYPE_IDENTITY); - signals[IDENTITY_RENEWED] = g_signal_new ("identity-renewed", - G_TYPE_FROM_INTERFACE (interface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UmIdentityManagerInterface, identity_renewed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, UM_TYPE_IDENTITY); - signals[IDENTITY_RENAMED] = g_signal_new ("identity-renamed", - G_TYPE_FROM_INTERFACE (interface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UmIdentityManagerInterface, identity_renamed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, UM_TYPE_IDENTITY); -} - -GQuark -um_identity_manager_error_quark (void) -{ - static GQuark error_quark = 0; - - if (error_quark == 0) { - error_quark = g_quark_from_static_string ("um-identity-manager-error"); - } - - return error_quark; -} - -void -um_identity_manager_list_identities (UmIdentityManager *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_return_if_fail (UM_IDENTITY_MANAGER_GET_IFACE (self)->list_identities); - UM_IDENTITY_MANAGER_GET_IFACE (self)->list_identities (self, - cancellable, - callback, - user_data); -} - -GList * -um_identity_manager_list_identities_finish (UmIdentityManager *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (UM_IDENTITY_MANAGER_GET_IFACE (self)->list_identities_finish, NULL); - return UM_IDENTITY_MANAGER_GET_IFACE (self)->list_identities_finish (self, - result, - error); -} - -void -um_identity_manager_sign_identity_out (UmIdentityManager *self, - UmIdentity *identity, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_return_if_fail (UM_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out); - UM_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out (self, identity, cancellable, callback, user_data); -} - -void -um_identity_manager_sign_identity_out_finish (UmIdentityManager *self, - GAsyncResult *result, - GError **error) -{ - g_return_if_fail (UM_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out_finish); - UM_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out_finish (self, result, error); -} - -char * -um_identity_manager_name_identity (UmIdentityManager *self, - UmIdentity *identity) -{ - g_return_val_if_fail (UM_IDENTITY_MANAGER_GET_IFACE (self)->name_identity, NULL); - return UM_IDENTITY_MANAGER_GET_IFACE (self)->name_identity (self, - identity); -} - -void -_um_identity_manager_emit_identity_added (UmIdentityManager *self, - UmIdentity *identity) -{ - g_signal_emit (G_OBJECT (self), signals[IDENTITY_ADDED], 0, identity); -} - -void -_um_identity_manager_emit_identity_removed (UmIdentityManager *self, - UmIdentity *identity) -{ - g_signal_emit (G_OBJECT (self), signals[IDENTITY_REMOVED], 0, identity); -} - -void -_um_identity_manager_emit_identity_expired (UmIdentityManager *self, - UmIdentity *identity) -{ - g_signal_emit (G_OBJECT (self), signals[IDENTITY_EXPIRED], 0, identity); -} - -void -_um_identity_manager_emit_identity_renewed (UmIdentityManager *self, - UmIdentity *identity) -{ - g_signal_emit (G_OBJECT (self), signals[IDENTITY_RENEWED], 0, identity); -} - -void -_um_identity_manager_emit_identity_renamed (UmIdentityManager *self, - UmIdentity *identity) -{ - g_signal_emit (G_OBJECT (self), signals[IDENTITY_RENAMED], 0, identity); -} diff --git a/panels/user-accounts/um-identity-manager.h b/panels/user-accounts/um-identity-manager.h deleted file mode 100644 index 1a47b68a6..000000000 --- a/panels/user-accounts/um-identity-manager.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Authors: Ray Strode - */ - -#ifndef __UM_IDENTITY_MANAGER_H__ -#define __UM_IDENTITY_MANAGER_H__ - -#include <glib.h> -#include <glib-object.h> -#include <gio/gio.h> - -#include "um-identity.h" - -G_BEGIN_DECLS - -#define UM_TYPE_IDENTITY_MANAGER (um_identity_manager_get_type ()) -#define UM_IDENTITY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UM_TYPE_IDENTITY_MANAGER, UmIdentityManager)) -#define UM_IDENTITY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_IDENTITY_MANAGER, UmIdentityManagerInterface)) -#define UM_IS_IDENTITY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UM_TYPE_IDENTITY_MANAGER)) -#define UM_IDENTITY_MANAGER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), UM_TYPE_IDENTITY_MANAGER, UmIdentityManagerInterface)) -#define UM_IDENTITY_MANAGER_ERROR (um_identity_manager_error_quark ()) - -typedef struct _UmIdentityManager UmIdentityManager; -typedef struct _UmIdentityManagerInterface UmIdentityManagerInterface; -typedef enum _UmIdentityManagerError UmIdentityManagerError; - -struct _UmIdentityManagerInterface -{ - GTypeInterface base_interface; - - /* Signals */ - void (* identity_added) (UmIdentityManager *identity_manager, - UmIdentity *identity); - - void (* identity_removed) (UmIdentityManager *identity_manager, - UmIdentity *identity); - void (* identity_expired) (UmIdentityManager *identity_manager, - UmIdentity *identity); - void (* identity_renewed) (UmIdentityManager *identity_manager, - UmIdentity *identity); - void (* identity_renamed) (UmIdentityManager *identity_manager, - UmIdentity *identity); - - /* Virtual Functions */ - void (* list_identities) (UmIdentityManager *identity_manager, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - GList * (* list_identities_finish) (UmIdentityManager *identity_manager, - GAsyncResult *result, - GError **error); - - void (* sign_identity_out) (UmIdentityManager *identity_manager, - UmIdentity *identity, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - void (* sign_identity_out_finish) (UmIdentityManager *identity_manager, - GAsyncResult *result, - GError **error); - - char * (* name_identity) (UmIdentityManager *identity_manager, - UmIdentity *identity); -}; - -enum _UmIdentityManagerError { - UM_IDENTITY_MANAGER_ERROR_INITIALIZING, - UM_IDENTITY_MANAGER_ERROR_MONITORING, - UM_IDENTITY_MANAGER_ERROR_SIGNING_OUT -}; - -GType um_identity_manager_get_type (void); -GQuark um_identity_manager_error_quark (void); - -void um_identity_manager_list_identities (UmIdentityManager *identity_manager, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -GList * um_identity_manager_list_identities_finish (UmIdentityManager *identity_manager, - GAsyncResult *result, - GError **error); -void um_identity_manager_sign_identity_out (UmIdentityManager *identity_manager, - UmIdentity *identity, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -void um_identity_manager_sign_identity_out_finish (UmIdentityManager *identity_manager, - GAsyncResult *result, - GError **error); -char *um_identity_manager_name_identity (UmIdentityManager *identity_manager, - UmIdentity *identity); - -G_END_DECLS - -#endif /* __UM_IDENTITY_MANAGER_H__ */ diff --git a/panels/user-accounts/um-identity.h b/panels/user-accounts/um-identity.h deleted file mode 100644 index 0a40775c8..000000000 --- a/panels/user-accounts/um-identity.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2012 Red Hat, 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., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Authors: Ray Strode <rstrode@redhat.com> - */ - -#ifndef __UM_IDENTITY_H__ -#define __UM_IDENTITY_H__ - -#include <glib.h> -#include <glib-object.h> - -G_BEGIN_DECLS - -#define UM_TYPE_IDENTITY (um_identity_get_type ()) -#define UM_IDENTITY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UM_TYPE_IDENTITY, UmIdentity)) -#define UM_IDENTITY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_IDENTITY, UmIdentityInterface)) -#define UM_IS_IDENTITY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UM_TYPE_IDENTITY)) -#define UM_IDENTITY_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), UM_TYPE_IDENTITY, UmIdentityInterface)) -#define UM_IDENTITY_ERROR (um_identity_error_quark ()) - -typedef struct _UmIdentity UmIdentity; -typedef struct _UmIdentityInterface UmIdentityInterface; -typedef enum _UmIdentityError UmIdentityError; - -struct _UmIdentityInterface -{ - GTypeInterface base_interface; - - const char * (* get_identifier) (UmIdentity *identity); - gboolean (* is_signed_in) (UmIdentity *identity); -}; - -enum _UmIdentityError { - UM_IDENTITY_ERROR_VERIFYING, - UM_IDENTITY_ERROR_ERASING -}; - -GType um_identity_get_type (void); -GQuark um_identity_error_quark (void); - -const char *um_identity_get_identifier (UmIdentity *identity); -gboolean um_identity_is_signed_in (UmIdentity *identity); - -G_END_DECLS - -#endif /* __UM_IDENTITY_H__ */ diff --git a/panels/user-accounts/um-realm-manager.c b/panels/user-accounts/um-realm-manager.c index c2e73596c..1d56aa89b 100644 --- a/panels/user-accounts/um-realm-manager.c +++ b/panels/user-accounts/um-realm-manager.c @@ -602,6 +602,10 @@ um_realm_join_finish (UmRealmKerberos *self, g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN, call_error->message); g_error_free (call_error); + } else if (g_str_equal (dbus_error, "org.freedesktop.realmd.Error.AlreadyEnrolled")) { + g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_ALREADY_ENROLLED, + call_error->message); + g_error_free (call_error); } else { g_propagate_error (error, call_error); } diff --git a/panels/user-accounts/um-realm-manager.h b/panels/user-accounts/um-realm-manager.h index 02e6e2ddf..7b2ee6e30 100644 --- a/panels/user-accounts/um-realm-manager.h +++ b/panels/user-accounts/um-realm-manager.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS typedef enum { UM_REALM_ERROR_BAD_LOGIN, UM_REALM_ERROR_BAD_PASSWORD, + UM_REALM_ERROR_ALREADY_ENROLLED, UM_REALM_ERROR_GENERIC, } UmRealmErrors; diff --git a/panels/user-accounts/um-user-panel.c b/panels/user-accounts/um-user-panel.c index 63602a313..7b9bb9594 100644 --- a/panels/user-accounts/um-user-panel.c +++ b/panels/user-accounts/um-user-panel.c @@ -41,10 +41,10 @@ #include "um-user.h" #include "um-user-manager.h" -#include "um-identity-manager.h" +#include "gsd-identity-manager.h" #ifdef HAVE_KERBEROS -#include "um-kerberos-identity-manager.h" +#include "gsd-kerberos-identity-manager.h" #endif #include "cc-strength-bar.h" @@ -71,7 +71,7 @@ G_DEFINE_DYNAMIC_TYPE (UmUserPanel, um_user_panel, CC_TYPE_PANEL) typedef struct { UmUserPanelPrivate *d; char *identifier; - UmIdentity *identity; + GsdIdentity *identity; GtkWidget *box; GtkWidget *label; GtkWidget *button; @@ -86,7 +86,7 @@ struct _UmUserPanelPrivate { GtkWidget *language_chooser; GHashTable *accessible_realms; - UmIdentityManager *identity_manager; + GsdIdentityManager *identity_manager; UmAccountDialog *account_dialog; UmPasswordDialog *password_dialog; UmPhotoDialog *photo_dialog; @@ -1445,7 +1445,7 @@ setup_main_window (UmUserPanelPrivate *d) #ifdef HAVE_KERBEROS static void remove_accessible_realm_for_identity (UmUserPanelPrivate *d, - UmIdentity *identity) + GsdIdentity *identity) { GtkWidget *grid; Realm *realm; @@ -1455,7 +1455,7 @@ remove_accessible_realm_for_identity (UmUserPanelPrivate *d, realm = g_hash_table_lookup (d->accessible_realms, (gpointer) - um_identity_get_identifier (identity)); + gsd_identity_get_identifier (identity)); if (realm == NULL) { return; @@ -1494,33 +1494,33 @@ remove_accessible_realm_for_identity (UmUserPanelPrivate *d, static void rename_accessible_realm_for_identity (UmUserPanelPrivate *d, - UmIdentity *identity) + GsdIdentity *identity) { Realm *realm; char *name; realm = g_hash_table_lookup (d->accessible_realms, (gpointer) - um_identity_get_identifier (identity)); + gsd_identity_get_identifier (identity)); if (realm == NULL) { return; } - name = um_identity_manager_name_identity (d->identity_manager, + name = gsd_identity_manager_name_identity (d->identity_manager, identity); gtk_label_set_text (GTK_LABEL (realm->label), name); g_free (name); } static void -on_signed_out (UmIdentityManager *manager, - GAsyncResult *result, - gpointer user_data) +on_signed_out (GsdIdentityManager *manager, + GAsyncResult *result, + gpointer user_data) { - UmIdentity *identity = UM_IDENTITY (user_data); + GsdIdentity *identity = GSD_IDENTITY (user_data); - um_identity_manager_sign_identity_out_finish (manager, + gsd_identity_manager_sign_identity_out_finish (manager, result, NULL); g_object_unref (identity); @@ -1532,25 +1532,25 @@ on_sign_out_clicked (GtkButton *button, { UmUserPanelPrivate *d = realm->d; - um_identity_manager_sign_identity_out (d->identity_manager, realm->identity, NULL, + gsd_identity_manager_sign_identity_out (d->identity_manager, realm->identity, NULL, (GAsyncReadyCallback) on_signed_out, g_object_ref (realm->identity)); } static Realm * realm_new (UmUserPanelPrivate *d, - UmIdentity *identity) + GsdIdentity *identity) { Realm *realm; char *name; realm = g_slice_new (Realm); realm->d = d; - realm->identifier = g_strdup (um_identity_get_identifier (identity)); + realm->identifier = g_strdup (gsd_identity_get_identifier (identity)); realm->identity = identity; realm->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - name = um_identity_manager_name_identity (d->identity_manager, + name = gsd_identity_manager_name_identity (d->identity_manager, identity); realm->label = gtk_label_new (name); g_free (name); @@ -1583,7 +1583,7 @@ realm_free (Realm *realm) static void add_accessible_realm_for_identity (UmUserPanelPrivate *d, - UmIdentity *identity) + GsdIdentity *identity) { Realm *realm; GtkWidget *grid; @@ -1610,49 +1610,49 @@ add_accessible_realm_for_identity (UmUserPanelPrivate *d, } static void -on_identity_added (UmIdentityManager *manager, - UmIdentity *identity, - UmUserPanelPrivate *d) +on_identity_added (GsdIdentityManager *manager, + GsdIdentity *identity, + UmUserPanelPrivate *d) { add_accessible_realm_for_identity (d, identity); } static void -on_identity_renewed (UmIdentityManager *manager, - UmIdentity *identity, - UmUserPanelPrivate *d) +on_identity_refreshed (GsdIdentityManager *manager, + GsdIdentity *identity, + UmUserPanelPrivate *d) { add_accessible_realm_for_identity (d, identity); } static void -on_identity_removed (UmIdentityManager *manager, - UmIdentity *identity, - UmUserPanelPrivate *d) +on_identity_removed (GsdIdentityManager *manager, + GsdIdentity *identity, + UmUserPanelPrivate *d) { remove_accessible_realm_for_identity (d, identity); } static void -on_identity_expired (UmIdentityManager *manager, - UmIdentity *identity, - UmUserPanelPrivate *d) +on_identity_expired (GsdIdentityManager *manager, + GsdIdentity *identity, + UmUserPanelPrivate *d) { remove_accessible_realm_for_identity (d, identity); } static void -on_identity_renamed (UmIdentityManager *manager, - UmIdentity *identity, - UmUserPanelPrivate *d) +on_identity_renamed (GsdIdentityManager *manager, + GsdIdentity *identity, + UmUserPanelPrivate *d) { rename_accessible_realm_for_identity (d, identity); } static void -on_identities_listed (UmIdentityManager *manager, - GAsyncResult *result, - UmUserPanelPrivate *d) +on_identities_listed (GsdIdentityManager *manager, + GAsyncResult *result, + UmUserPanelPrivate *d) { GError *error = NULL; GList *identities, *node; @@ -1673,15 +1673,15 @@ on_identities_listed (UmIdentityManager *manager, d); g_signal_connect (manager, - "identity-renewed", - G_CALLBACK (on_identity_renewed), + "identity-refreshed", + G_CALLBACK (on_identity_refreshed), d); g_signal_connect (manager, "identity-renamed", G_CALLBACK (on_identity_renamed), d); - identities = um_identity_manager_list_identities_finish (manager, + identities = gsd_identity_manager_list_identities_finish (manager, result, &error); @@ -1695,9 +1695,9 @@ on_identities_listed (UmIdentityManager *manager, } for (node = identities; node != NULL; node = node->next) { - UmIdentity *identity = node->data; + GsdIdentity *identity = node->data; - if (um_identity_is_signed_in (identity)) + if (gsd_identity_is_signed_in (identity)) add_accessible_realm_for_identity (d, identity); } } @@ -1710,8 +1710,8 @@ setup_realms (UmUserPanelPrivate *d) d->accessible_realms = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) realm_free); - d->identity_manager = um_kerberos_identity_manager_new (); - um_identity_manager_list_identities (d->identity_manager, NULL, + d->identity_manager = gsd_kerberos_identity_manager_new (); + gsd_identity_manager_list_identities (d->identity_manager, NULL, (GAsyncReadyCallback) on_identities_listed, d); #endif } |