1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
/* gkr-location-watch.c - Watch for changes in all base locations
4
Copyright (C) 2007 Stefan Walter
6
The Gnome Keyring Library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Library General Public License as
8
published by the Free Software Foundation; either version 2 of the
9
License, or (at your option) any later version.
11
The Gnome Keyring Library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Library General Public License for more details.
16
You should have received a copy of the GNU Library General Public
17
License along with the Gnome Library; see the file COPYING.LIB. If not,
18
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19
Boston, MA 02111-1307, USA.
21
Author: Stef Walter <stef@memberwebs.com>
26
#include "gkr-location-watch.h"
29
#include <glib/gstdio.h>
44
GkrLocationWatch *watch;
48
typedef struct _GkrLocationWatchPrivate GkrLocationWatchPrivate;
49
struct _GkrLocationWatchPrivate {
51
GPatternSpec *include;
52
GPatternSpec *exclude;
56
/* Matched Locations */
57
GHashTable *locations;
60
#define GKR_LOCATION_WATCH_GET_PRIVATE(o) \
61
(G_TYPE_INSTANCE_GET_PRIVATE((o), GKR_TYPE_LOCATION_WATCH, GkrLocationWatchPrivate))
63
G_DEFINE_TYPE (GkrLocationWatch, gkr_location_watch, G_TYPE_OBJECT);
65
static guint signals[LAST_SIGNAL] = { 0 };
67
/* -----------------------------------------------------------------------------
72
copy_key_value (gpointer key, gpointer value, gpointer data)
74
GHashTable *dest = (GHashTable*)data;
75
g_hash_table_replace (dest, key, value);
79
remove_locations (gpointer key, gpointer value, gpointer data)
81
GkrLocationWatch *watch = GKR_LOCATION_WATCH (data);
82
GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
84
g_hash_table_remove (pv->locations, key);
85
g_signal_emit (watch, signals[LOCATION_REMOVED], 0, GPOINTER_TO_UINT(key));
89
update_location (GkrLocationWatch *watch, gboolean force_all, GQuark location)
91
GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
95
/* TODO: Allocating and freeing this all the time is braindead */
96
path = gkr_location_to_path (location);
100
if (stat (path, &sb) < 0) {
101
if (errno != ENOENT && errno != ENOTDIR && errno != EPERM)
102
g_warning ("couldn't stat file: %s: %s", path, g_strerror (errno));
110
/* See if it has actually changed */
111
if (gkr_location_manager_note_mtime (watch->manager, location, sb.st_mtime) ||
113
g_assert (g_hash_table_lookup (pv->locations, GUINT_TO_POINTER (location)));
114
g_signal_emit (watch, signals[LOCATION_CHANGED], 0, location);
121
update_each_descendant (gpointer key, gpointer unused, gpointer data)
123
UpdateDescendants *ctx = (UpdateDescendants*)data;
124
GQuark location = GPOINTER_TO_UINT (key);
126
if (!gkr_location_is_descendant (ctx->parent, location))
129
if (update_location (ctx->watch, FALSE, location))
130
g_hash_table_remove (ctx->checks, GUINT_TO_POINTER (location));
134
update_volume (GkrLocationWatch *watch, GQuark volume, gboolean force_all,
137
GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
138
UpdateDescendants uctx;
142
const char *filename;
152
g_assert (GKR_IS_LOCATION_WATCH (watch));
154
dirloc = pv->subdir ? gkr_location_from_child (volume, pv->subdir) : volume;
155
path = gkr_location_to_path (dirloc);
157
/* Can't resolve the location? Skip. */
161
if (stat (path, &sb) < 0) {
162
if (errno != ENOENT && errno != ENOTDIR && errno != EPERM)
163
g_message ("couldn't stat directory: %s: %s", path, g_strerror (errno));
168
/* See if it was updated since last seen or not */
169
if (!gkr_location_manager_note_mtime (watch->manager, dirloc, sb.st_mtime) &&
172
uctx.parent = dirloc;
174
uctx.checks = checks;
176
/* Still need to check for individual file updates */
177
g_hash_table_foreach (pv->locations, update_each_descendant, &uctx);
183
/* Actually list the directory */
184
dir = g_dir_open (path, 0, &err);
186
if (errno != ENOENT && errno != ENOTDIR && errno != EPERM)
187
g_message ("couldn't list keyrings at: %s: %s", path,
188
err && err->message ? err->message : "");
194
while ((filename = g_dir_read_name (dir)) != NULL) {
195
if (filename[0] == '.')
197
if (pv->include && !g_pattern_match_string (pv->include, filename))
199
if (pv->exclude && g_pattern_match_string (pv->exclude, filename))
202
loc = gkr_location_from_child (dirloc, filename);
205
/* If we hadn't yet seen this, then add it */
206
key = GUINT_TO_POINTER (loc);
207
if (!g_hash_table_remove (checks, key)) {
209
/* Get the last modified time for this one */
210
file = gkr_location_to_path (loc);
212
ret = g_stat (file, &sb);
215
/* Couldn't access the file */
217
g_message ("couldn't stat file: %s: %s", file, g_strerror (lasterr));
224
/* We don't do directories */
225
if (sb.st_mode & S_IFDIR)
228
g_hash_table_replace (pv->locations, key, key);
229
gkr_location_manager_note_mtime (watch->manager, loc, sb.st_mtime);
230
g_signal_emit (watch, signals[LOCATION_ADDED], 0, loc);
232
/* Otherwise we already had it, see if it needs updating */
234
update_location (watch, force_all, loc);
242
/* -----------------------------------------------------------------------------
247
gkr_location_watch_init (GkrLocationWatch *obj)
249
GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (obj);
250
pv->locations = g_hash_table_new (g_direct_hash, g_direct_equal);
254
gkr_location_watch_dispose (GObject *obj)
256
GkrLocationWatch *watch = GKR_LOCATION_WATCH (obj);
259
g_object_unref (watch->manager);
260
watch->manager = NULL;
262
G_OBJECT_CLASS (gkr_location_watch_parent_class)->dispose (obj);
266
gkr_location_watch_finalize (GObject *obj)
268
GkrLocationWatch *watch = GKR_LOCATION_WATCH (obj);
269
GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
272
g_pattern_spec_free (pv->include);
274
g_pattern_spec_free (pv->exclude);
277
g_hash_table_destroy (pv->locations);
279
G_OBJECT_CLASS (gkr_location_watch_parent_class)->finalize (obj);
283
gkr_location_watch_class_init (GkrLocationWatchClass *klass)
285
GObjectClass *gobject_class;
286
gobject_class = (GObjectClass*) klass;
288
gkr_location_watch_parent_class = g_type_class_peek_parent (klass);
289
gobject_class->dispose = gkr_location_watch_dispose;
290
gobject_class->finalize = gkr_location_watch_finalize;
292
g_type_class_add_private (gobject_class, sizeof (GkrLocationWatchPrivate));
294
signals[LOCATION_ADDED] = g_signal_new ("location-added", GKR_TYPE_LOCATION_WATCH,
295
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkrLocationWatchClass, location_added),
296
NULL, NULL, g_cclosure_marshal_VOID__UINT,
297
G_TYPE_NONE, 1, G_TYPE_UINT);
299
signals[LOCATION_CHANGED] = g_signal_new ("location-changed", GKR_TYPE_LOCATION_WATCH,
300
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkrLocationWatchClass, location_changed),
301
NULL, NULL, g_cclosure_marshal_VOID__UINT,
302
G_TYPE_NONE, 1, G_TYPE_UINT);
304
signals[LOCATION_REMOVED] = g_signal_new ("location-removed", GKR_TYPE_LOCATION_WATCH,
305
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkrLocationWatchClass, location_removed),
306
NULL, NULL, g_cclosure_marshal_VOID__UINT,
307
G_TYPE_NONE, 1, G_TYPE_UINT);
311
gkr_location_watch_new (GkrLocationManager *locmgr, GQuark only_volume,
312
const gchar *subdir, const gchar *include, const gchar *exclude)
314
GkrLocationWatch *watch = g_object_new (GKR_TYPE_LOCATION_WATCH, NULL);
315
GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
318
locmgr = gkr_location_manager_get ();
320
g_return_val_if_fail (GKR_IS_LOCATION_MANAGER (locmgr), NULL);
322
/* TODO: Use properties */
323
pv->include = include ? g_pattern_spec_new (include) : NULL;
324
pv->exclude = exclude ? g_pattern_spec_new (exclude) : NULL;
325
pv->subdir = g_strdup (subdir);
326
pv->only_volume = only_volume;
328
watch->manager = locmgr;
329
g_object_ref (locmgr);
335
gkr_location_watch_refresh (GkrLocationWatch *watch, gboolean force_all)
337
GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
342
g_return_if_fail (GKR_IS_LOCATION_WATCH (watch));
344
/* Copy into our check set */
345
checks = g_hash_table_new (g_direct_hash, g_direct_equal);
346
g_hash_table_foreach (pv->locations, copy_key_value, checks);
348
/* If only one volume, then just try and access it directly */
349
if (pv->only_volume) {
350
if (gkr_location_manager_has_volume (watch->manager, pv->only_volume))
351
update_volume (watch, pv->only_volume, force_all, checks);
353
/* Go through each base location and update */
355
volumes = gkr_location_manager_get_volumes (watch->manager);
356
for (l = volumes; l; l = g_slist_next (l)) {
357
volume = GPOINTER_TO_UINT (l->data);
358
update_volume (watch, volume, force_all, checks);
362
/* Find any keyrings whose paths we didn't see */
363
g_hash_table_foreach (checks, remove_locations, watch);
364
g_hash_table_destroy (checks);