/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 1999-2025 Free Software Foundation, Inc.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library.  If not, see
   <http://www.gnu.org/licenses/>. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef ENABLE_SENDMAIL

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <confpaths.h>

#include <mailutils/address.h>
#include <mailutils/debug.h>
#include <mailutils/observer.h>
#include <mailutils/property.h>
#include <mailutils/url.h>
#include <mailutils/errno.h>
#include <mailutils/progmailer.h>

#include <mailutils/sys/url.h>
#include <mailutils/sys/mailer.h>
#include <mailutils/sys/registrar.h>

static void sendmail_destroy (mu_mailer_t);
static int sendmail_open (mu_mailer_t, int);
static int sendmail_close (mu_mailer_t);
static int sendmail_send_message (mu_mailer_t, mu_message_t, mu_address_t,
				  mu_address_t);


static int
_url_sendmail_init (mu_url_t url)
{
  if (url->path == NULL)
    if ((url->path = strdup (PATH_SENDMAIL)) == 0)
      return ENOMEM;

  return 0;
}

int
_mu_mailer_sendmail_init (mu_mailer_t mailer)
{
  int status;
  mu_progmailer_t pm;

  status = mu_progmailer_create (&pm);
  if (status)
    return status;
  
  mailer->data = pm;
  mailer->_destroy = sendmail_destroy;
  mailer->_open = sendmail_open;
  mailer->_close = sendmail_close;
  mailer->_send_message = sendmail_send_message;

  /* Set our properties.  */
  {
    mu_property_t property = NULL;
    mu_mailer_get_property (mailer, &property);
    mu_property_set_value (property, "TYPE", "SENDMAIL", 1);
  }
  return 0;
}

static void
sendmail_destroy (mu_mailer_t mailer)
{
  mu_progmailer_destroy ((mu_progmailer_t*)&mailer->data);
}

static int
sendmail_open (mu_mailer_t mailer, int flags)
{
  mu_progmailer_t pm = mailer->data;
  int status;
  const char *path;

  /* Sanity checks.  */
  if (pm == NULL)
    return EINVAL;

  mailer->flags = flags;

  if ((status = mu_url_sget_path (mailer->url, &path)))
    return status;

  if (access (path, X_OK) == -1)
    return errno;
  status = mu_progmailer_set_command (pm, path);
  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("sendmail binary: %s", path));
  return status;
}

static int
sendmail_close (mu_mailer_t mailer)
{
  return mu_progmailer_close (mailer->data);
}

static int
mailer_property_is_set (mu_mailer_t mailer, const char *name)
{
  mu_property_t property = NULL;

  mu_mailer_get_property (mailer, &property);
  return mu_property_is_set (property, name);
}

static int
sendmail_send_message (mu_mailer_t mailer, mu_message_t msg, mu_address_t from,
		       mu_address_t to)
{
  mu_progmailer_t pm = mailer->data;
  int argc = 0;
  const char **argvec = NULL;
  size_t tocount = 0;
  const char *emailfrom = NULL;
  int status;
  
  if (!pm)
    return EINVAL;
  
  /* Count the length of the arg vec: */

  argc++;			/* terminating NULL */
  argc++;			/* sendmail */
  argc++;			/* -oi (do not treat '.' as message
				   terminator) */
  
  if (from)
    {
      if ((status = mu_address_sget_email (from, 1, &emailfrom)) != 0)
	{
	  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		    ("cannot get recipient email: %s",
		     mu_strerror (status)));
	  return status;
	}

      if (!emailfrom)
	{
	  /* the address wasn't fully qualified, choke (for now) */
	  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE,
		    ("envelope from (%s) not fully qualified\n",
		     emailfrom));
	  return MU_ERR_INVALID_EMAIL;
	}

      argc += 2;		/* -f from */
    }
	
  if (to)
    {
      status = mu_address_get_email_count (to, &tocount);
      if (status)
	return status;

      if (tocount == 0)
	{
	  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("missing recipients"));
	  return MU_ERR_NOENT;
	}
      
      argc += tocount;	/* 1 per to address */
    }

  argc++;		/* -t */

  /* Allocate arg vec: */
  if ((argvec = calloc (argc, sizeof (*argvec))) == 0)
    return ENOMEM;
  
  argc = 0;
  
  if (mu_progmailer_sget_command (pm, &argvec[argc]) || argvec[argc] == NULL)
    {
      free (argvec);
      return EINVAL;
    }
  
  argc++;
  argvec[argc++] = "-oi";

  if (from)
    {
      argvec[argc++] = "-f";
      argvec[argc++] = emailfrom;
    }
	
  if (!to || mailer_property_is_set (mailer, "READ_RECIPIENTS"))
    {
      argvec[argc++] = "-t";
    }
  else
    {
      size_t i;
      size_t count = 0;
      
      mu_address_get_count (to, &count);
      
      for (i = 1; i <= count; i++)
	{
	  const char *email;
	  if ((status = mu_address_sget_email (to, i, &email)) != 0)
	    {
	      free (argvec);
	      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
			("cannot get email of recipient #%lu: %s",
			 (unsigned long) i, mu_strerror (status)));
	      return status;
	    }
	  
	  if (!email)
	    {
	      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE,
			("envelope to (%s) not fully qualified",
			 email));
	      free (argvec);
	      return MU_ERR_INVALID_EMAIL;
	    }
	  argvec[argc++] = email;
	}
    }
  argvec[argc] = NULL;
  
  status = mu_progmailer_open (pm, (char**) argvec);
  if (status == 0)
    {
      status = mu_progmailer_send (pm, msg);
      if (status == 0)
	  mu_observable_notify (mailer->observable, MU_EVT_MAILER_MESSAGE_SENT,
				msg);
      else
	mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		  ("progmailer error: %s", mu_strerror (status)));
    }
  
  free (argvec);
  return status;
}

static struct _mu_record _sendmail_record =
{
  MU_SENDMAIL_PRIO,
  MU_SENDMAIL_SCHEME,
  MU_RECORD_DEFAULT,
  MU_URL_SCHEME | MU_URL_PATH,
  MU_URL_SCHEME, /* Nothing is required in the URL, except scheme. 
                    Missing path means using PATH_SENDMAIL. */
  _url_sendmail_init,    /* url init.  */
  _mu_mailer_mailbox_init,     /* Mailbox entry.  */
  _mu_mailer_sendmail_init, /* Mailer entry.  */
  _mu_mailer_folder_init, /* Folder entry.  */
  NULL, /* No need for a back pointer.  */
  NULL, /* _is_scheme method.  */
  NULL, /* _get_url method.  */
  NULL, /* _get_mailbox method.  */
  NULL, /* _get_mailer method.  */
  NULL  /* _get_folder method.  */
};
/* We export, url parsing and the initialization of
   the mailbox, via the register entry/record.  */
mu_record_t mu_sendmail_record = &_sendmail_record;

#else
#include <stdio.h>
#include <mailutils/sys/registrar.h>
mu_record_t mu_sendmail_record = NULL;
#endif
