/*
 * 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"
%}

%h{
#define MN_PI_MAILBOX_N_CONNECTION_TYPES	3
%}

%privateheader{
#include <gtk/gtk.h>
#include "mn-client-session.h"

#define MN_PI_MAILBOX_SESSION_PRIVATE \
  MNMailbox			*mailbox;	\
  MNAuthenticatedMailbox	*auth_mailbox;	\
  MNPIMailbox			*pi_mailbox;	\
  MNClientSession		*session
%}

%{
#include <glib/gi18n.h>
#include "mn-authenticated-mailbox-private.h"
#include "mn-mailbox-private.h"
#include "mn-util.h"
#include "mn-stock.h"
#include "mn-conf.h"

struct _MNClientSessionPrivate
{
  MN_PI_MAILBOX_SESSION_PRIVATE;
};
%}

enum MN_PI_MAILBOX_CONNECTION_TYPE
{
  NORMAL,
  INBAND_SSL,
  SSL,
} MN:PI:Mailbox:Connection:Type;

class MN:PI:Mailbox from MN:Authenticated:Mailbox (abstract)
{
  classwide int *default_ports;

  public MNPIMailboxConnectionType connection_type;
  property ENUM connection_type (link,
				 enum_type = MN:PI:Mailbox:Connection:Type,
				 flags = CONSTRUCT | MN_MAILBOX_PARAM_LOAD_SAVE,
				 default_value = MN_PI_MAILBOX_CONNECTION_TYPE_NORMAL);

  public char *authmech destroywith g_free;
  property STRING authmech (link, flags = MN_MAILBOX_PARAM_LOAD_SAVE);

  /* named hostname and not server for historical reasons */
  public char *hostname destroywith g_free;
  property STRING hostname (link, flags = MN_MAILBOX_PARAM_LOAD_SAVE | MN_MAILBOX_PARAM_REQUIRED);

  public int port;
  property INT port (link,
		     flags = MN_MAILBOX_PARAM_LOAD_SAVE,
		     minimum = 0,
		     maximum = 65535);

  public int runtime_port;

  private GtkWidget *untrusted_dialog;

  class_init (class)
  {
    /*
     * 5 minutes is a good default check delay for remote POP3/IMAP
     * mailboxes.
     */
    MN_MAILBOX_CLASS(class)->default_check_delay = 60 * 5;
  }

  init (self)
  {
    mn_mailbox_set_stock_id(MN_MAILBOX(self), MN_STOCK_REMOTE);
  }

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

    PARENT_HANDLER(mailbox);

    /* do not save default port to mailboxes.xml */
    if (self->port == SELF_GET_CLASS(self)->default_ports[self->connection_type])
      self->port = 0;

    self->runtime_port = self->port == 0
      ? SELF_GET_CLASS(self)->default_ports[self->connection_type]
      : self->port;

    auth_mailbox->keyring_server = g_strdup(self->hostname);
    auth_mailbox->keyring_port = self->runtime_port;
  }

  override (MN:Mailbox) void
    removed (MNMailbox *mailbox)
  {
    Self *self = SELF(mailbox);

    PARENT_HANDLER(mailbox);

    if (selfp->untrusted_dialog)
      gtk_dialog_response(GTK_DIALOG(selfp->untrusted_dialog), GTK_RESPONSE_CANCEL);
  }

  override (MN:Authenticated:Mailbox) void
    authenticated_check (MNAuthenticatedMailbox *mailbox)
  {
#if ! WITH_SSL
    Self *self = SELF(mailbox);
#endif

    PARENT_HANDLER(mailbox);

#if ! WITH_SSL
    if (self->connection_type == MN_PI_MAILBOX_CONNECTION_TYPE_INBAND_SSL
	|| self->connection_type == MN_PI_MAILBOX_CONNECTION_TYPE_SSL)
      {
	GDK_THREADS_ENTER();

	mn_mailbox_set_error(MN_MAILBOX(mailbox), _("SSL/TLS support has not been compiled in"));
	mn_mailbox_set_poll(MN_MAILBOX(mailbox), FALSE); /* disable the mailbox */

	gdk_flush();
	GDK_THREADS_LEAVE();
      }
#endif
  }

  protected gboolean
    split_uri (const char *uri (check null),
	       int maxlen,
	       char *scheme (check null),
	       char *auth (check null),
	       char *location (check null))
  {
    char *pat;
    int n;

    pat = g_strdup_printf("%%%i[^:]://%%%i[^@]@%%%is", maxlen, maxlen, maxlen);
    n = sscanf(uri, pat, scheme, auth, location);
    g_free(pat);

    return n == 3;	/* MN requires an username, hence the auth part */
  }

  protected gboolean
    split_uri_auth (const char *auth (check null),
		    int maxlen,
		    char *username (check null),
		    char *authmech (check null),
		    gboolean *has_authmech (check null))
  {
    char *pat;
    int n;

    pat = g_strdup_printf("%%%i[^;];AUTH=%%%is", maxlen, maxlen);
    n = sscanf(auth, pat, username, authmech);
    g_free(pat);

    if (n < 1)
      return FALSE;		/* MN requires an username */

    *has_authmech = n == 2;

    return TRUE;
  }

  protected void
    split_uri_hostport (const char *hostport (check null),
			int maxlen,
			char *hostname (check null),
			int *port (check null))
  {
    char *pat;
    int n;

    /* split host:port part in 2 subparts: host and port */

    pat = g_strdup_printf("[%%%i[^]]]:%%u", maxlen);
    n = sscanf(hostport, pat, hostname, port);
    g_free(pat);

    if (n < 1)
      {
	pat = g_strdup_printf("%%%i[^:]:%%u", maxlen);
	n = sscanf(hostport, pat, hostname, port);
	g_free(pat);
      }

    g_assert(n >= 1);

    if (n < 2)
      *port = 0;
  }

  protected void
    session_private_init (self, MNClientSessionPrivate *priv (check null))
  {
    priv->mailbox = MN_MAILBOX(self);
    priv->auth_mailbox = MN_AUTHENTICATED_MAILBOX(self);
    priv->pi_mailbox = MN_PI_MAILBOX(self);
  }

  protected gboolean
    sasl_get_credentials_cb (MNClientSession *session,
			     MNClientSessionPrivate *priv,
			     const char **username,
			     const char **password)
  {
#if WITH_SASL
    g_return_val_if_fail(username != NULL || password != NULL, FALSE);

    if (username)
      *username = priv->auth_mailbox->username;

    if (password)
      {
	if (mn_authenticated_mailbox_fill_password(priv->auth_mailbox, TRUE))
	  *password = priv->auth_mailbox->runtime_password;
	else
	  return FALSE;
      }
    else
      /*
       * Reset auth_cancelled ourselves since we did not call
       * mn_authenticated_mailbox_fill_password().
       */
      priv->auth_mailbox->auth_cancelled = FALSE;

    return TRUE;
#else
    g_assert_not_reached();
    return FALSE;
#endif /* WITH_SASL */
  }

  protected gboolean
    ssl_trust_server_cb (MNClientSession *session,
			 const char *server,
			 int port,
			 const char *fingerprint,
			 const char *verify_error,
			 MNClientSessionPrivate *priv)
  {
#if WITH_SSL
    gboolean status = FALSE;

    if (fingerprint)
      {
	GSList *gconf_fingerprints;

	gconf_fingerprints = mn_conf_get_string_list(MN_CONF_TRUSTED_X509_CERTIFICATES);

	if (mn_g_str_slist_find(gconf_fingerprints, fingerprint))
	  status = TRUE;
	else
	  if (self_run_untrusted_dialog(priv->pi_mailbox, server, verify_error, fingerprint))
	    {
	      status = TRUE;
	      gconf_fingerprints = g_slist_append(gconf_fingerprints, g_strdup(fingerprint));
	      mn_conf_set_string_list(MN_CONF_TRUSTED_X509_CERTIFICATES, gconf_fingerprints);
	    }

	mn_g_slist_free_deep(gconf_fingerprints);
      }
    else
      {
	char *key;
	GSList *gconf_servers = NULL;

	key = g_strdup_printf("%s:%i", server, port);
	gconf_servers = mn_conf_get_string_list(MN_CONF_TRUSTED_SERVERS);

	if (mn_g_str_slist_find(gconf_servers, key))
	  status = TRUE;
	else
	  {
	    if (self_run_untrusted_dialog(priv->pi_mailbox, server, _("missing certificate"), NULL))
	      {
		status = TRUE;
		gconf_servers = g_slist_append(gconf_servers, g_strdup(key));
		mn_conf_set_string_list(MN_CONF_TRUSTED_SERVERS, gconf_servers);
	      }
	  }

	g_free(key);
	mn_g_slist_free_deep(gconf_servers);
      }

    return status;
#else
    g_assert_not_reached();
    return FALSE;
#endif /* WITH_SSL */
  }

  private gboolean
    run_untrusted_dialog (self,
			  const char *server (check null),
			  const char *reason (check null),
			  const char *cert_fingerprint)
  {
#if WITH_SSL
    GString *secondary;
    gboolean status;

    g_return_val_if_fail(server != NULL, FALSE);
    g_return_val_if_fail(reason != NULL, FALSE);

    secondary = g_string_new(NULL);
    g_string_printf(secondary,
		    _("Mail Notification was unable to trust \"%s\" "
		      "(%s). It is possible that someone is "
		      "intercepting your communication to obtain "
		      "your confidential information.\n"
		      "\n"
		      "You should only connect to the server if you "
		      "are certain you are connected to \"%s\". "
		      "If you choose to connect to the server, this "
		      "message will not be shown again."),
		    server, reason, server);

    if (cert_fingerprint)
      {
	g_string_append(secondary, "\n\n");
	g_string_append_printf(secondary, _("Certificate fingerprint: %s."), cert_fingerprint);
      }

    GDK_THREADS_ENTER();

    selfp->untrusted_dialog = mn_alert_dialog_new(NULL,
						  GTK_MESSAGE_WARNING, 0,
						  _("Connect to untrusted server?"),
						  secondary->str);
    g_string_free(secondary, TRUE);

    mn_add_weak_pointer(&selfp->untrusted_dialog);

    gtk_dialog_add_buttons(GTK_DIALOG(selfp->untrusted_dialog),
			   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			   GTK_STOCK_CONNECT, GTK_RESPONSE_OK,
			   NULL);

    status = mn_dialog_run_nonmodal(GTK_DIALOG(selfp->untrusted_dialog)) == GTK_RESPONSE_OK;
    gtk_widget_destroy(selfp->untrusted_dialog);

    gdk_flush();
    GDK_THREADS_LEAVE();

    return status;
#else
    g_assert_not_reached();
    return FALSE;
#endif /* WITH_SSL */
  }

  protected void
    notice_cb (MNClientSession *session,
	       const char *message,
	       MNClientSessionPrivate *priv)
  {
    mn_mailbox_notice(priv->mailbox, "%s", message);
  }

  protected void
    warning_cb (MNClientSession *session,
		const char *message,
		MNClientSessionPrivate *priv)
  {
    mn_mailbox_warning(priv->mailbox, "%s", message);
  }
}
