src/mn-vfs-mailbox.gob (12022B) - raw
1 /*
2 * Mail Notification
3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 %headertop{
21 #include <libgnomevfs/gnome-vfs.h>
22 #include "mn-reentrant-mailbox.h"
23 %}
24
25 %h{
26 #define MN_VFS_MAILBOX_MAX_BACKEND_TYPES 5
27 extern GType mn_vfs_mailbox_backend_types[MN_VFS_MAILBOX_MAX_BACKEND_TYPES + 1];
28 %}
29
30 %privateheader{
31 #include "mn-vfs-mailbox-backend.h"
32 %}
33
34 %{
35 #include <glib/gi18n.h>
36 #include "mn-mailbox-private.h"
37 #include "mn-reentrant-mailbox-private.h"
38 #if WITH_MBOX
39 #include "mn-mbox-mailbox-backend.h"
40 #endif
41 #if WITH_MOZILLA
42 #include "mn-mozilla-mailbox-backend.h"
43 #endif
44 #if WITH_MH
45 #include "mn-mh-mailbox-backend.h"
46 #endif
47 #if WITH_MAILDIR
48 #include "mn-maildir-mailbox-backend.h"
49 #endif
50 #if WITH_SYLPHEED
51 #include "mn-sylpheed-mailbox-backend.h"
52 #endif
53 #include "mn-vfs-mailbox-backend-private.h"
54 #include "mn-vfs.h"
55 #include "mn-stock.h"
56 #include "mn-conf.h"
57 #include "mn-util.h"
58 #include "mn-locked-callback.h"
59
60 GType mn_vfs_mailbox_backend_types[MN_VFS_MAILBOX_MAX_BACKEND_TYPES + 1];
61
62 typedef struct
63 {
64 char *uri;
65 MNVFSMailboxBackend *owner;
66 GnomeVFSMonitorType type;
67 GnomeVFSMonitorCallback callback;
68 gpointer user_data;
69
70 GnomeVFSResult result;
71 GnomeVFSMonitorHandle *handle;
72 } Monitor;
73 %}
74
75 class MN:VFS:Mailbox from MN:Reentrant:Mailbox (abstract)
76 {
77 public char *uri destroywith g_free;
78 public GnomeVFSURI *vfs_uri unrefwith gnome_vfs_uri_unref;
79
80 private GSList *monitors;
81 private MNVFSMailboxBackend *backend unrefwith g_object_unref;
82 private unsigned int update_poll_id;
83
84 private GMutex *mutex = {g_mutex_new()} destroywith g_mutex_free;
85
86 public void
87 init_types (void)
88 {
89 int i = 0;
90
91 #if WITH_MBOX
92 mn_vfs_mailbox_backend_types[i++] = MN_TYPE_MBOX_MAILBOX_BACKEND;
93 #endif
94 #if WITH_MOZILLA
95 mn_vfs_mailbox_backend_types[i++] = MN_TYPE_MOZILLA_MAILBOX_BACKEND;
96 #endif
97 /*
98 * Claws Mail mailboxes contain both a .sylpheed_mark and a
99 * .mh_sequences file. List the Sylpheed backend first, in order
100 * to treat them as Sylpheed mailboxes. This way, the need to use
101 * rcvstore from procmail is removed.
102 */
103 #if WITH_SYLPHEED
104 mn_vfs_mailbox_backend_types[i++] = MN_TYPE_SYLPHEED_MAILBOX_BACKEND;
105 #endif
106 #if WITH_MH
107 mn_vfs_mailbox_backend_types[i++] = MN_TYPE_MH_MAILBOX_BACKEND;
108 #endif
109 #if WITH_MAILDIR
110 mn_vfs_mailbox_backend_types[i++] = MN_TYPE_MAILDIR_MAILBOX_BACKEND;
111 #endif
112 mn_vfs_mailbox_backend_types[i] = 0;
113 }
114
115 class_init (class)
116 {
117 /*
118 * Most VFS mailboxes will probably be local files, or remote
119 * files located in the same LAN. We therefore choose a small
120 * default check delay (1 minute).
121 */
122 MN_MAILBOX_CLASS(class)->default_check_delay = 60 * 1;
123 }
124
125 constructor (self)
126 {
127 /* set default poll to the most likely value */
128 mn_mailbox_set_poll(MN_MAILBOX(self), FALSE);
129 }
130
131 dispose (self)
132 {
133 /*
134 * We are no longer operational after dispose (see unrefwith
135 * above), so we must remove our monitors.
136 */
137 mn_source_clear(&selfp->update_poll_id);
138 mn_g_slist_clear_deep_custom(&selfp->monitors, (GFunc) self_monitor_free, NULL);
139 }
140
141 override (MN:Mailbox) void
142 seal (MNMailbox *mailbox)
143 {
144 Self *self = SELF(mailbox);
145
146 PARENT_HANDLER(mailbox);
147
148 if (self->uri)
149 {
150 self->vfs_uri = gnome_vfs_uri_new(self->uri);
151
152 if (! mailbox->stock_id && self->vfs_uri && ! gnome_vfs_uri_is_local(self->vfs_uri))
153 mn_mailbox_set_stock_id(mailbox, MN_STOCK_REMOTE);
154
155 if (! mailbox->runtime_name)
156 {
157 char *path;
158
159 path = gnome_vfs_get_local_path_from_uri(self->uri);
160 if (path)
161 {
162 mailbox->runtime_name = g_filename_display_name(path);
163 g_free(path);
164 }
165 else
166 mailbox->runtime_name = self->vfs_uri
167 ? gnome_vfs_uri_to_string(self->vfs_uri, GNOME_VFS_URI_HIDE_PASSWORD)
168 : g_strdup(self->uri);
169 }
170 }
171
172 if (! mailbox->stock_id)
173 mn_mailbox_set_stock_id(mailbox, MN_STOCK_LOCAL);
174 }
175
176 private Monitor *
177 find_monitor (self,
178 const char *uri (check null),
179 MNVFSMailboxBackend *owner,
180 GnomeVFSMonitorType type,
181 GnomeVFSMonitorCallback callback,
182 gpointer user_data)
183 {
184 GSList *l;
185
186 MN_LIST_FOREACH(l, selfp->monitors)
187 {
188 Monitor *monitor = l->data;
189
190 if (monitor->owner == owner
191 && monitor->type == type
192 && monitor->callback == callback
193 && monitor->user_data == user_data
194 && ! strcmp(monitor->uri, uri))
195 return monitor;
196 }
197
198 return NULL;
199 }
200
201 protected void
202 monitor (self,
203 const char *uri (check null),
204 MNVFSMailboxBackend *owner,
205 GnomeVFSMonitorType type,
206 GnomeVFSMonitorCallback callback,
207 gpointer user_data)
208 {
209 Monitor *monitor;
210
211 g_return_if_fail(owner == NULL || MN_IS_VFS_MAILBOX_BACKEND(owner));
212
213 monitor = self_find_monitor(self, uri, owner, type, callback, user_data);
214 if (! monitor)
215 {
216 monitor = g_new0(Monitor, 1);
217 monitor->uri = g_strdup(uri);
218 monitor->owner = owner;
219 monitor->type = type;
220 monitor->callback = callback;
221 monitor->user_data = user_data;
222 monitor->result = -1;
223
224 selfp->monitors = g_slist_append(selfp->monitors, monitor);
225 }
226
227 if (monitor->result != GNOME_VFS_OK)
228 {
229 GnomeVFSResult result;
230
231 result = mn_gnome_vfs_monitor_add_locked(&monitor->handle, uri, type, callback, user_data);
232 if (result != monitor->result)
233 {
234 monitor->result = result;
235 if (result != GNOME_VFS_OK)
236 mn_mailbox_warning(MN_MAILBOX(self), _("unable to monitor %s (%s), poll mode activated"), uri, gnome_vfs_result_to_string(result));
237
238 self_queue_update_poll(self);
239 }
240 }
241 }
242
243 private void
244 monitor_free (Monitor *monitor (check null))
245 {
246 g_free(monitor->uri);
247
248 if (monitor->result == GNOME_VFS_OK)
249 mn_gnome_vfs_monitor_cancel_locked(monitor->handle);
250
251 g_free(monitor);
252 }
253
254 private void
255 monitor_cb (GnomeVFSMonitorHandle *handle,
256 const char *monitor_uri,
257 const char *info_uri,
258 GnomeVFSMonitorEventType event_type,
259 gpointer user_data)
260 {
261 if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED
262 || event_type == GNOME_VFS_MONITOR_EVENT_DELETED
263 || event_type == GNOME_VFS_MONITOR_EVENT_CREATED)
264 {
265 Self *self = user_data;
266 MNVFSMailboxBackend *backend;
267
268 /*
269 * Queue a check to detect if the mailbox type has changed. If
270 * the mailbox has a backend, we must use
271 * mn_vfs_mailbox_backend_queue_check() (which respects the
272 * check_latency member of MNVFSMailboxBackend) in order to
273 * avoid race conditions.
274 */
275
276 self_lock(self);
277
278 backend = selfp->backend;
279 if (backend)
280 mn_vfs_mailbox_backend_queue_check(backend);
281
282 self_unlock(self);
283
284 /*
285 * Note that at this point, we have released the lock and the
286 * backend might have been finalized by another thread: we can
287 * only test the nullity of the pointer.
288 */
289 if (! backend)
290 mn_reentrant_mailbox_queue_check(MN_REENTRANT_MAILBOX(self));
291 }
292 }
293
294 protected void
295 remove_monitors_by_owner (self, MNVFSMailboxBackend *owner)
296 {
297 GSList *l;
298
299 g_return_if_fail(owner == NULL || MN_IS_VFS_MAILBOX_BACKEND(owner));
300
301 start:
302 MN_LIST_FOREACH(l, selfp->monitors)
303 {
304 Monitor *monitor = l->data;
305
306 if (monitor->owner == owner)
307 {
308 selfp->monitors = mn_g_slist_delete_link_deep_custom(selfp->monitors, l, (GFunc) self_monitor_free, NULL);
309 self_queue_update_poll(self);
310 goto start;
311 }
312 }
313 }
314
315 private void
316 queue_update_poll (self)
317 {
318 if (! selfp->update_poll_id)
319 selfp->update_poll_id = gdk_threads_add_idle(self_update_poll_cb, self);
320 }
321
322 private gboolean
323 update_poll_cb (gpointer data)
324 {
325 Self *self = data;
326 gboolean polled = FALSE;
327 GSList *l;
328
329 self_lock(self);
330
331 MN_LIST_FOREACH(l, selfp->monitors)
332 {
333 Monitor *monitor = l->data;
334
335 if (monitor->result != GNOME_VFS_OK)
336 {
337 polled = TRUE;
338 break;
339 }
340 }
341
342 selfp->update_poll_id = 0;
343
344 self_unlock(self);
345
346 if (mn_mailbox_get_poll(MN_MAILBOX(self)) != polled)
347 mn_mailbox_set_poll(MN_MAILBOX(self), polled);
348
349 return FALSE; /* remove source */
350 }
351
352 override (MN:Reentrant:Mailbox) void
353 reentrant_check (MNReentrantMailbox *mailbox, int check_id)
354 {
355 Self *self = SELF(mailbox);
356 MNVFSMailboxBackend *backend;
357 MNVFSMailboxBackendClass *class;
358 GType backend_type = 0;
359 gboolean exists;
360
361 self_lock(self);
362
363 /*
364 * Install the monitors that detect a mailbox type change. These
365 * monitors are not meant to detect if the mail status of a
366 * mailbox changes (it is the responsability of the backend),
367 * although they will do so with some mailbox formats.
368 *
369 * For properly detecting the new mailbox type when the file or
370 * directory changes we need install both a file and a directory
371 * monitor, since depending on the underlying implementation we
372 * might not get file events if the URI is a directory, etc.
373 */
374 self_monitor(self, self->uri, NULL, GNOME_VFS_MONITOR_FILE, self_monitor_cb, self);
375 self_monitor(self, self->uri, NULL, GNOME_VFS_MONITOR_DIRECTORY, self_monitor_cb, self);
376
377 backend = selfp->backend;
378 if (backend)
379 g_object_ref(backend);
380
381 self_unlock(self);
382
383 class = backend ? MN_VFS_MAILBOX_BACKEND_GET_CLASS(backend) : NULL;
384 if (class && class->is(NULL, class, self))
385 backend_type = G_OBJECT_TYPE(backend);
386 else
387 {
388 exists = mn_vfs_test(self->vfs_uri, G_FILE_TEST_EXISTS);
389 if (exists)
390 {
391 int i;
392
393 for (i = 0; mn_vfs_mailbox_backend_types[i]; i++)
394 {
395 class = g_type_class_peek(mn_vfs_mailbox_backend_types[i]);
396 g_assert(class != NULL);
397
398 if (class->is(NULL, class, self))
399 {
400 backend_type = mn_vfs_mailbox_backend_types[i];
401 break;
402 }
403
404 if (mn_reentrant_mailbox_check_aborted(mailbox, check_id))
405 goto end;
406 }
407 }
408 }
409
410 if (! backend || backend_type != G_OBJECT_TYPE(backend))
411 {
412 MNVFSMailboxBackend *new_backend = NULL;
413
414 if (! mn_reentrant_mailbox_check_aborted(mailbox, check_id))
415 {
416 GDK_THREADS_ENTER();
417
418 if (backend_type)
419 {
420 new_backend = g_object_new(backend_type, MN_VFS_MAILBOX_BACKEND_PROP_MAILBOX(self), NULL);
421 mn_mailbox_set_format(MN_MAILBOX(self), (char *) MN_VFS_MAILBOX_BACKEND_GET_CLASS(new_backend)->format);
422 }
423 else
424 {
425 mn_mailbox_set_format(MN_MAILBOX(self), NULL);
426 mn_mailbox_set_error(MN_MAILBOX(self), exists ? _("unknown mailbox format") : _("does not exist"));
427 }
428
429 gdk_flush();
430 GDK_THREADS_LEAVE();
431 }
432
433 self_lock(self);
434
435 if (backend)
436 {
437 g_object_unref(backend);
438 backend = NULL;
439 }
440
441 if (! mn_reentrant_mailbox_check_aborted(mailbox, check_id))
442 {
443 if (selfp->backend)
444 {
445 g_object_unref(selfp->backend);
446 selfp->backend = NULL;
447 }
448
449 if (new_backend)
450 {
451 selfp->backend = backend = new_backend;
452 new_backend = NULL;
453
454 g_object_ref(backend);
455 }
456 }
457
458 self_unlock(self);
459
460 if (new_backend)
461 g_object_unref(new_backend);
462 }
463
464 if (backend)
465 mn_vfs_mailbox_backend_check(backend, check_id);
466
467 end:
468 if (backend)
469 {
470 self_lock(self);
471 g_object_unref(backend);
472 self_unlock(self);
473 }
474 }
475
476 public void
477 lock (self)
478 {
479 g_mutex_lock(selfp->mutex);
480 }
481
482 public void
483 unlock (self)
484 {
485 g_mutex_unlock(selfp->mutex);
486 }
487 }