/*
 * 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 "mn-authenticated-mailbox.h"
%}

%{
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <glib/gi18n.h>
#include <gmime/gmime.h>
#include "mn-mailbox-private.h"
#include "mn-authenticated-mailbox-private.h"
#include "mn-message-mime.h"
#include "mn-util.h"
%}

class MN:Webmail:Mailbox from MN:Authenticated:Mailbox (abstract)
{
  classwide const char *default_domain;

  public char *folder destroywith g_free;
  property STRING folder (link,
			  flags = CONSTRUCT
			  | MN_MAILBOX_PARAM_LOAD_SAVE
			  | MN_MAILBOX_PARAM_REQUIRED,
			  default_value = "Inbox");

  override (MN:Mailbox) void
    seal (MNMailbox *mailbox)
  {
    MNAuthenticatedMailbox *auth_mailbox = MN_AUTHENTICATED_MAILBOX(mailbox);
    Self *self = SELF(mailbox);

    PARENT_HANDLER(mailbox);

    if (! mailbox->runtime_name)
      mailbox->runtime_name = self_build_name(SELF_GET_CLASS(self), MN_AUTHENTICATED_MAILBOX(mailbox)->username, self->folder);

    if (! auth_mailbox->keyring_domain)
      auth_mailbox->keyring_domain = g_strdup(MN_WEBMAIL_MAILBOX_CLASS(SELF_GET_CLASS(self))->default_domain);
  }

  protected void
    parse_username (self, char **username (check null), char **domain (check null))
  {
    mn_authenticated_mailbox_parse_username(MN_AUTHENTICATED_MAILBOX(self), username, domain);
    if (! *domain)
      *domain = g_strdup(MN_WEBMAIL_MAILBOX_CLASS(SELF_GET_CLASS(self))->default_domain);
  }

  private int
    create_temporary_file (char **filename (check null), GError **err)
  {
    int fd;
    char *_filename;
    GError *tmp_err = NULL;

    /* GetLive chokes on the hyphen in "mail-notification", so use "mn" */
    fd = g_file_open_tmp("mn.XXXXXX", &_filename, &tmp_err);
    if (fd == -1)
      {
	g_set_error(err, 0, 0, _("unable to create a temporary file: %s"), tmp_err->message);
	g_error_free(tmp_err);
	return -1;
      }

    if (fchmod(fd, S_IRUSR | S_IWUSR) < 0)
      {
	g_set_error(err, 0, 0, _("unable to change the permissions of the temporary file: %s"), g_strerror(errno));
	g_free(_filename);
	close(fd);
	return -1;
      }

    *filename = _filename;
    return fd;
  }

  protected char *
    create_spool_file (GError **err)
  {
    int fd;
    char *filename;

    fd = self_create_temporary_file(&filename, err);
    if (fd == -1)
      return NULL;

    close(fd);

    return filename;
  }

  protected char *
    write_temporary_file (const char *contents (check null),
			  const char *encoding (check null),
			  GError **err)
  {
    int fd;
    GError *tmp_err = NULL;
    char *filename;
    GIOChannel *channel;
    gsize bytes_written;

    fd = self_create_temporary_file(&filename, err);
    if (fd == -1)
      return NULL;

    channel = g_io_channel_unix_new(fd);

    if (g_io_channel_set_encoding(channel, encoding, &tmp_err) != G_IO_STATUS_NORMAL)
      {
	g_set_error(err, 0, 0, _("unable to set encoding of %s: %s"), filename, tmp_err->message);
	goto error;
      }

    if (g_io_channel_write_chars(channel, contents, -1, &bytes_written, &tmp_err) != G_IO_STATUS_NORMAL)
      {
	g_set_error(err, 0, 0, _("unable to write to %s: %s"), filename, tmp_err->message);
	goto error;
      }

    if (g_io_channel_shutdown(channel, TRUE, &tmp_err) != G_IO_STATUS_NORMAL)
      {
	g_set_error(err, 0, 0, _("unable to close %s: %s"), filename, tmp_err->message);
	goto error_noshutdown;
      }

    g_io_channel_unref(channel);

    return filename;

  error:
    g_io_channel_shutdown(channel, FALSE, NULL);
  error_noshutdown:
    g_io_channel_unref(channel);
    g_free(filename);
    return NULL;
  }

  virtual private char *
    get_error_message (self,
		       const char *helper_stdout,
		       const char *helper_stderr,
		       gboolean *is_auth_failure (check null));

  private void
    print_multiline_text (self,
			  const char *text (check null),
			  const char *line_prefix (check null))
  {
    char **lines;
    int i;

    lines = g_strsplit(text, "\n", 0);
    for (i = 0; lines[i]; i++)
      {
	// do not print the empty last line
	if (! lines[i + 1] && ! lines[i][0])
	  break;

	mn_mailbox_notice(MN_MAILBOX(self), "%s%s", line_prefix, lines[i]);
      }
    g_strfreev(lines);
  }

  protected void
    print_config (self, const char *config (check null))
  {
    mn_mailbox_notice(MN_MAILBOX(self), _("configuration:"));
    self_print_multiline_text(self, config, "> ");
  }

  private void
    print_output (self,
		  const char *header (check null),
		  const char *contents)
  {
    if (! contents || ! *contents)
      return;

    mn_mailbox_notice(MN_MAILBOX(self), "%s", header);
    self_print_multiline_text(self, contents, "< ");
  }

  protected gboolean
    run_helper (self,
		const char *name (check null),
		const char *command (check null),
		GError **err)
  {
    GError *tmp_err = NULL;
    char *helper_stdout;
    char *helper_stderr;
    int exit_status;
    gboolean status = FALSE;

    mn_mailbox_notice(MN_MAILBOX(self), _("running %s"), command);

    if (! g_spawn_command_line_sync(command, &helper_stdout, &helper_stderr, &exit_status, &tmp_err))
      {
	g_set_error(err, 0, 0, _("cannot execute \"%s\": %s"), name, tmp_err->message);
	g_error_free(tmp_err);
	return FALSE;
      }

    self_print_output(self, _("standard output:"), helper_stdout);
    self_print_output(self, _("standard error output:"), helper_stderr);

    if (WEXITSTATUS(exit_status) != 0)
      {
	char *message;
	gboolean is_auth_failure = FALSE;

	message = self_get_error_message(self,
					 helper_stdout,
					 helper_stderr,
					 &is_auth_failure);
	if (message)
	  {
	    g_set_error(err, 0, 0, "%s", message);
	    g_free(message);
	  }
	else
	  /* translators: %s is a program name, as in "unknown fetchyahoo failure" */
	  g_set_error(err, 0, 0, _("unknown %s failure"), name);

	if (is_auth_failure && MN_AUTHENTICATED_MAILBOX(self)->auth_prompted)
	  mn_authenticated_mailbox_auth_failed(MN_AUTHENTICATED_MAILBOX(self));

	goto end;
      }

    status = TRUE;

  end:
    g_free(helper_stdout);
    g_free(helper_stderr);

    return status;
  }

  protected gboolean
    read_spool_file (self, const char *spool_file (check null), GError **err)
  {
    int fd;
    GMimeStream *stream;
    GMimeParser *parser;
    GSList *messages = NULL;

    fd = open(spool_file, O_RDONLY);
    if (fd < 0)
      {
	g_set_error(err, 0, 0, _("unable to open %s: %s"), spool_file, strerror(errno));
	return FALSE;
      }

    stream = g_mime_stream_fs_new(fd);

    parser = g_mime_parser_new_with_stream(stream);
    g_object_unref(stream);
    g_mime_parser_set_scan_from(parser, TRUE);

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

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

	    message = mn_message_new_from_mime_message(MN_MAILBOX(self), mime_message, NULL, 0, TRUE);
	    g_object_unref(mime_message);

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

    g_object_unref(parser);

    /* the fd is owned by the stream and closed by it */

    GDK_THREADS_ENTER();

    mn_mailbox_set_messages(MN_MAILBOX(self), messages);
    mn_g_object_slist_free(messages);

    gdk_flush();
    GDK_THREADS_LEAVE();

    return TRUE;
  }

  virtual private gboolean
    webmail_check (self, GError **err);

  override (MN:Authenticated:Mailbox) void
    authenticated_check (MNAuthenticatedMailbox *mailbox)
  {
    Self *self = SELF(mailbox);
    GError *err = NULL;

    PARENT_HANDLER(mailbox);

  again:
    if (! mn_authenticated_mailbox_fill_password(mailbox, TRUE))
      return;

    if (! self_webmail_check(self, &err))
      {
	if (mailbox->auth_prompted && mailbox->auth_failed)
	  {
	    g_clear_error(&err);
	    goto again;
	  }

	GDK_THREADS_ENTER();

	mn_mailbox_set_error(MN_MAILBOX(self), "%s", err->message);
	g_error_free(err);

	gdk_flush();
	GDK_THREADS_LEAVE();
      }
  }

  public char *
    build_name (MNWebmailMailboxClass *class (check null),
		const char *username (check null),
		const char *folder)
  {
    GString *name;

    name = g_string_new(NULL);

    if (strchr(username, '@'))
      g_string_append(name, username);
    else
      g_string_append_printf(name, "%s@%s", username, class->default_domain);

    if (folder && strcmp(folder, "Inbox"))
      g_string_append_printf(name, "/%s", folder);

    return g_string_free(name, FALSE);
  }
}
