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 }