~ubuntu-branches/ubuntu/natty/gnome-keyring/natty

« back to all changes in this revision

Viewing changes to daemon/util/gkr-location-watch.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2010-02-16 19:00:06 UTC
  • mfrom: (1.1.58 upstream)
  • Revision ID: james.westby@ubuntu.com-20100216190006-cqpnic4zxlkmmi0o
Tags: 2.29.90git20100218-0ubuntu1
Updated to a git snapshot version

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
3
 
 
4
 
   Copyright (C) 2007 Stefan Walter
5
 
 
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.
10
 
 
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.
15
 
 
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.
20
 
 
21
 
   Author: Stef Walter <stef@memberwebs.com>
22
 
*/
23
 
 
24
 
#include "config.h"
25
 
 
26
 
#include "gkr-location-watch.h"
27
 
 
28
 
#include <glib.h>
29
 
#include <glib/gstdio.h>
30
 
 
31
 
#include <sys/stat.h>
32
 
#include <errno.h>
33
 
#include <unistd.h>
34
 
 
35
 
enum {
36
 
        LOCATION_ADDED,
37
 
        LOCATION_REMOVED,
38
 
        LOCATION_CHANGED,
39
 
        LAST_SIGNAL
40
 
};
41
 
 
42
 
typedef struct {
43
 
        GQuark parent;
44
 
        GkrLocationWatch *watch;
45
 
        GHashTable *checks;
46
 
} UpdateDescendants;
47
 
 
48
 
typedef struct _GkrLocationWatchPrivate GkrLocationWatchPrivate;
49
 
struct _GkrLocationWatchPrivate {
50
 
        /* Specification */
51
 
        GPatternSpec *include;
52
 
        GPatternSpec *exclude;
53
 
        gchar *subdir;
54
 
        GQuark only_volume;
55
 
        
56
 
        /* Matched Locations */
57
 
        GHashTable *locations;
58
 
};
59
 
 
60
 
#define GKR_LOCATION_WATCH_GET_PRIVATE(o) \
61
 
        (G_TYPE_INSTANCE_GET_PRIVATE((o), GKR_TYPE_LOCATION_WATCH, GkrLocationWatchPrivate))
62
 
 
63
 
G_DEFINE_TYPE (GkrLocationWatch, gkr_location_watch, G_TYPE_OBJECT);
64
 
 
65
 
static guint signals[LAST_SIGNAL] = { 0 };
66
 
 
67
 
/* -----------------------------------------------------------------------------
68
 
 * HELPERS
69
 
 */
70
 
 
71
 
static void
72
 
copy_key_value (gpointer key, gpointer value, gpointer data)
73
 
{
74
 
        GHashTable *dest = (GHashTable*)data;
75
 
        g_hash_table_replace (dest, key, value);
76
 
}
77
 
 
78
 
static void
79
 
remove_locations (gpointer key, gpointer value, gpointer data)
80
 
{
81
 
        GkrLocationWatch *watch = GKR_LOCATION_WATCH (data);
82
 
        GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
83
 
 
84
 
        g_hash_table_remove (pv->locations, key);
85
 
        g_signal_emit (watch, signals[LOCATION_REMOVED], 0, GPOINTER_TO_UINT(key));
86
 
87
 
 
88
 
static gboolean
89
 
update_location (GkrLocationWatch *watch, gboolean force_all, GQuark location)
90
 
{
91
 
        GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
92
 
        struct stat sb;
93
 
        gchar *path;
94
 
 
95
 
        /* TODO: Allocating and freeing this all the time is braindead */
96
 
        path = gkr_location_to_path (location);
97
 
        if (!path)
98
 
                return FALSE;
99
 
 
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));
103
 
                g_free (path);
104
 
                return FALSE;
105
 
        }
106
 
        
107
 
        g_free (path);
108
 
 
109
 
 
110
 
        /* See if it has actually changed */
111
 
        if (gkr_location_manager_note_mtime (watch->manager, location, sb.st_mtime) || 
112
 
            force_all) {
113
 
                g_assert (g_hash_table_lookup (pv->locations, GUINT_TO_POINTER (location)));
114
 
                g_signal_emit (watch, signals[LOCATION_CHANGED], 0, location);
115
 
        }
116
 
        
117
 
        return TRUE;
118
 
}
119
 
                
120
 
static void
121
 
update_each_descendant (gpointer key, gpointer unused, gpointer data)
122
 
{
123
 
        UpdateDescendants *ctx = (UpdateDescendants*)data;
124
 
        GQuark location = GPOINTER_TO_UINT (key);
125
 
        
126
 
        if (!gkr_location_is_descendant (ctx->parent, location))
127
 
                return;
128
 
 
129
 
        if (update_location (ctx->watch, FALSE, location))
130
 
                g_hash_table_remove (ctx->checks, GUINT_TO_POINTER (location));
131
 
}
132
 
 
133
 
static void
134
 
update_volume (GkrLocationWatch *watch, GQuark volume, gboolean force_all, 
135
 
               GHashTable *checks)
136
 
{
137
 
        GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
138
 
        UpdateDescendants uctx;
139
 
        struct stat sb;
140
 
        GQuark dirloc;
141
 
        GError *err = NULL;
142
 
        const char *filename;
143
 
        gpointer key;
144
 
        gchar *path;
145
 
        gchar *file;
146
 
        GDir *dir;
147
 
        GQuark loc;
148
 
        int ret, lasterr;
149
 
 
150
 
        g_assert (volume);
151
 
        g_assert (checks);
152
 
        g_assert (GKR_IS_LOCATION_WATCH (watch));
153
 
        
154
 
        dirloc = pv->subdir ? gkr_location_from_child (volume, pv->subdir) : volume;
155
 
        path = gkr_location_to_path (dirloc);
156
 
        
157
 
        /* Can't resolve the location? Skip. */
158
 
        if (!path)
159
 
                return;
160
 
 
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));
164
 
                g_free (path);
165
 
                return;
166
 
        }
167
 
 
168
 
        /* See if it was updated since last seen or not */
169
 
        if (!gkr_location_manager_note_mtime (watch->manager, dirloc, sb.st_mtime) && 
170
 
            !force_all) {
171
 
 
172
 
                uctx.parent = dirloc;
173
 
                uctx.watch = watch;
174
 
                uctx.checks = checks;
175
 
                
176
 
                /* Still need to check for individual file updates */
177
 
                g_hash_table_foreach (pv->locations, update_each_descendant, &uctx);
178
 
                
179
 
                g_free (path);
180
 
                return;
181
 
        } 
182
 
 
183
 
        /* Actually list the directory */
184
 
        dir = g_dir_open (path, 0, &err);
185
 
        if (dir == NULL) {
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 : "");
189
 
                g_error_free (err);  
190
 
                g_free (path);
191
 
                return;
192
 
        }
193
 
        
194
 
        while ((filename = g_dir_read_name (dir)) != NULL) {
195
 
                if (filename[0] == '.')
196
 
                        continue;
197
 
                if (pv->include && !g_pattern_match_string (pv->include, filename))
198
 
                        continue;
199
 
                if (pv->exclude && g_pattern_match_string (pv->exclude, filename))
200
 
                        continue;
201
 
                        
202
 
                loc = gkr_location_from_child (dirloc, filename);
203
 
                g_assert (loc);
204
 
 
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)) {
208
 
                        
209
 
                        /* Get the last modified time for this one */
210
 
                        file = gkr_location_to_path (loc);
211
 
                        g_assert (file);
212
 
                        ret = g_stat (file, &sb);
213
 
                        lasterr = errno;
214
 
                        
215
 
                        /* Couldn't access the file */
216
 
                        if (ret < 0) {
217
 
                                g_message ("couldn't stat file: %s: %s", file, g_strerror (lasterr));
218
 
                                g_free (file);
219
 
                                continue;
220
 
                        }
221
 
                        
222
 
                        g_free (file);
223
 
                        
224
 
                        /* We don't do directories */
225
 
                        if (sb.st_mode & S_IFDIR)
226
 
                                continue;
227
 
 
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);
231
 
                        
232
 
                /* Otherwise we already had it, see if it needs updating */
233
 
                } else {
234
 
                        update_location (watch, force_all, loc);
235
 
                }
236
 
        }
237
 
 
238
 
        g_dir_close (dir);
239
 
        g_free (path);
240
 
}
241
 
 
242
 
/* -----------------------------------------------------------------------------
243
 
 * OBJECT
244
 
 */
245
 
 
246
 
static void
247
 
gkr_location_watch_init (GkrLocationWatch *obj)
248
 
{
249
 
        GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (obj);
250
 
        pv->locations = g_hash_table_new (g_direct_hash, g_direct_equal);
251
 
}
252
 
 
253
 
static void
254
 
gkr_location_watch_dispose (GObject *obj)
255
 
{
256
 
        GkrLocationWatch *watch = GKR_LOCATION_WATCH (obj);
257
 
 
258
 
        if (watch->manager)
259
 
                g_object_unref (watch->manager);
260
 
        watch->manager = NULL;   
261
 
        
262
 
        G_OBJECT_CLASS (gkr_location_watch_parent_class)->dispose (obj);
263
 
}
264
 
 
265
 
static void
266
 
gkr_location_watch_finalize (GObject *obj)
267
 
{
268
 
        GkrLocationWatch *watch = GKR_LOCATION_WATCH (obj);
269
 
        GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
270
 
         
271
 
        if (pv->include)
272
 
                g_pattern_spec_free (pv->include);
273
 
        if (pv->exclude)
274
 
                g_pattern_spec_free (pv->exclude);
275
 
        g_free (pv->subdir);
276
 
        
277
 
        g_hash_table_destroy (pv->locations);
278
 
        
279
 
        G_OBJECT_CLASS (gkr_location_watch_parent_class)->finalize (obj);
280
 
}
281
 
 
282
 
static void
283
 
gkr_location_watch_class_init (GkrLocationWatchClass *klass)
284
 
{
285
 
        GObjectClass *gobject_class;
286
 
        gobject_class = (GObjectClass*) klass;
287
 
 
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;
291
 
 
292
 
        g_type_class_add_private (gobject_class, sizeof (GkrLocationWatchPrivate));
293
 
 
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);
298
 
                        
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);
303
 
 
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);
308
 
}
309
 
 
310
 
GkrLocationWatch* 
311
 
gkr_location_watch_new (GkrLocationManager *locmgr, GQuark only_volume, 
312
 
                        const gchar *subdir, const gchar *include, const gchar *exclude)
313
 
{
314
 
        GkrLocationWatch *watch = g_object_new (GKR_TYPE_LOCATION_WATCH, NULL);
315
 
        GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
316
 
        
317
 
        if (!locmgr)
318
 
                locmgr = gkr_location_manager_get ();
319
 
                
320
 
        g_return_val_if_fail (GKR_IS_LOCATION_MANAGER (locmgr), NULL);
321
 
                
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;
327
 
        
328
 
        watch->manager = locmgr;
329
 
        g_object_ref (locmgr);
330
 
        
331
 
        return watch;
332
 
}
333
 
 
334
 
void
335
 
gkr_location_watch_refresh (GkrLocationWatch *watch, gboolean force_all)
336
 
{
337
 
        GkrLocationWatchPrivate *pv = GKR_LOCATION_WATCH_GET_PRIVATE (watch);
338
 
        GHashTable *checks;
339
 
        GSList *l, *volumes;
340
 
        GQuark volume;
341
 
        
342
 
        g_return_if_fail (GKR_IS_LOCATION_WATCH (watch));
343
 
        
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);
347
 
        
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);
352
 
                
353
 
        /* Go through each base location and update */
354
 
        } else {
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);
359
 
                }
360
 
        }
361
 
        
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);
365
 
}