mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

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 }