/*
 * Mail Notification
 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

%headertop{
#include <gmime/gmime.h>
#include "mn-vfs-mailbox-backend.h"
#include "mn-message.h"
%}

%{
#include <string.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "mn-mailbox-private.h"
#include "mn-reentrant-mailbox-private.h"
#include "mn-vfs-mailbox-backend-private.h"
#include "mn-message-mime.h"
#include "mn-gmime-stream-vfs.h"
#include "mn-util.h"
#include "mn-vfs.h"
#if WITH_MBOX
#include "mn-mbox-mailbox-backend.h"
#endif
#if WITH_MOZILLA
#include "mn-mozilla-mailbox-backend.h"
#endif
%}

class MN:Base:Mbox:Mailbox:Backend from MN:VFS:Mailbox:Backend (abstract)
{
  private GnomeVFSURI *uri unrefwith gnome_vfs_uri_unref;
  private GnomeVFSFileSize last_size;
  private time_t last_mtime;
  private time_t last_ctime;

  private GMutex *mutex = {g_mutex_new()} destroywith g_mutex_free;

  override (MN:VFS:Mailbox:Backend) gboolean
    is (MNVFSMailboxBackend *dummy,
	MNVFSMailboxBackendClass *class,
	MNVFSMailbox *mailbox)
  {
    gboolean is = FALSE;
    GnomeVFSURI *uri;
    GnomeVFSFileInfo *file_info;

    uri = gnome_vfs_uri_ref(mailbox->vfs_uri);
    file_info = gnome_vfs_file_info_new();

    if (gnome_vfs_get_file_info_uri(uri, file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS | GNOME_VFS_FILE_INFO_GET_MIME_TYPE) == GNOME_VFS_OK)
      {
	GnomeVFSURI *uncompressed_uri;

	/* handle a compressed mailbox */
	uncompressed_uri = self_get_uncompressed_uri(uri, file_info);
	if (uncompressed_uri)
	  {
	    GnomeVFSFileInfo *uncompressed_file_info;

	    uncompressed_file_info = gnome_vfs_file_info_new();
	    if (gnome_vfs_get_file_info_uri(uncompressed_uri, uncompressed_file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS) == GNOME_VFS_OK)
	      {
		gnome_vfs_file_info_unref(file_info);
		file_info = uncompressed_file_info;

		gnome_vfs_uri_unref(uri);
		uri = uncompressed_uri;
	      }
	    else
	      {
		gnome_vfs_file_info_unref(uncompressed_file_info);
		gnome_vfs_uri_unref(uncompressed_uri);
	      }
	  }

	if ((file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE) != 0
	    && file_info->type == GNOME_VFS_FILE_TYPE_REGULAR
	    && self_get_mailbox_type(uri) == G_TYPE_FROM_CLASS(class))
	  is = TRUE;
      }

    gnome_vfs_uri_unref(uri);
    gnome_vfs_file_info_unref(file_info);

    return is;
  }

  private GType
    get_mailbox_type (GnomeVFSURI *uri (check null))
  {
    GMimeParser *parser;
    GType type = 0;

    parser = self_parser_new(uri, NULL);
    if (parser)
      {
	if (g_mime_parser_eos(parser))
	  /*
	   * The file is empty. For now, treat it as a mbox mailbox
	   * or, if mbox support was not compiled in, as a Mozilla
	   * mailbox. Whenever the file grows, it will be checked
	   * again and at that point we will decide whether it is a
	   * mbox mailbox, a Mozilla mailbox or another type of file.
	   */
	  {
#if WITH_MBOX
	    type = MN_TYPE_MBOX_MAILBOX_BACKEND;
#elif WITH_MOZILLA
	    type = MN_TYPE_MOZILLA_MAILBOX_BACKEND;
#endif
	  }
	else
	  {
	    /*
	     * The file is not empty. Decide whether it is a Mozilla
	     * mailbox or a mbox mailbox by checking if the first
	     * message has a X-Mozilla-Status header. If the first
	     * message cannot be parsed, then it is neither a Mozilla
	     * mailbox nor a mbox mailbox.
	     */

	    GMimeMessage *mime_message;

	    mime_message = g_mime_parser_construct_message(parser);
	    if (mime_message)
	      {
		if (g_mime_object_get_header(mime_message, "X-Mozilla-Status"))
		  {
#if WITH_MOZILLA
		    type = MN_TYPE_MOZILLA_MAILBOX_BACKEND;
#endif
		  }
		else
		  {
#if WITH_MBOX
		    type = MN_TYPE_MBOX_MAILBOX_BACKEND;
#endif
		  }
		g_object_unref(mime_message);
	      }
	  }
	g_object_unref(parser);
      }

    return type;
  }

  private GMimeParser *
    parser_new (GnomeVFSURI *uri (check null), GnomeVFSResult *result)
  {
    GnomeVFSResult _result;
    GnomeVFSHandle *handle;
    GMimeParser *parser = NULL;

    _result = gnome_vfs_open_uri(&handle, uri, GNOME_VFS_OPEN_READ);
    if (_result == GNOME_VFS_OK)
      {
	GMimeStream *stream;

	stream = mn_gmime_stream_vfs_new(handle, uri, &_result);
	if (stream)
	  {
	    parser = g_mime_parser_new_with_stream(stream);
	    g_object_unref(stream);
	    g_mime_parser_set_scan_from(parser, TRUE);
	  }
      }

    if (result)
      *result = _result;

    return parser;
  }

  override (MN:VFS:Mailbox:Backend) void
    check (MNVFSMailboxBackend *backend, int check_id)
  {
    Self *self = SELF(backend);
    gboolean polled;
    GMimeParser *parser;
    GnomeVFSResult result;
    gboolean uri_set;

    mn_vfs_mailbox_backend_monitor(backend, check_id, backend->mailbox->uri, GNOME_VFS_MONITOR_FILE);

    if (mn_reentrant_mailbox_check_aborted(MN_REENTRANT_MAILBOX(backend->mailbox), check_id))
      return;

    /* the poll property is protected by the GDK lock */
    GDK_THREADS_ENTER();

    polled = mn_mailbox_get_poll(MN_MAILBOX(backend->mailbox));

    gdk_flush();
    GDK_THREADS_LEAVE();

    if (polled)
      {
	GnomeVFSFileInfo *file_info;
	gboolean check;

	/*
	 * We are here because the mailbox has to be polled; only
	 * check it if necessary.
	 */

	file_info = gnome_vfs_file_info_new();
	result = gnome_vfs_get_file_info_uri(backend->mailbox->vfs_uri, file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS);

	g_mutex_lock(selfp->mutex);

	if (result == GNOME_VFS_OK
	    && (file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0
	    && (file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME) != 0
	    && file_info->size == selfp->last_size
	    && file_info->mtime == selfp->last_mtime)
	  {
	    if ((file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_CTIME) != 0)
	      check = file_info->ctime != selfp->last_ctime;
	    else
	      /*
	       * The ctime is often missing for remote mailboxes;
	       * since anyway the size and mtime do not have changed,
	       * we do not check the mailbox.
	       */
	      check = FALSE;
	  }
	else
	  check = TRUE;

	if (check)
	  {
	    selfp->last_size = file_info->size;
	    selfp->last_mtime = file_info->mtime;
	    selfp->last_ctime = file_info->ctime;

	    if (mn_reentrant_mailbox_check_aborted(MN_REENTRANT_MAILBOX(backend->mailbox), check_id))
	      check = FALSE;
	  }

	g_mutex_unlock(selfp->mutex);

	gnome_vfs_file_info_unref(file_info);

	if (! check)
	  return;
      }

    g_mutex_lock(selfp->mutex);
    uri_set = selfp->uri != NULL;
    g_mutex_unlock(selfp->mutex);

    if (! uri_set)
      {
	GnomeVFSFileInfo *file_info;
	GnomeVFSURI *uri;
	gboolean aborted;

	file_info = gnome_vfs_file_info_new();
	if (gnome_vfs_get_file_info_uri(backend->mailbox->vfs_uri, file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS | GNOME_VFS_FILE_INFO_GET_MIME_TYPE) == GNOME_VFS_OK)
	  uri = self_get_uncompressed_uri(backend->mailbox->vfs_uri, file_info);
	gnome_vfs_file_info_unref(file_info);

	if (! uri)
	  uri = gnome_vfs_uri_ref(backend->mailbox->vfs_uri);

	g_mutex_lock(selfp->mutex);

	if (! selfp->uri)
	  {
	    selfp->uri = uri;
	    uri = NULL;
	  }

	aborted = mn_reentrant_mailbox_check_aborted(MN_REENTRANT_MAILBOX(backend->mailbox), check_id);

	g_mutex_unlock(selfp->mutex);

	if (uri)
	  gnome_vfs_uri_unref(uri);

	if (aborted)
	  return;
      }

    parser = self_parser_new(selfp->uri, &result);
    if (parser)
      {
	GSList *messages = NULL;

	while (! g_mime_parser_eos(parser))
	  {
	    GMimeMessage *mime_message;

	    if (mn_reentrant_mailbox_check_aborted(MN_REENTRANT_MAILBOX(backend->mailbox), check_id))
	      break;

	    mime_message = g_mime_parser_construct_message(parser);
	    if (mime_message)
	      {
		MNMessage *message;

		message = self_get_new_message(self, mime_message);
		g_object_unref(mime_message);

		if (message)
		  messages = g_slist_prepend(messages, message);
	      }
	  }

	g_object_unref(parser);

	GDK_THREADS_ENTER();

	if (! mn_reentrant_mailbox_check_aborted(MN_REENTRANT_MAILBOX(backend->mailbox), check_id))
	  mn_mailbox_set_messages(MN_MAILBOX(backend->mailbox), messages);

	mn_g_object_slist_free(messages);

	gdk_flush();
	GDK_THREADS_LEAVE();

	return;			/* success */
      }

    if (! mn_reentrant_mailbox_check_aborted(MN_REENTRANT_MAILBOX(backend->mailbox), check_id))
      {
	GDK_THREADS_ENTER();

	mn_mailbox_set_error(MN_MAILBOX(backend->mailbox), _("unable to open mailbox: %s"), gnome_vfs_result_to_string(result));

	gdk_flush();
	GDK_THREADS_LEAVE();
      }
  }

  virtual private MNMessage *
    get_new_message (self, GMime:Message *mime_message (check null type));

  private GnomeVFSURI *
    get_uncompressed_uri (GnomeVFSURI *uri (check null),
			  GnomeVFSFileInfo *file_info (check null))
  {
    if ((file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE) != 0 && file_info->mime_type)
      {
	/* compression methods supported by GnomeVFS */
	static const struct
	{
	  const char *mime_type;
	  const char *method;
	} compressions[] = {
	  { "application/x-gzip", "gzip" },
	  { "application/x-bzip", "bzip2" }
	};
	int i;

	for (i = 0; i < G_N_ELEMENTS(compressions); i++)
	  if (! strcmp(file_info->mime_type, compressions[i].mime_type))
	    {
	      char *str;
	      GnomeVFSURI *uncompressed_uri;

	      str = g_strdup_printf("%s%s:/", GNOME_VFS_URI_MAGIC_STR, compressions[i].method);
	      uncompressed_uri = gnome_vfs_uri_append_string(uri, str);
	      g_free(str);

	      return uncompressed_uri;
	    }
      }

    return NULL;
  }
}
