/*
 * msmtp.c
 *
 * This file is part of msmtp, an SMTP client.
 *
 * Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007
 * Martin Lambers <marlam@marlam.de>
 *
 *   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, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <getopt.h>
extern char *optarg;
extern int optind;
#include <unistd.h>
#include <sysexits.h>
#ifdef ENABLE_NLS
# include <locale.h>
#endif
#ifdef HAVE_SYSLOG
# include <syslog.h>
#endif
#ifdef W32_NATIVE
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <winsock2.h>
#elif defined DJGPP
#include <io.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>
#else /* UNIX */
#include <netdb.h>
#include <arpa/inet.h>
#endif

#include "getpass.h"
#include "gettext.h"
#include "xalloc.h"
#include "xvasprintf.h"

#include "conf.h"
#include "list.h"
#include "net.h"
#include "netrc.h"
#include "smtp.h"
#include "tools.h"
#ifdef HAVE_TLS
# include "tls.h"
#endif /* HAVE_TLS */

/* Default file names. */
#ifdef W32_NATIVE
#define SYSCONFFILE	"msmtprc.txt"
#define USERCONFFILE	"msmtprc.txt"
#define NETRCFILE	"netrc.txt"
#elif defined (DJGPP)
#define SYSCONFFILE	"msmtprc"
#define USERCONFFILE	"_msmtprc"
#define NETRCFILE	"_netrc"
#else /* UNIX */
#define SYSCONFFILE	"msmtprc"
#define USERCONFFILE	".msmtprc"
#define NETRCFILE	".netrc"
#endif

/* The name of this program */
const char *prgname;


/* 
 * Die if memory allocation fails
 */

void xalloc_die(void)
{
    fprintf(stderr, "%s: FATAL: %s", prgname, strerror(ENOMEM));
    exit(EX_OSERR);
}


/*
 * Translate error codes from net.h, tls.h or smtp.h 
 * to error codes from sysexits.h
 */

int exitcode_net(int net_error_code)
{
    switch (net_error_code)
    {
	case NET_EHOSTNOTFOUND:
	    return EX_NOHOST;

	case NET_ESOCKET:
	    return EX_OSERR;

	case NET_ECONNECT:
	    return EX_TEMPFAIL;	    

	case NET_EIO:
	    return EX_IOERR;
	    
	case NET_ELIBFAILED:
	default:
	    return EX_SOFTWARE;
    }
}

#ifdef HAVE_TLS
int exitcode_tls(int tls_error_code)
{
    switch (tls_error_code)
    {
	case TLS_EIO:
	    return EX_IOERR;

	case TLS_EFILE:
	    return EX_NOINPUT;

	case TLS_EHANDSHAKE:
	    return EX_PROTOCOL;

	case TLS_ECERT:
	    /* did not find anything better... */
	    return EX_UNAVAILABLE;

	case TLS_ELIBFAILED:
	case TLS_ESEED:
	default:
	    return EX_SOFTWARE;
    }
}
#endif /* HAVE_TLS */

int exitcode_smtp(int smtp_error_code)
{
    switch (smtp_error_code)
    {
	case SMTP_EIO:
	    return EX_IOERR;

	case SMTP_EPROTO:
	    return EX_PROTOCOL;

	case SMTP_EINVAL:
	    return EX_DATAERR;
	    
	case SMTP_EAUTHFAIL:
	    return EX_NOPERM;

	case SMTP_EINSECURE:
	case SMTP_EUNAVAIL:
	    return EX_UNAVAILABLE;

	case SMTP_ELIBFAILED:
	default:
	    return EX_SOFTWARE;
    }
}


/* 
 * Return the name of a sysexits.h exitcode
 */
const char *exitcode_to_string(int exitcode)
{
    switch (exitcode)
    {
	case EX_OK:
	    return "EX_OK";

	case EX_USAGE:
	    return "EX_USAGE";
	    
	case EX_DATAERR:
	    return "EX_DATAERR";

	case EX_NOINPUT:
	    return "EX_NOINPUT";

	case EX_NOUSER:
	    return "EX_NOUSER";

	case EX_NOHOST:
	    return "EX_NOHOST";

	case EX_UNAVAILABLE:
	    return "EX_UNAVAILABLE";

	case EX_SOFTWARE:
	    return "EX_SOFTWARE";

	case EX_OSERR:
	    return "EX_OSERR";

	case EX_OSFILE:
	    return "EX_OSFILE";
	    
	case EX_CANTCREAT:
	    return "EX_CANTCREAT";
	    
	case EX_IOERR:
	    return "EX_IOERR";

	case EX_TEMPFAIL:
	    return "EX_TEMPFAIL";
	    
	case EX_PROTOCOL:
	    return "EX_PROTOCOL";

	case EX_NOPERM:
	    return "EX_NOPERM";

	case EX_CONFIG:
	    return "EX_CONFIG";

	default:
	    return "BUG:UNKNOWN";
    }
}


/* 
 * msmtp_sanitize_string()
 *
 * Replaces all control characters in the string with a question mark
 */

char *msmtp_sanitize_string(char *str)
{
    char *p = str;
    
    while (*p != '\0')
    {
	if (iscntrl((unsigned char)*p))
	{
    	    *p = '?';
	}
	p++;
    }

    return str;
}


/* 
 * msmtp_password_callback()
 *
 * This function will be called by smtp_auth() to get a password if none was
 * given. It tries to read a password from .netrc, and if that fails reads a 
 * password with getpass().
 * It must return NULL on failure or a password in an allocated buffer.
 */

char *msmtp_password_callback(const char *hostname, const char *user)
{
    char *homedir;
    char *netrc_filename;
    netrc_entry *netrc_hostlist;
    netrc_entry *netrc_host;
    char *prompt;
    char *gpw;
    char *password = NULL;

    homedir = get_homedir();
    netrc_filename = get_filename(homedir, NETRCFILE);
    free(homedir);
    if ((netrc_hostlist = parse_netrc(netrc_filename)))
    {
	if ((netrc_host = search_netrc(netrc_hostlist, hostname, user)))
	{
	    password = xstrdup(netrc_host->password);
	}
	free_netrc_entry_list(netrc_hostlist);
    }
    free(netrc_filename);
    
    if (!password)
    {
	prompt = xasprintf(_("password for %s at %s: "), user, hostname);
	gpw = getpass(prompt);
	free(prompt);
	if (gpw)
	{
	    password = xstrdup(gpw);
	}
    }
    
    return password;
}


/*
 * msmtp_print_tls_cert_info()
 *
 * Prints information about a TLS certificate.
 */

#ifdef HAVE_TLS
/* Convert the given time into a string. */
void msmtp_time_to_string(time_t *t, char *buf, size_t bufsize)
{
#ifdef ENABLE_NLS
    (void)strftime(buf, bufsize, "%c", localtime(t));
#else
    char *p;
    
    (void)snprintf(buf, bufsize, "%s", ctime(t));
    if ((p = strchr(buf, '\n')))
    {
	*p = '\0';
    }
#endif
}

void msmtp_print_tls_cert_info(tls_cert_info_t *tci)
{
    const char *info_fieldname[6] = { N_("Common Name"), N_("Organization"), 
	N_("Organizational unit"), N_("Locality"), N_("State or Province"), 
	N_("Country") };
    char hex[] = "0123456789ABCDEF";
    char sha1_fingerprint_string[60];
    char md5_fingerprint_string[48];
    char timebuf[128];		/* should be long enough for every locale */
    char *tmp;
    int i;
    
    for (i = 0; i < 20; i++)
    {
	sha1_fingerprint_string[3 * i] = 
	    hex[(tci->sha1_fingerprint[i] & 0xf0) >> 4];
	sha1_fingerprint_string[3 * i + 1] = 
	    hex[tci->sha1_fingerprint[i] & 0x0f];
	sha1_fingerprint_string[3 * i + 2] = ':';
    }
    sha1_fingerprint_string[59] = '\0';
    for (i = 0; i < 16; i++)
    {
	md5_fingerprint_string[3 * i] = 
	    hex[(tci->md5_fingerprint[i] & 0xf0) >> 4];
	md5_fingerprint_string[3 * i + 1] = 
	    hex[tci->md5_fingerprint[i] & 0x0f];
	md5_fingerprint_string[3 * i + 2] = ':';
    }
    md5_fingerprint_string[47] = '\0';
    
    printf(_("TLS certificate information:\n"));
    printf("    %s:\n", _("Owner"));
    for (i = 0; i < 6; i++)
    {
	if (tci->owner_info[i])
	{
	    tmp = xstrdup(tci->owner_info[i]);
	    printf("        %s: %s\n", gettext(info_fieldname[i]), 
		    msmtp_sanitize_string(tmp));
	    free(tmp);
	}
    }
    printf("    %s:\n", _("Issuer"));
    for (i = 0; i < 6; i++)
    {
	if (tci->issuer_info[i])
	{
	    tmp = xstrdup(tci->issuer_info[i]);
	    printf("        %s: %s\n", gettext(info_fieldname[i]), 
		    msmtp_sanitize_string(tmp));
	    free(tmp);
	}
    }
    printf("    %s:\n", _("Validity"));
    msmtp_time_to_string(&tci->activation_time, timebuf, sizeof(timebuf));
    printf("        %s: %s\n", _("Activation time"), timebuf);
    msmtp_time_to_string(&tci->expiration_time, timebuf, sizeof(timebuf));
    printf("        %s: %s\n", _("Expiration time"), timebuf);
    printf("    %s:\n", _("Fingerprints"));
    printf("        SHA1: %s\n", sha1_fingerprint_string);
    printf("        MD5:  %s\n", md5_fingerprint_string);
}
#endif


/*
 * msmtp_endsession()
 *
 * Quit an SMTP session and close the connection.
 * QUIT is only sent when the flag 'quit' is set.
 */

void msmtp_endsession(smtp_server_t *srv, int quit)
{
    char *tmp_errstr;
    
    if (quit)
    {
	tmp_errstr = NULL;
	(void)smtp_quit(srv, &tmp_errstr);
	free(tmp_errstr);
    }
    smtp_close(srv);
}


/*
 * msmtp_rmqs()
 *
 * Sends an ETRN request to the SMTP server specified in the account 'acc'.
 * If an error occured, '*errstr' points to an allocated string that describes
 * the error or is NULL, and '*msg' may contain the offending message from the
 * SMTP server (or be NULL).
 */

int msmtp_rmqs(account_t *acc, int debug, const char *rmqs_argument, 
	list_t **msg, char **errstr)
{
    smtp_server_t srv;
    int e;
#ifdef HAVE_TLS
    tls_cert_info_t *tci = NULL;
#endif /* HAVE_TLS */
    
    *errstr = NULL;
    *msg = NULL;
    
    /* create a new smtp_server_t */
    srv = smtp_new(debug ? stdout : NULL, acc->protocol);

    /* connect */
    if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout,
		    NULL, NULL, errstr)) != NET_EOK)
    {
	return exitcode_net(e);
    }

    /* prepare tls */
#ifdef HAVE_TLS
    if (acc->tls)
    {
	if ((e = smtp_tls_init(&srv, acc->tls_key_file, acc->tls_cert_file, 
			acc->tls_trust_file, acc->tls_force_sslv3, errstr)) 
		!= TLS_EOK)
	{
	    return exitcode_tls(e);
	}
    }
#endif /* HAVE_TLS */

    /* start tls for ssmtp servers */
#ifdef HAVE_TLS
    if (acc->tls && acc->tls_nostarttls)
    {
	if (debug)
	{
	    tci = tls_cert_info_new();
	}
	if ((e = smtp_tls(&srv, acc->host, acc->tls_nocertcheck, tci, errstr))
		!= TLS_EOK)
	{
	    if (debug)
	    {
		tls_cert_info_free(tci);
	    }
	    msmtp_endsession(&srv, 0);
	    return exitcode_tls(e);
	}
	if (debug)
	{
	    msmtp_print_tls_cert_info(tci);
	    tls_cert_info_free(tci);
	}
    }
#endif /* HAVE_TLS */

    /* get greeting */
    if ((e = smtp_get_greeting(&srv, msg, NULL, errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	return exitcode_smtp(e);
    }
		    
    /* initialize session */
    if ((e = smtp_init(&srv, acc->domain, msg, errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	return exitcode_smtp(e);
    }
    
    /* start tls for starttls servers */
#ifdef HAVE_TLS
    if (acc->tls && !acc->tls_nostarttls)
    {
	if (!(srv.cap.flags & SMTP_CAP_STARTTLS))
	{
	    *errstr = xasprintf(_("the server does not support TLS "
			"via the STARTTLS command"));
	    msmtp_endsession(&srv, 1);
	    return EX_UNAVAILABLE;
	}
	if ((e = smtp_tls_starttls(&srv, msg, errstr)) != SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    return exitcode_smtp(e);
	}
	if (debug)
	{
	    tci = tls_cert_info_new();
	}
	if ((e = smtp_tls(&srv, acc->host, acc->tls_nocertcheck, tci, errstr))
		!= TLS_EOK)
	{
	    if (debug)
	    {
		tls_cert_info_free(tci);
	    }
	    msmtp_endsession(&srv, 0);
	    return exitcode_tls(e);
	}
	if (debug)
	{
	    msmtp_print_tls_cert_info(tci);
	    tls_cert_info_free(tci);
	}
	/* initialize again */
	if ((e = smtp_init(&srv, acc->domain, msg, errstr)) != SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    return exitcode_smtp(e);
	}
    }
#endif /* HAVE_TLS */

    if (!(srv.cap.flags & SMTP_CAP_ETRN))
    {
	*errstr = xasprintf(_("the server does not support "
		    "Remote Message Queue Starting"));
	msmtp_endsession(&srv, 1);
	return EX_UNAVAILABLE;
    }

    /* authenticate */
    if (acc->auth_mech)
    {
	if (!(srv.cap.flags & SMTP_CAP_AUTH))
	{
	    *errstr = xasprintf(
		    _("the server does not support authentication"));
	    msmtp_endsession(&srv, 1);
	    return EX_UNAVAILABLE;
	}
	if ((e = smtp_auth(&srv, acc->host, acc->username, acc->password, 
			acc->ntlmdomain, acc->auth_mech, 
			msmtp_password_callback, msg, errstr)) 
		!= SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    return exitcode_smtp(e);
	}
    }
    
    /* send the ETRN request */
    if ((e = smtp_etrn(&srv, rmqs_argument, msg, errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	return exitcode_smtp(e);
    }

    /* end session */
    msmtp_endsession(&srv, 1);
    return EX_OK;
}


/*
 * msmtp_serverinfo()
 *
 * Prints information about the SMTP server specified in the account 'acc'.
 * If an error occured, '*errstr' points to an allocated string that describes
 * the error or is NULL, and '*msg' may contain the offending message from the
 * SMTP server (or be NULL).
 */

int msmtp_serverinfo(account_t *acc, int debug, list_t **msg, char **errstr)
{
    smtp_server_t srv;
    char *server_canonical_name = NULL;
    char *server_address = NULL;
    char *server_greeting = NULL;
    int e;
#ifdef HAVE_TLS
    tls_cert_info_t *tci = NULL;
#endif /* HAVE_TLS */
    
    *errstr = NULL;
    *msg = NULL;
    
    /* create a new smtp_server_t */
    srv = smtp_new(debug ? stdout : NULL, acc->protocol);

    /* connect */
    if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout,
		    &server_canonical_name, &server_address, errstr))
	    != NET_EOK)
    {
	e = exitcode_net(e);
       	goto error_exit;
    }

    /* prepare tls */
#ifdef HAVE_TLS
    if (acc->tls)
    {
	tci = tls_cert_info_new();
	if ((e = smtp_tls_init(&srv, acc->tls_key_file, acc->tls_cert_file, 
			acc->tls_trust_file, acc->tls_force_sslv3, errstr)) 
		!= TLS_EOK)
	{
	    e = exitcode_tls(e);
	    goto error_exit;
	}
    }
#endif /* HAVE_TLS */

    /* start tls for ssmtp servers */
#ifdef HAVE_TLS
    if (acc->tls && acc->tls_nostarttls)
    {
	if ((e = smtp_tls(&srv, acc->host, acc->tls_nocertcheck, tci, errstr))
		!= TLS_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_tls(e);
	    goto error_exit;
	}
    }
#endif /* HAVE_TLS */

    /* get greeting */
    if ((e = smtp_get_greeting(&srv, msg, &server_greeting, 
		    errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	e = exitcode_smtp(e);
       	goto error_exit;
    }
		    
    /* initialize session */
    if ((e = smtp_init(&srv, acc->domain, msg, errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	e = exitcode_smtp(e);
       	goto error_exit;
    }
    
    /* start tls for starttls servers */
#ifdef HAVE_TLS
    if (acc->tls && !acc->tls_nostarttls)
    {
	if (!(srv.cap.flags & SMTP_CAP_STARTTLS))
	{
	    *errstr = xasprintf(_("the server does not support TLS "
			"via the STARTTLS command"));
	    msmtp_endsession(&srv, 1);
	    e = EX_UNAVAILABLE;
	    goto error_exit;
	}
	if ((e = smtp_tls_starttls(&srv, msg, errstr)) != SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
	if ((e = smtp_tls(&srv, acc->host, acc->tls_nocertcheck, tci, errstr))
		!= TLS_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_tls(e);
	    goto error_exit;
	}
	/* initialize again */
	if ((e = smtp_init(&srv, acc->domain, msg, errstr)) != SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
    }
#endif /* HAVE_TLS */

    /* end session */
    msmtp_endsession(&srv, 1);

    /* print results */
    if (server_canonical_name && server_address)
    {
	printf(_("%s server at %s (%s [%s]), port %d:\n"), 
		acc->protocol == SMTP_PROTO_SMTP ? "SMTP" : "LMTP", 
		acc->host, server_canonical_name, server_address, acc->port);
    }
    else if (server_canonical_name)
    {
	printf(_("%s server at %s (%s), port %d:\n"), 
		acc->protocol == SMTP_PROTO_SMTP ? "SMTP" : "LMTP", 
		acc->host, server_canonical_name, acc->port);
    }
    else if (server_address)
    {
	printf(_("%s server at %s ([%s]), port %d:\n"), 
		acc->protocol == SMTP_PROTO_SMTP ? "SMTP" : "LMTP", 
		acc->host, server_address, acc->port);
    }
    else
    {
	printf(_("%s server at %s, port %d:\n"), 
		acc->protocol == SMTP_PROTO_SMTP ? "SMTP" : "LMTP", 
		acc->host, acc->port);
    }
    if (*server_greeting != '\0')
    {
	printf("    %s\n", msmtp_sanitize_string(server_greeting));
    }
#ifdef HAVE_TLS
    if (acc->tls)
    {
	msmtp_print_tls_cert_info(tci);
    }
#endif /* HAVE_TLS */
#ifdef HAVE_TLS
    if (srv.cap.flags == 0 && !(acc->tls && !acc->tls_nostarttls))
#else /* not HAVE_TLS */
    if (srv.cap.flags == 0)
#endif /* not HAVE_TLS */
    {
	printf(_("No special capabilities.\n"));
    }
    else
    {
	printf(_("Capabilities:\n"));
	if (srv.cap.flags & SMTP_CAP_SIZE)
	{
	    printf("    SIZE %ld:\n        %s", srv.cap.size, 
		    _("Maximum message size is "));
	    if (srv.cap.size == 0)
	    {
		printf(_("unlimited\n"));
	    }
	    else
	    {
		printf(_("%ld bytes"), srv.cap.size);
		if (srv.cap.size > 1024 * 1024)
		{
		    printf(_(" = %.2f MB"), 
			    (float)srv.cap.size / (1024.0 * 1024.0));
		}
		else if (srv.cap.size > 1024)
		{
		    printf(_(" = %.2f KB"), (float)srv.cap.size / 1024.0);
		}
		printf("\n");
	    }
       	}
	if (srv.cap.flags & SMTP_CAP_PIPELINING)
	{
    	    printf("    PIPELINING:\n        %s\n", _("Support for command "
			"grouping for faster transmission"));
       	}
	if (srv.cap.flags & SMTP_CAP_ETRN)
	{
    	    printf("    ETRN:\n        %s\n", _("Support for RMQS "
			"(Remote Message Queue Starting)"));
       	}
	if (srv.cap.flags & SMTP_CAP_DSN)
	{
    	    printf("    DSN:\n        %s\n", _("Support for "
			"Delivery Status Notifications"));
       	}
#ifdef HAVE_TLS
	if ((acc->tls && !acc->tls_nostarttls) 
		|| (srv.cap.flags & SMTP_CAP_STARTTLS))
#else /* not HAVE_TLS */
        if (srv.cap.flags & SMTP_CAP_STARTTLS)
#endif /* not HAVE_TLS */
	{
    	    printf("    STARTTLS:\n        %s\n", _("Support for "
			"TLS encryption via the STARTTLS command"));
       	}
	if (srv.cap.flags & SMTP_CAP_AUTH)
	{
    	    printf("    AUTH:\n        %s\n        ", 
		    _("Supported authentication methods:"));
    	    if (srv.cap.flags & SMTP_CAP_AUTH_PLAIN)
    	    {
    		printf("PLAIN ");
    	    }
    	    if (srv.cap.flags & SMTP_CAP_AUTH_CRAM_MD5)
    	    {
    		printf("CRAM-MD5 ");
    	    }
    	    if (srv.cap.flags & SMTP_CAP_AUTH_DIGEST_MD5)
    	    {
    		printf("DIGEST-MD5 ");
    	    }
    	    if (srv.cap.flags & SMTP_CAP_AUTH_GSSAPI)
    	    {
    		printf("GSSAPI ");
    	    }
    	    if (srv.cap.flags & SMTP_CAP_AUTH_EXTERNAL)
    	    {
    		printf("EXTERNAL ");
    	    }
    	    if (srv.cap.flags & SMTP_CAP_AUTH_LOGIN)
    	    {
    		printf("LOGIN ");
    	    }
    	    if (srv.cap.flags & SMTP_CAP_AUTH_NTLM)
    	    {
    		printf("NTLM ");
    	    }
	    printf("\n");
       	}
#ifdef HAVE_TLS
	if ((srv.cap.flags & SMTP_CAP_STARTTLS) && !acc->tls)
#else /* not HAVE_TLS */
	if (srv.cap.flags & SMTP_CAP_STARTTLS)
#endif /* not HAVE_TLS */
	{
    	    printf(_("This server might advertise more or other "
		    "capabilities when TLS is active.\n"));
       	}
    }

    e = EX_OK;
    
error_exit:
    free(server_canonical_name);
    free(server_address);
#ifdef HAVE_TLS
    if (tci)
    {
	tls_cert_info_free(tci);
    }
#endif /* HAVE_TLS */
    free(server_greeting);
    return e;
}


/*
 * msmtp_read_recipients()
 *
 * Copies the headers of the mail from 'mailf' to a temporary file '*tmpfile',
 * including the blank line that separates the header from the body of the mail.
 * Extracts all recipients from the To, Cc, and Bcc headers and adds them
 * to 'recipients'.
 * This function rewinds '*tmpfile' after writing the headers to it.
 * 
 * See RFC2822, section 3 for the format of these headers.
 * 
 * Return codes: EX_OK, EX_IOERR
 */

#define STATE_LINESTART_FRESH		0	/* a new line started; the 
						   previous line was not a 
						   recipient header */
#define STATE_LINESTART_AFTER_RCPTHDR	1	/* a new line started; the 
						   previous line was a 
						   recipient header */
#define STATE_OTHER_HDR			2	/* a header we don't 
						   care about */
#define STATE_TO			3	/* we saw "^T" */
#define STATE_CC			4	/* we saw "^C" */
#define STATE_BCC1			5	/* we saw "^B" */
#define STATE_BCC2			6	/* we saw "^Bc" */
#define STATE_RCPTHDR_ALMOST		7	/* we saw "^To", "^Cc" 
						   or "^Bcc" */
#define STATE_RCPTHDR_DEFAULT		8	/* in_rcpt_hdr and in_rcpt 
						   state our position */
#define STATE_RCPTHDR_DQUOTE		9	/* duoble quotes */
#define STATE_RCPTHDR_BRACKETS_START	10	/* entering <...> */
#define STATE_RCPTHDR_IN_BRACKETS	11	/* an address inside <> */
#define STATE_RCPTHDR_PARENTH_START	12	/* entering (...) */
#define STATE_RCPTHDR_IN_PARENTH	13	/* a comment inside () */
#define STATE_RCPTHDR_IN_ADDRESS	14	/* a bare address */
#define STATE_RCPTHDR_BACKQUOTE		15	/* we saw a '\\' */
#define STATE_HEADERS_END		16	/* we saw "^$", the blank line 
						   between headers and body */

int msmtp_read_recipients(FILE *mailf, list_t *recipients, FILE **tmpfile, 
	char **errstr)
{
    int c;
    int state = STATE_LINESTART_FRESH;
    int oldstate = STATE_LINESTART_FRESH;
    int backquote_savestate = STATE_LINESTART_FRESH;
    int parentheses_depth = 0;
    int parentheses_savestate = STATE_LINESTART_FRESH;
    int folded_rcpthdr_savestate = STATE_LINESTART_FRESH;
    char *current_recipient = NULL;
    size_t current_recipient_len = 0;
    int forget_current_recipient = 0;
    int finish_current_recipient = 0;
    size_t bufsize = 0;
    /* The buffer that is filled with the current recipient grows by 
     * 'bufsize_step' if the remaining space becomes too small. This value must
     * be at least 2. Wasted characters are at most (bufsize_step - 1). A value 
     * of 10 means low wasted space and a low number of realloc()s per 
     * recipient. */
    const size_t bufsize_step = 10; 
    

    if (!(*tmpfile = tempfile(PACKAGE_NAME)))
    {
	*errstr = xasprintf(_("cannot create temporary file: %s"), 
		strerror(errno));
	goto error_exit;
    }
    
    for (;;)
    {
	c = fgetc(mailf);
	/* Convert CRLF to LF. According to RFC 2822, CRs may only occur in a
	 * mail when they are followed by LF, so just ignoring CRs is ok. */
	if (c == '\r')
	{
	    continue;
	}
	oldstate = state;
	if (c == EOF)
	{
	    state = STATE_HEADERS_END;
	    if (current_recipient)
		finish_current_recipient = 1;
	}
	else
	{
	    switch (state)
	    {
		case STATE_LINESTART_FRESH:
		    parentheses_depth = 0;
		    if (c == 't' || c == 'T')
			state = STATE_TO;
		    else if (c == 'c' || c == 'C')
			state = STATE_CC;
		    else if (c == 'b' || c == 'B')
			state = STATE_BCC1;
		    else if (c == '\n')
			state = STATE_HEADERS_END;
		    else
			state = STATE_OTHER_HDR;
		    break;

		case STATE_LINESTART_AFTER_RCPTHDR:
		    if (c != ' ' && c != '\t' && current_recipient)
			finish_current_recipient = 1;
		    if (c == ' ' || c == '\t')
			state = folded_rcpthdr_savestate;
		    else if (c == 't' || c == 'T')
			state = STATE_TO;
		    else if (c == 'c' || c == 'C')
			state = STATE_CC;
		    else if (c == 'b' || c == 'B')
			state = STATE_BCC1;
		    else if (c == '\n')
			state = STATE_HEADERS_END;
		    else
			state = STATE_OTHER_HDR;
		    break;

		case STATE_OTHER_HDR:
		    if (c == '\n')
			state = STATE_LINESTART_FRESH;
		    break;

		case STATE_TO:
	    	    if (c == 'o' || c == 'O')
	    		state = STATE_RCPTHDR_ALMOST;
	    	    else if (c == '\n')
	    		state = STATE_LINESTART_FRESH;
	    	    else
	    		state = STATE_OTHER_HDR;
	    	    break;

		case STATE_CC:
		    if (c == 'c' || c == 'C')
			state = STATE_RCPTHDR_ALMOST;
		    else if (c == '\n')
			state = STATE_LINESTART_FRESH;
		    else
			state = STATE_OTHER_HDR;
		    break;

		case STATE_BCC1:
		    if (c == 'c' || c == 'C')
			state = STATE_BCC2;
		    else if (c == '\n')
			state = STATE_LINESTART_FRESH;
		    else
			state = STATE_OTHER_HDR;
		    break;

		case STATE_BCC2:
		    if (c == 'c' || c == 'C')
			state = STATE_RCPTHDR_ALMOST;
		    else if (c == '\n')
			state = STATE_LINESTART_FRESH;
		    else
			state = STATE_OTHER_HDR;
		    break;

		case STATE_RCPTHDR_ALMOST:
		    if (c == ':')
			state = STATE_RCPTHDR_DEFAULT;
		    else if (c == '\n')
			state = STATE_LINESTART_FRESH;
		    else
			state = STATE_OTHER_HDR;
		    break;

		case STATE_RCPTHDR_DEFAULT:
	    	    if (c == '\n')
	    	    {
	    		if (current_recipient)
	    		    finish_current_recipient = 1;
	    		folded_rcpthdr_savestate = state;
	    		state = STATE_LINESTART_AFTER_RCPTHDR;
	    	    }
	    	    else if (c == '\\')
	    	    {
	    		backquote_savestate = state;
	    		state = STATE_RCPTHDR_BACKQUOTE;
	    	    }
	    	    else if (c == '(')
	    	    {
	    		parentheses_savestate = state;
	    		state = STATE_RCPTHDR_PARENTH_START;
	    	    }
	    	    else if (c == '"')
	    	    {
	    		if (current_recipient)
	    		    forget_current_recipient = 1;
	    		state = STATE_RCPTHDR_DQUOTE;
	    	    }
	    	    else if (c == '<')
	    	    {
	    		if (current_recipient)
	    		    forget_current_recipient = 1;
	    		state = STATE_RCPTHDR_BRACKETS_START;
	    	    }
	    	    else if (c == ' ' || c == '\t')
	    		; /* keep state */
	    	    else if (c == ':')
	    	    {
	    		if (current_recipient)
	    		    forget_current_recipient = 1;
	    	    }
	    	    else if (c == ';' || c == ',')
	    	    {
	    		if (current_recipient)
	    		    finish_current_recipient = 1;
	    	    }
	    	    else
	    	    {
	    		if (current_recipient)
	    		    forget_current_recipient = 1;
	    		state = STATE_RCPTHDR_IN_ADDRESS;
	    	    }
	    	    break;

		case STATE_RCPTHDR_DQUOTE:
	    	    if (c == '\n')
	    	    {
	    		folded_rcpthdr_savestate = state;
	    		state = STATE_LINESTART_AFTER_RCPTHDR;
	    	    }
	    	    else if (c == '\\')
	    	    {
	    		backquote_savestate = state;
	    		state = STATE_RCPTHDR_BACKQUOTE;
	    	    }
	    	    else if (c == '"')
	    		state = STATE_RCPTHDR_DEFAULT;
	    	    break;

		case STATE_RCPTHDR_BRACKETS_START:
		    if (c == '\n')
		    {
		       	folded_rcpthdr_savestate = state;
			state = STATE_LINESTART_AFTER_RCPTHDR;
		    }
		    else if (c == '(')
		    {
		       	parentheses_savestate = state;
			state = STATE_RCPTHDR_PARENTH_START;
		    }
		    else if (c == '>')
			state = STATE_RCPTHDR_DEFAULT;
		    else
			state = STATE_RCPTHDR_IN_BRACKETS;
		    break;

		case STATE_RCPTHDR_IN_BRACKETS:
	    	    if (c == '\n')
	    	    {
	    		folded_rcpthdr_savestate = state;
	    		state = STATE_LINESTART_AFTER_RCPTHDR;
	    	    }
	    	    else if (c == '\\')
	    	    {
	    		backquote_savestate = state;
	    		state = STATE_RCPTHDR_BACKQUOTE;
	    	    }
	    	    else if (c == '(')
	    	    {
	    		parentheses_savestate = state;
	    		state = STATE_RCPTHDR_PARENTH_START;
	    	    }
	    	    else if (c == '>')
	    	    {
	    		finish_current_recipient = 1;
	    		state = STATE_RCPTHDR_DEFAULT;
	    	    }
	    	    break;

		case STATE_RCPTHDR_PARENTH_START:
	    	    if (c == '\n')
	    	    {
	    		folded_rcpthdr_savestate = state;
	    		state = STATE_LINESTART_AFTER_RCPTHDR;
	    	    }
	    	    else if (c == ')')
	    		state = parentheses_savestate;
	    	    else
	    	    {
	    		parentheses_depth++;
	    		state = STATE_RCPTHDR_IN_PARENTH;
	    	    }
	    	    break;

		case STATE_RCPTHDR_IN_PARENTH:
	    	    if (c == '\n')
	    	    {
	    		folded_rcpthdr_savestate = state;
	    		state = STATE_LINESTART_AFTER_RCPTHDR;
	    	    }
	    	    else if (c == '\\')
	    	    {
	    		backquote_savestate = state;
	    		state = STATE_RCPTHDR_BACKQUOTE;
	    	    }
	    	    else if (c == '(')
	    		state = STATE_RCPTHDR_PARENTH_START;
	    	    else if (c == ')')
	    	    {
	    		parentheses_depth--;
	    		if (parentheses_depth == 0)
	    		    state = parentheses_savestate;
	    	    }
	    	    break;

		case STATE_RCPTHDR_IN_ADDRESS:
	    	    if (c == '\n')
	    	    {
	    		folded_rcpthdr_savestate = STATE_RCPTHDR_DEFAULT;
	    		state = STATE_LINESTART_AFTER_RCPTHDR;
	    	    }
	    	    else if (c == '\\')
	    	    {
	    		backquote_savestate = state;
	    		state = STATE_RCPTHDR_BACKQUOTE;
	    	    }
	    	    else if (c == '"')
	    	    {
	    		forget_current_recipient = 1;
	    		state = STATE_RCPTHDR_DQUOTE;
	    	    }
	    	    else if (c == '(')
	    	    {
	    		parentheses_savestate = state;
	    		state = STATE_RCPTHDR_PARENTH_START;
	    	    }
	    	    else if (c == '<')
	    	    {
	    		forget_current_recipient = 1;
	    		state = STATE_RCPTHDR_BRACKETS_START;
	    	    }
	    	    else if (c == ' ' || c == '\t')
	    		state = STATE_RCPTHDR_DEFAULT;
	    	    else if (c == ':')
	    	    {
	    		forget_current_recipient = 1;
	    		state = STATE_RCPTHDR_DEFAULT;
	    	    }
	    	    else if (c == ',' || c == ';')
	    	    {
	    		finish_current_recipient = 1;
	    		state = STATE_RCPTHDR_DEFAULT;
	    	    }
	    	    break;

		case STATE_RCPTHDR_BACKQUOTE:
		    if (c == '\n')
		    {
		       	folded_rcpthdr_savestate = STATE_RCPTHDR_DEFAULT;
			state = STATE_LINESTART_AFTER_RCPTHDR;
		    }
		    else
			state = backquote_savestate;
		    break;
	    }
	}

    	if (c != EOF && fputc(c, *tmpfile) == EOF)
	{
	    *errstr = xasprintf(_("cannot write mail headers to temporary "
			"file: output error"));
	    goto error_exit;
	}
	
    	if (forget_current_recipient)
	{
	    /* this was just junk */
	    free(current_recipient);
	    current_recipient = NULL;
	    current_recipient_len = 0;
	    bufsize = 0;
	    forget_current_recipient = 0;
	}
	if (finish_current_recipient)
	{
	    /* The current recipient just ended. Add it to the list */
	    current_recipient[current_recipient_len] = '\0';
	    list_insert(recipients, current_recipient);
	    recipients = recipients->next;
	    /* Reset for the next recipient */
	    current_recipient = NULL;
	    current_recipient_len = 0;
	    bufsize = 0;
	    finish_current_recipient = 0;
	}
	if ((state == STATE_RCPTHDR_IN_ADDRESS 
		    || state == STATE_RCPTHDR_IN_BRACKETS)
		&& oldstate != STATE_RCPTHDR_PARENTH_START
		&& oldstate != STATE_RCPTHDR_IN_PARENTH
		&& oldstate != STATE_LINESTART_AFTER_RCPTHDR)
	{
    	    /* Add this character to the current recipient */
	    current_recipient_len++;
	    if (bufsize < current_recipient_len + 1)
	    {
		bufsize += bufsize_step;
		current_recipient = xrealloc(current_recipient,
			bufsize * sizeof(char));
	    }
	    /* sanitize characters */
	    if (!iscntrl((unsigned char)c) && !isspace((unsigned char)c))
	    {
		current_recipient[current_recipient_len - 1] = (char)c;
	    }
	    else
	    {
		current_recipient[current_recipient_len - 1] = '_';
	    }
	}
	
	if (state == STATE_HEADERS_END)
	{
	    break;
	}
    }
    
    if (ferror(mailf))
    {
	*errstr = xasprintf(_("input error while reading the mail"));
	goto error_exit;
    }
    
    if (fseek(*tmpfile, 0L, SEEK_SET) != 0)
    {
	*errstr = xasprintf(_("cannot rewind temporary file: %s"), 
		strerror(errno));
	goto error_exit;
    }

    return EX_OK;

error_exit:
    if (*tmpfile)
    {
	(void)fclose(*tmpfile);
	*tmpfile = NULL;
    }
    free(current_recipient);
    return EX_IOERR;
}

 
/*
 * msmtp_sendmail()
 *
 * Sends a mail. Returns a value from sysexits.h.
 * If 'read_recipients' is true, recipients are extracted from the To, Cc,
 * and Bcc headers *in addition* to the recipients in 'recipients'. 
 * If an error occured, '*errstr' points to an allocated string that describes
 * the error or is NULL, and '*msg' may contain the offending message from the
 * SMTP server (or be NULL).
 * In case of success, 'mailsize' contains the number of bytes of the mail 
 * transferred to the SMTP server. In case of failure, its contents are
 * undefined.
 */

int msmtp_sendmail(account_t *acc, list_t *recipients, int read_recipients, 
	FILE *f, int debug, long *mailsize, 
	list_t **lmtp_errstrs, list_t **lmtp_error_msgs,
	list_t **msg, char **errstr)
{
    smtp_server_t srv;
    FILE *tmpfile = NULL;
    int e;
#ifdef HAVE_TLS
    tls_cert_info_t *tci = NULL;
#endif /* HAVE_TLS */
    
    *errstr = NULL;
    *msg = NULL;
    *lmtp_errstrs = NULL;
    *lmtp_error_msgs = NULL;
    
    /* Read recipients from the mail as soon as possible. Important for 
     * error reporting/logging. */
    if (read_recipients)
    {
	if ((e = msmtp_read_recipients(f, list_last(recipients), &tmpfile, 
			errstr)) != EX_OK)
	{
	    goto error_exit;
	}
	if (list_is_empty(recipients))
	{
	    *errstr = xasprintf(_("no recipients found"));
	    e = EX_DATAERR;
	    goto error_exit;
	}
    }
    /* create a new smtp_server_t */
    srv = smtp_new(debug ? stdout : NULL, acc->protocol);

    /* prepare tls */
#ifdef HAVE_TLS
    if (acc->tls)
    {
	if ((e = smtp_tls_init(&srv, acc->tls_key_file, acc->tls_cert_file, 
			acc->tls_trust_file, acc->tls_force_sslv3, errstr)) 
		!= TLS_EOK)
	{
	    e = exitcode_tls(e);
	    goto error_exit;
	}
    }
#endif /* HAVE_TLS */

    /* connect */
    if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout,
		    NULL, NULL, errstr)) != NET_EOK)
    {
	e = exitcode_net(e);
	goto error_exit;
    }

    /* start tls for ssmtp servers */
#ifdef HAVE_TLS
    if (acc->tls && acc->tls_nostarttls)
    {
	if (debug)
	{
	    tci = tls_cert_info_new();
	}
	if ((e = smtp_tls(&srv, acc->host, acc->tls_nocertcheck, tci, errstr))
		!= TLS_EOK)
	{
	    if (debug)
	    {
		tls_cert_info_free(tci);
	    }
	    msmtp_endsession(&srv, 0);
	    e = exitcode_tls(e);
	    goto error_exit;
	}
	if (debug)
	{
	    msmtp_print_tls_cert_info(tci);
	    tls_cert_info_free(tci);
	}
    }
#endif /* HAVE_TLS */

    /* get greeting */
    if ((e = smtp_get_greeting(&srv, msg, NULL, errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	e = exitcode_smtp(e);
	goto error_exit;
    }
		    
    /* initialize session */
    if ((e = smtp_init(&srv, acc->domain, msg, errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	e = exitcode_smtp(e);
	goto error_exit;
    }

    /* start tls for starttls servers */
#ifdef HAVE_TLS
    if (acc->tls && !acc->tls_nostarttls)
    {
	if (!(srv.cap.flags & SMTP_CAP_STARTTLS))
	{
	    *errstr = xasprintf(_("the server does not support TLS "
			"via the STARTTLS command"));
	    msmtp_endsession(&srv, 1);
	    e = EX_UNAVAILABLE;
	    goto error_exit;
	}
	if ((e = smtp_tls_starttls(&srv, msg, errstr)) != SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
	if (debug)
	{
	    tci = tls_cert_info_new();
	}
	if ((e = smtp_tls(&srv, acc->host, acc->tls_nocertcheck, tci, errstr))
		!= TLS_EOK)
	{
	    if (debug)
	    {
		tls_cert_info_free(tci);
	    }
	    msmtp_endsession(&srv, 0);
	    e = exitcode_tls(e);
	    goto error_exit;
	}
	if (debug)
	{
	    msmtp_print_tls_cert_info(tci);
	    tls_cert_info_free(tci);
	}
	/* initialize again */
	if ((e = smtp_init(&srv, acc->domain, msg, errstr)) != SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
    }
#endif /* HAVE_TLS */

    /* test for needed features */
    if ((acc->dsn_return || acc->dsn_notify) && !(srv.cap.flags & SMTP_CAP_DSN))
    {
	*errstr = xasprintf(_("the server does not support DSN"));
	msmtp_endsession(&srv, 1);
	e = EX_UNAVAILABLE;
	goto error_exit;
    }
    /* authenticate */
    if (acc->auth_mech)
    {
	if (!(srv.cap.flags & SMTP_CAP_AUTH))
	{
	    *errstr = xasprintf(
		    _("the server does not support authentication"));
	    msmtp_endsession(&srv, 1);
	    e = EX_UNAVAILABLE;
	    goto error_exit;
	}
	if ((e = smtp_auth(&srv, acc->host, acc->username, acc->password, 
			acc->ntlmdomain, acc->auth_mech, 
			msmtp_password_callback, msg, errstr)) 
		!= SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
    }
    
    /* send the envelope */
    if ((e = smtp_send_envelope(&srv, acc->from, recipients, 
		    acc->dsn_notify, acc->dsn_return, msg, errstr)) != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	e = exitcode_smtp(e);
	goto error_exit;
    }
    /* send header and body */
    *mailsize = 0;
    if (read_recipients)
    {
	/* first the headers from the temp file */
	if ((e = smtp_send_mail(&srv, tmpfile, acc->keepbcc, mailsize, errstr))
	     	!= SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
	(void)fclose(tmpfile);
	tmpfile = NULL;
	/* then the body from the original file */
	if ((e = smtp_send_mail(&srv, f, 1, mailsize, errstr)) != SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
    }
    else
    {
	if ((e = smtp_send_mail(&srv, f, acc->keepbcc, mailsize, errstr)) 
	     	!= SMTP_EOK)
	{
	    msmtp_endsession(&srv, 0);
	    e = exitcode_smtp(e);
	    goto error_exit;
	}
    }
    /* end the mail */
    if (acc->protocol == SMTP_PROTO_SMTP)
    {
	e = smtp_end_mail(&srv, msg, errstr);
    }
    else
    {
	e = smtp_end_mail_lmtp(&srv, recipients, 
		lmtp_errstrs, lmtp_error_msgs, errstr);
    }
    if (e != SMTP_EOK)
    {
	msmtp_endsession(&srv, 0);
	e = exitcode_smtp(e);
	goto error_exit;
    }

    /* end session */
    msmtp_endsession(&srv, 1);

    e = EX_OK;

error_exit:
    if (tmpfile)
    {
	(void)fclose(tmpfile);
    }
    return e;
}


/*
 * print_error()
 *
 * Print an error message
 */

/* make gcc print format warnings for this function */
#ifdef __GNUC__
void print_error(const char *format, ...) 
    __attribute__ ((format (printf, 1, 2)));
#endif

void print_error(const char *format, ...)
{
    va_list args;
    fprintf(stderr, "%s: ", prgname);
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, "\n");
}


/*
 * msmtp_get_log_info()
 *
 * Gather log information for syslog or logfile and put it in a string:
 * - host=%s
 * - tls=on|off
 * - auth=on|off
 * - user=%s (only if auth == on and username != NULL)
 * - from=%s
 * - recipients=%s,%s,... 
 * - mailsize=%s (only if exitcode == EX_OK)
 * - smtpstatus=%s (only if exitcode != EX_OK and a smtp msg is available)
 * - smtpmsg='%s' (only if exitcode != EX_OK and a smtp msg is available)
 * - errormsg='%s' (only if exitcode != EX_OK and an error msg is available)
 * - exitcode=%s 
 * 'exitcode' must be one of the sysexits.h exitcodes.
 * This function cannot fail.
 */

char *msmtp_get_log_info(account_t *acc, list_t *recipients, long mailsize,
	list_t *errmsg, char *errstr, int exitcode)
{
    int i;
    size_t s;
    list_t *l;
    char *line;
    int n;
    char *p;
    char *tmp;
    /* temporary strings: */
    char *mailsize_str = NULL;
    const char *exitcode_str;
    char *smtpstatus_str = NULL;
    char *smtperrmsg_str = NULL;
    
    
    /* gather information */
    
    line = NULL;
    /* mailsize */
    if (exitcode == EX_OK)
    {
	mailsize_str = xasprintf("%ld", mailsize);
    }	
    /* exitcode */
    exitcode_str = exitcode_to_string(exitcode);
    /* smtp status and smtp error message */
    if (exitcode != EX_OK && errmsg)
    {
	smtpstatus_str = xasprintf("%d", smtp_msg_status(errmsg));
	l = errmsg;
	s = 0;
	while (!list_is_empty(l))
	{
	    l = l->next;
	    s += strlen(l->data) + 2;
	}
	s += 1;
	smtperrmsg_str = xmalloc(s * sizeof(char));
	smtperrmsg_str[0] = '\'';
	i = 1;
	l = errmsg;
	while (!list_is_empty(l))
	{
	    l = l->next;
	    p = msmtp_sanitize_string(l->data);
	    while (*p != '\0')
	    {
		/* hide single quotes to make the info easy to parse */
		smtperrmsg_str[i] = (*p == '\'') ? '?' : *p;
		p++;
		i++;
	    }
	    smtperrmsg_str[i++] = '\\';
	    smtperrmsg_str[i++] = 'n';
	}
	i -= 2;
	smtperrmsg_str[i++] = '\'';
	smtperrmsg_str[i++] = '\0';
    }
    
    /* calculate the length of the log line */
    
    s = 0;
    /* "host=%s " */
    s += 5 + strlen(acc->host) + 1;
    /* "tls=on|off " */
    s += 4 + (acc->tls ? 2 : 3) + 1;
    /* "auth=on|off " */
    s += 5 + (acc->auth_mech ? 2 : 3) + 1;
    /* "user=%s " */
    if (acc->auth_mech && acc->username)
    {
	s += 5 + strlen(acc->username) + 1;
    }
    /* "from=%s " */
    s += 5 + strlen(acc->from) + 1;
    /* "recipients=%s,%s,... " */
    s += 11;
    l = recipients;
    while (!list_is_empty(l))
    {
	l = l->next;
	s += strlen(l->data) + 1;
    }
    /* "mailsize=%s " */
    if (exitcode == EX_OK)
    {
	s += 9 + strlen(mailsize_str) + 1;
    }
    /* "smtpstatus=%s smtpmsg=%s " */
    if (exitcode != EX_OK && errmsg)
    {
	s += 11 + strlen(smtpstatus_str) + 1 + 8 + strlen(smtperrmsg_str) + 1;
    }
    /* "errormsg='%s' */
    if (exitcode != EX_OK && errstr[0] != '\0')
    {
	s += 10 + strlen(errstr) + 2;
    }
    /* "exitcode=%s" */
    s += 9 + strlen(exitcode_str);
    /* '\0' */
    s++;
    
    line = xmalloc(s * sizeof(char));
    
    /* build the log line */
    
    p = line;
    n = snprintf(p, s, "host=%s tls=%s auth=%s ",
	    acc->host, (acc->tls ? "on" : "off"), 
	    (acc->auth_mech ? "on" : "off"));
    s -= n;
    p += n;
    if (acc->auth_mech && acc->username)
    {
	n = snprintf(p, s, "user=%s ", acc->username);
	s -= n;
	p += n;
    }
    n = snprintf(p, s, "from=%s recipients=", acc->from);
    s -= n;
    p += n;
    l = recipients;
    while (!list_is_empty(l))
    {
	l = l->next;
	n = snprintf(p, s, "%s,", (char *)(l->data));
	s -= n;
	p += n;
    }
    /* delete the last ',' */
    *(p - 1) = ' ';
    if (exitcode == EX_OK)
    {
	n = snprintf(p, s, "mailsize=%s ", mailsize_str);
	s -= n;
	p += n;
    }
    if (exitcode != EX_OK && errmsg)
    {
	n = snprintf(p, s, "smtpstatus=%s smtpmsg=%s ", 
		smtpstatus_str, smtperrmsg_str);
	s -= n;
	p += n;
    }
    if (exitcode != EX_OK && errstr[0] != '\0')
    {
	/* hide single quotes to make the info easy to parse */
	tmp = errstr;
	while (*tmp)
	{
	    if (*tmp == '\'')
	    {
		*tmp = '?';
	    }
	    tmp++;
	}
	n = snprintf(p, s, "errormsg='%s' ", msmtp_sanitize_string(errstr));
	s -= n;
	p += n;
    }
    (void)snprintf(p, s, "exitcode=%s", exitcode_str);
    
    free(mailsize_str);
    free(smtpstatus_str);	
    free(smtperrmsg_str);
    return line;
}


/*
 * msmtp_log_to_file()
 *
 * Append a log entry to 'acc->logfile' with the following information:
 * - date/time 
 * - the log line as delivered by msmtp_get_log_info
 */

void msmtp_log_to_file(const char *logfile, const char *loginfo)
{
    FILE *f;
    time_t t;
    struct tm *tm;
    char *failure_reason;
    char time_str[64];
    int e;
    
    /* get time */
    if ((t = time(NULL)) < 0)
    {
	failure_reason = xasprintf(_("cannot get system time: %s"), 
		strerror(errno));
	goto log_failure;
    }
    if (!(tm = localtime(&t)))
    {
	failure_reason = xstrdup(_("cannot convert UTC time to local time"));
	goto log_failure;
    }
    (void)strftime(time_str, sizeof(time_str), "%b %d %H:%M:%S", tm);
    
    /* write log to file */
    if (strcmp(logfile, "-") == 0)
    {
	f = stdout;
    }
    else
    {
	if (!(f = fopen(logfile, "a")))
	{
	    failure_reason = xasprintf(_("cannot open: %s"), strerror(errno));
	    goto log_failure;
	}
	if ((e = lock_file(f, TOOLS_LOCK_WRITE, 10)) != 0)
	{
	    if (e == 1)
	    {
		failure_reason = xasprintf(
			_("cannot lock (tried for %d seconds): %s"), 
			10, strerror(errno));
	    }
	    else
	    {
		failure_reason = xasprintf(_("cannot lock: %s"), 
			strerror(errno));
	    }
	    goto log_failure;
	}
    }
    if ((fputs(time_str, f) == EOF) || (fputc(' ', f) == EOF)
	|| (fputs(loginfo, f) == EOF) || (fputc('\n', f) == EOF))
    {
	failure_reason = xstrdup(_("output error"));
	goto log_failure;
    }
    if (f != stdout && fclose(f) != 0)
    {
	failure_reason = xstrdup(strerror(errno));
	goto log_failure;
    }

    return;
    
    /* error exit target */
log_failure:
    print_error(_("cannot log to %s: %s"), logfile, failure_reason);
    free(failure_reason);
    if (loginfo)
    {
	print_error(_("log info was: %s"), loginfo);
    }
}


/*
 * msmtp_log_to_syslog()
 *
 * Log the information delivered by msmtp_get_log_info() to syslog
 * the facility_str must be one of "LOG_MAIL", "LOG_USER", "LOG_LOCAL0", ...
 * "LOG_LOCAL7"
 * If 'error' is set, LOG_ERR is used, else LOG_INFO is used.
 */

#ifdef HAVE_SYSLOG
void msmtp_log_to_syslog(const char *facility_str, 
	const char *loginfo, int error)
{
    int facility;

    if (facility_str[4] == 'M')
    {
	facility = LOG_MAIL;
    }
    else if (facility_str[4] == 'U')
    {
	facility = LOG_USER;
    }
    else if (facility_str[9] == '0')
    {
	facility = LOG_LOCAL0;
    }
    else if (facility_str[9] == '1')
    {
	facility = LOG_LOCAL1;
    }
    else if (facility_str[9] == '2')
    {
	facility = LOG_LOCAL2;
    }
    else if (facility_str[9] == '3')
    {
	facility = LOG_LOCAL3;
    }
    else if (facility_str[9] == '4')
    {
	facility = LOG_LOCAL4;
    }
    else if (facility_str[9] == '5')
    {
	facility = LOG_LOCAL5;
    }
    else if (facility_str[9] == '6')
    {
	facility = LOG_LOCAL6;
    }
    else
    {
	facility = LOG_LOCAL7;
    }
    
    openlog(PACKAGE_NAME, 0, facility);
    syslog(error ? LOG_ERR : LOG_INFO, "%s", loginfo);
    closelog();
}
#endif /* HAVE_SYSLOG */


/*
 * msmtp_construct_env_from()
 *
 * Build an envelope from address for the current user.
 * If maildomain is not NULL and not the empty string, it will be the domain 
 * part of the address. Otherwise, the address won't have a domain part.
 */

char *msmtp_construct_env_from(const char *maildomain)
{
    char *envelope_from;
    size_t len;

    envelope_from = get_username();
    if (maildomain && *maildomain != '\0')
    {
	len = strlen(envelope_from);
	envelope_from = xrealloc(envelope_from, 
		((len + 1 + strlen(maildomain) + 1) * sizeof(char)));
	envelope_from[len] = '@';
	strcpy(envelope_from + len + 1, maildomain);
    }
    return envelope_from;
}


/*
 * msmtp_print_version()
 *
 * Print --version information
 */

void msmtp_print_version(void)
{
    char *sysconfdir;
    char *sysconffile;
    char *homedir;
    char *userconffile;
    
    printf(_("%s version %s\n"), PACKAGE_NAME, VERSION);
    /* TLS/SSL support */
    printf(_("TLS/SSL library: %s\n"),
#ifdef HAVE_LIBGNUTLS
	    "GnuTLS"
#elif defined (HAVE_OPENSSL)
    	    "OpenSSL"
#else
	    _("none")
#endif
	  );
    /* Authentication support */
    printf(_("Authentication library: %s\n"
		"Supported authentication methods:\n"),
#ifdef HAVE_LIBGSASL
	    "GNU SASL"
#else
	    _("built-in")
#endif /* HAVE_LIBGSASL */
	  );
    if (smtp_client_supports_authmech("PLAIN"))
    {
	printf("plain ");
    }
    if (smtp_client_supports_authmech("CRAM-MD5"))
    {
	printf("cram-md5 ");
    }
    if (smtp_client_supports_authmech("DIGEST-MD5"))
    {
	printf("digest-md5 ");
    }
    if (smtp_client_supports_authmech("GSSAPI"))
    {
	printf("gssapi ");
    }
    if (smtp_client_supports_authmech("EXTERNAL"))
    {
	printf("external ");
    }
    if (smtp_client_supports_authmech("LOGIN"))
    {
	printf("login ");
    }
    if (smtp_client_supports_authmech("NTLM"))
    {
	printf("ntlm ");
    }
    printf("\n");
    /* Internationalized Domain Names support */
    printf(_("IDN support: "));
#ifdef HAVE_LIBIDN
    printf(_("enabled"));
#else
    printf(_("disabled"));
#endif
    printf("\n");
    /* Native language support */
    printf(_("NLS: "));
#ifdef ENABLE_NLS
    printf(_("enabled"));
    printf(_(", LOCALEDIR is %s"), LOCALEDIR);
#else
    printf(_("disabled"));
#endif
    printf("\n");
    sysconfdir = get_sysconfdir();
    sysconffile = get_filename(sysconfdir, SYSCONFFILE);
    printf(_("System configuration file name: %s\n"), sysconffile);
    free(sysconffile);
    free(sysconfdir);
    homedir = get_homedir();
    userconffile = get_filename(homedir, USERCONFFILE);
    printf(_("User configuration file name: %s\n"), userconffile);
    free(userconffile);
    free(homedir);
    printf("\n");
    printf(_("Copyright (C) 2007 Martin Lambers and others.\n"
		"This is free software.  You may redistribute copies of "
		    "it under the terms of\n"
		"the GNU General Public License "
		    "<http://www.gnu.org/licenses/gpl.html>.\n"
		"There is NO WARRANTY, to the extent permitted by law.\n"));
}


/*
 * msmtp_print_help()
 *
 * Print --help information
 */

void msmtp_print_help(void)
{
    printf(_("USAGE:\n\n"
	    "Sendmail mode (default):\n"
	    "  %s [option...] [--] recipient...\n"
	    "  %s [option...] -t [--] [recipient...]\n"
	    "  Read a mail from standard input and transmit it to an SMTP "
	    	"or LMTP server.\n"
	    "Server information mode:\n"
	    "  %s [option...] --serverinfo\n"
	    "  Print information about a server.\n"
	    "Remote Message Queue Starting mode:\n"
	    "  %s [option...] --rmqs=host|@domain|#queue\n"
	    "  Send a Remote Message Queue Starting request to a server.\n"
            "\nOPTIONS:\n\n"
	    "General options:\n"
	    "  --version                    Print version.\n"
	    "  --help                       Print help.\n"
	    "  -P, --pretend                Print configuration info and "
	        "exit.\n"
	    "  -d, --debug                  Print debugging information.\n"
	    "Changing the mode of operation:\n"
	    "  -S, --serverinfo             Print information about the "
	    	"server.\n"
	    "  --rmqs=host|@domain|#queue   Send a Remote Message Queue "
	    	"Starting request.\n"
            "Configuration options:\n"
	    "  -C, --file=filename          Set configuration file.\n"
	    "  -a, --account=id             Use the given account instead of "
	        "the account\n"
	    "                               named \"default\"; its settings "
	        "may be changed\n"
	    "                               with command line options.\n"
	    "  --host=hostname              Set the server, use only command "
	    	"line settings;\n"
	    "                               do not use any configuration file "
	    	"data.\n"
	    "  --port=number                Set port number.\n"
	    "  --timeout=(off|seconds)      Set/unset network timeout in "
		"seconds.\n"
	    "  --protocol=(smtp|lmtp)       Use the given sub protocol.\n"
	    "  --domain=string              Set the argument of EHLO or LHLO "
	    	"command.\n"
	    "  --auth[=(on|off|method)]     Enable/disable authentication and "
	    	"optionally\n"
	    "                               choose the method.\n"
	    "  --user=[username]            Set/unset user name for "
	    	"authentication.\n"
	    "  --tls[=(on|off)]             Enable/disable TLS encryption.\n"
	    "  --tls-starttls[=(on|off)]    Enable/disable STARTTLS for TLS.\n"
	    "  --tls-trust-file=[file]      Set/unset trust file for TLS.\n"
            "  --tls-key-file=[file]        Set/unset private key file for "
	    	"TLS.\n"
	    "  --tls-cert-file=[file]       Set/unset private cert file for "
	    	"TLS.\n"
	    "  --tls-certcheck[=(on|off)]   Enable/disable server certificate "
	    	"checks for TLS.\n"
	    "  --tls-force-sslv3[=(on|off)] Enable/disable restriction to "
	        "SSLv3.\n"
            "Options specific to sendmail mode:\n"
	    "  --auto-from[=(on|off)]       Enable/disable automatic "
	        "envelope-from addresses.\n"
	    "  -f, --from=address           Set envelope from address.\n"
	    "  --maildomain=[domain]        Set the domain for automatic "
	        "envelope from\n"
	    "                               addresses.\n"
	    "  -N, --dsn-notify=(off|cond)  Set/unset DSN conditions.\n"
	    "  -R, --dsn-return=(off|ret)   Set/unset DSN amount.\n"
	    "  --keepbcc[=(on|off)]         Enable/disable preservation of the "
	    	"Bcc header.\n"
            "  -X, --logfile=[file]         Set/unset log file.\n"
	    "  --syslog[=(on|off|facility)] Enable/disable/configure syslog "
	    	"logging.\n"
	    "  -t, --read-recipients        Read additional recipients from "
	    	"the mail.\n"
	    "  --                           End of options.\n"
            "Accepted but ignored: -A, -B, -bm, -F, -G, -h, -i, -L, -m, -n, "
	    	"-O, -o, -v\n"
	    "\nReport bugs to <%s>.\n"),
	    prgname, prgname, prgname, prgname, PACKAGE_BUGREPORT);
}


/*
 * msmtp_cmdline()
 *
 * Process the command line
 */

typedef struct
{
    /* the configuration */
    int print_version;
    int print_help;
    int print_conf;
    int debug;
    int pretend;
    int read_recipients;
    /* mode of operation */
    int sendmail;
    int serverinfo;
    int rmqs;
    char *rmqs_argument;
    /* account information from the command line */
    account_t *cmdline_account;
    const char *account_id;
    char *user_conffile;
    /* the list of recipients */
    list_t *recipients;
} msmtp_cmdline_conf_t;

/* long options without a corresponding short option */
#define LONGONLYOPT_VERSION 		0
#define LONGONLYOPT_HELP    		1
#define LONGONLYOPT_HOST		2
#define LONGONLYOPT_PORT		3
#define LONGONLYOPT_TIMEOUT		4
#define LONGONLYOPT_AUTH		5
#define LONGONLYOPT_USER		6
#define LONGONLYOPT_TLS			7
#define LONGONLYOPT_TLS_STARTTLS	8
#define LONGONLYOPT_TLS_TRUST_FILE	9
#define LONGONLYOPT_TLS_KEY_FILE	10
#define LONGONLYOPT_TLS_CERT_FILE	11
#define LONGONLYOPT_TLS_CERTCHECK	12
#define LONGONLYOPT_TLS_FORCE_SSLV3	13
#define LONGONLYOPT_PROTOCOL		14
#define LONGONLYOPT_DOMAIN		15
#define LONGONLYOPT_KEEPBCC		16
#define LONGONLYOPT_RMQS		17
#define LONGONLYOPT_SYSLOG		18
#define LONGONLYOPT_MAILDOMAIN		19
#define LONGONLYOPT_AUTO_FROM		20

int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[])
{
    struct option options[] =
    {
	{ "version",         no_argument,       0, LONGONLYOPT_VERSION },
	{ "help",            no_argument,       0, LONGONLYOPT_HELP },
	{ "pretend",         no_argument,       0, 'P' },
	/* accept an optional argument for sendmail compatibility: */
	{ "debug",           optional_argument, 0, 'd' },
	{ "serverinfo",      no_argument,       0, 'S' },
	{ "rmqs",            required_argument, 0, LONGONLYOPT_RMQS },
	{ "file",            required_argument, 0, 'C' },
	{ "account",         required_argument, 0, 'a' },
	{ "host",            required_argument, 0, LONGONLYOPT_HOST },
	{ "port",            required_argument, 0, LONGONLYOPT_PORT },
	{ "timeout",         required_argument, 0, LONGONLYOPT_TIMEOUT},
	/* for compatibility with versions <= 1.4.1: */
	{ "connect-timeout", required_argument, 0, LONGONLYOPT_TIMEOUT},
	{ "auto-from",       optional_argument, 0, LONGONLYOPT_AUTO_FROM },
	{ "from",            required_argument, 0, 'f' },
	{ "maildomain",      required_argument, 0, LONGONLYOPT_MAILDOMAIN },
	{ "auth",            optional_argument, 0, LONGONLYOPT_AUTH },
	{ "user",            required_argument, 0, LONGONLYOPT_USER },
	{ "tls",             optional_argument, 0, LONGONLYOPT_TLS },
	{ "tls-starttls",    optional_argument, 0, LONGONLYOPT_TLS_STARTTLS },
	{ "tls-trust-file",  required_argument, 0, LONGONLYOPT_TLS_TRUST_FILE },
	{ "tls-key-file",    required_argument, 0, LONGONLYOPT_TLS_KEY_FILE },
	{ "tls-cert-file",   required_argument, 0, LONGONLYOPT_TLS_CERT_FILE },
	{ "tls-certcheck",   optional_argument, 0, LONGONLYOPT_TLS_CERTCHECK },
	{ "tls-force-sslv3", optional_argument, 0, 
	    LONGONLYOPT_TLS_FORCE_SSLV3 },
	{ "dsn-notify",      required_argument, 0, 'N' },
	{ "dsn-return",      required_argument, 0, 'R' },
	{ "protocol",        required_argument, 0, LONGONLYOPT_PROTOCOL },
	{ "domain",          required_argument, 0, LONGONLYOPT_DOMAIN },
	{ "keepbcc",         optional_argument, 0, LONGONLYOPT_KEEPBCC },
	{ "logfile",         required_argument, 0, 'X' },
	{ "syslog",          optional_argument, 0, LONGONLYOPT_SYSLOG },
	{ "read-recipients", no_argument,       0, 't' },
	{ 0, 0, 0, 0 }
    };
    int rcptc;
    char **rcptv;
    int error_code;
    int c;
    int i;
    list_t *lp;

    /* the program name */
    prgname = get_prgname(argv[0]);
    /* the configuration */
    conf->print_version = 0;
    conf->print_help = 0;
    conf->print_conf = 0;
    conf->debug = 0;
    conf->pretend = 0;
    conf->read_recipients = 0;
    /* mode of operation */
    conf->sendmail = 1;
    conf->serverinfo = 0;
    conf->rmqs = 0;
    conf->rmqs_argument = NULL;
    /* account information from the command line */
    conf->cmdline_account = account_new(NULL, NULL);
    conf->account_id = NULL;
    conf->user_conffile = NULL;
    /* the recipients */
    conf->recipients = NULL;
    
    /* process the command line */
    error_code = 0;
    for (;;)
    {
	c = getopt_long(argc, argv, "Pd::SC:a:f:N:R:X:tA:B:b:F:Gh:iL:mnO:o:v", 
		options, NULL);
	if (c == -1)
	{
	    break;
	}
	switch(c)
	{
	    case LONGONLYOPT_VERSION:
		conf->print_version = 1;
    		conf->sendmail = 0;
		conf->serverinfo = 0;
		break;

	    case LONGONLYOPT_HELP:
		conf->print_help = 1;
		conf->sendmail = 0;
		conf->serverinfo = 0;
		break;

	    case 'P':
		conf->print_conf = 1;
		conf->pretend = 1;
		break;
		
	    case 'd':
		conf->print_conf = 1;
		conf->debug = 1;
		/* only care about the optional argument if it's "0.1", which is
		 * the only argument that's documented for sendmail: it prints
		 * version information */
		if (optarg && strcmp(optarg, "0.1") == 0)
		{
	    	    conf->print_version = 1;
		}
		break;
		
	    case 'S':
		if (conf->rmqs)
		{
		    print_error(_("cannot use both --serverinfo and --rmqs"));
		    error_code = 1;
		}
		else
		{
		    conf->serverinfo = 1;
		    conf->sendmail = 0;
		    conf->rmqs = 0;
		}
		break;

	    case LONGONLYOPT_RMQS:
		if (conf->serverinfo)
		{
		    print_error(_("cannot use both --serverinfo and --rmqs"));
		    error_code = 1;
		}
		else
		{
		    conf->rmqs = 1;
		    conf->rmqs_argument = optarg;
		    conf->sendmail = 0;
		    conf->serverinfo = 0;
		}
		break;
		
	    case 'C':
		free(conf->user_conffile);
		conf->user_conffile = xstrdup(optarg);
		break;

	    case 'a':
		if (conf->cmdline_account->host)
		{
		    print_error(_("cannot use both --host and --account"));
		    error_code = 1;
		}
		else
		{
		    conf->account_id = optarg;
		}
		break;

	    case LONGONLYOPT_HOST:
		if (conf->account_id)
		{
		    print_error(_("cannot use both --host and --account"));
		    error_code = 1;
		}
		else
		{
		    free(conf->cmdline_account->host);
		    conf->cmdline_account->host = xstrdup(optarg);
		    conf->cmdline_account->mask |= ACC_HOST;
		}
		break;
		
	    case LONGONLYOPT_PORT:
		conf->cmdline_account->port = get_pos_int(optarg);
		if (conf->cmdline_account->port < 1 
			|| conf->cmdline_account->port > 65535)
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--port");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_PORT;
		break;		
		
	    case LONGONLYOPT_TIMEOUT:
		if (is_off(optarg))
		{
		    conf->cmdline_account->timeout = 0;
		}
		else
		{
		    conf->cmdline_account->timeout = 
			get_pos_int(optarg);
		    if (conf->cmdline_account->timeout < 1)
		    {
			print_error(_("invalid argument %s for %s"),
				optarg, "--timeout");
			error_code = 1;
		    }
		}
		conf->cmdline_account->mask |= ACC_TIMEOUT;
		break;
		
	    case LONGONLYOPT_AUTO_FROM:
	    	if (!optarg || is_on(optarg))
    		{
		    conf->cmdline_account->auto_from = 1;
		}
		else if (is_off(optarg))
		{
		    conf->cmdline_account->auto_from = 0;
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--auto-from");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_AUTO_FROM;
		break;
		
	    case 'f':
		free(conf->cmdline_account->from);
		conf->cmdline_account->from = xstrdup(optarg);
		conf->cmdline_account->mask |= ACC_FROM;
		break;

	    case LONGONLYOPT_MAILDOMAIN:
		free(conf->cmdline_account->maildomain);
		conf->cmdline_account->maildomain = 
		    (*optarg == '\0') ? NULL : xstrdup(optarg);
		conf->cmdline_account->mask |= ACC_MAILDOMAIN;
		break;

	    case LONGONLYOPT_AUTH:
		free(conf->cmdline_account->auth_mech);
		if (!optarg || is_on(optarg))
		{
		    conf->cmdline_account->auth_mech = xstrdup("");
		}
		else if (is_off(optarg))
    		{
		    conf->cmdline_account->auth_mech = NULL;
		}
		else if (check_auth_arg(optarg) == 0)
		{
		    conf->cmdline_account->auth_mech = xstrdup(optarg);
		}
		else
		{
		    conf->cmdline_account->auth_mech = NULL;
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--auth");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_AUTH_MECH;
		break;

	    case LONGONLYOPT_USER:
		free(conf->cmdline_account->username);
		conf->cmdline_account->username =
		    (*optarg == '\0') ? NULL : xstrdup(optarg);
		conf->cmdline_account->mask |= ACC_USERNAME;
		break;

	    case LONGONLYOPT_TLS:
	    	if (!optarg || is_on(optarg))
    		{
		    conf->cmdline_account->tls = 1;
		}
		else if (is_off(optarg))
		{
		    conf->cmdline_account->tls = 0;
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--tls");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_TLS;
		break;
		
	    case LONGONLYOPT_TLS_STARTTLS:
	    	if (!optarg || is_on(optarg))
    		{
		    conf->cmdline_account->tls_nostarttls = 0;
		}
		else if (is_off(optarg))
		{
		    conf->cmdline_account->tls_nostarttls = 1;
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--tls-starttls");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_TLS_NOSTARTTLS;
		break;

	    case LONGONLYOPT_TLS_TRUST_FILE:
		free(conf->cmdline_account->tls_trust_file);
		if (*optarg)
		{
		    conf->cmdline_account->tls_trust_file = 
			expand_tilde(optarg);
		}
		else
		{
		    conf->cmdline_account->tls_trust_file = NULL;
		}
		conf->cmdline_account->mask |= ACC_TLS_TRUST_FILE;
		break;

	    case LONGONLYOPT_TLS_KEY_FILE:
		free(conf->cmdline_account->tls_key_file);
		if (*optarg)
		{
		    conf->cmdline_account->tls_key_file = expand_tilde(optarg);
		}
		else
		{
		    conf->cmdline_account->tls_key_file = NULL;
		}
		conf->cmdline_account->mask |= ACC_TLS_KEY_FILE;
		break;

	    case LONGONLYOPT_TLS_CERT_FILE:
		free(conf->cmdline_account->tls_cert_file);
		if (*optarg)
		{
		    conf->cmdline_account->tls_cert_file = expand_tilde(optarg);
		}
		else
		{
		    conf->cmdline_account->tls_cert_file = NULL;
		}
		conf->cmdline_account->mask |= ACC_TLS_CERT_FILE;
		break;

	    case LONGONLYOPT_TLS_CERTCHECK:
	   	if (!optarg || is_on(optarg))
    		{
		    conf->cmdline_account->tls_nocertcheck = 0;
		}
		else if (is_off(optarg))
		{
		    conf->cmdline_account->tls_nocertcheck = 1;
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--tls-certcheck");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_TLS_NOCERTCHECK;
		break;

	    case LONGONLYOPT_TLS_FORCE_SSLV3:
	   	if (!optarg || is_on(optarg))
    		{
		    conf->cmdline_account->tls_force_sslv3 = 1;
		}
		else if (is_off(optarg))
		{
		    conf->cmdline_account->tls_force_sslv3 = 0;
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--tls-force-sslv3");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_TLS_FORCE_SSLV3;
		break;

	    case 'N':
		free(conf->cmdline_account->dsn_notify);
		if (is_off(optarg))
		{
		    conf->cmdline_account->dsn_notify = NULL;
		}
		else if (check_dsn_notify_arg(optarg) == 0)
		{
		    conf->cmdline_account->dsn_notify = xstrdup(optarg);
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--dsn-notify");
	    	    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_DSN_NOTIFY;
		break;

	    case 'R':
		/* be compatible to both sendmail and the dsn_notify command */
		free(conf->cmdline_account->dsn_return);
		if (is_off(optarg))
		{
		    conf->cmdline_account->dsn_return = NULL;
		}
		else if (strcmp(optarg, "hdrs") == 0 
			|| strcmp(optarg, "headers") == 0)
		{
		    conf->cmdline_account->dsn_return = xstrdup("HDRS");
		}
		else if (strcmp(optarg, "full") == 0)
		{
		    conf->cmdline_account->dsn_return = xstrdup("FULL");
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--dsn-return");
	    	    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_DSN_RETURN;
		break;
		
	    case LONGONLYOPT_PROTOCOL:
		conf->cmdline_account->mask |= ACC_PROTOCOL;
		if (strcmp(optarg, "smtp") == 0)
		{
		    conf->cmdline_account->protocol = SMTP_PROTO_SMTP;
		}
		else if (strcmp(optarg, "lmtp") == 0)
		{
		    conf->cmdline_account->protocol = SMTP_PROTO_LMTP;
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--protocol");
	    	    error_code = 1;
		}
		break;

	    case LONGONLYOPT_DOMAIN:
		free(conf->cmdline_account->domain);
		conf->cmdline_account->domain = xstrdup(optarg);
		conf->cmdline_account->mask |= ACC_DOMAIN;
		break;

	    case LONGONLYOPT_KEEPBCC:
	    	if (!optarg || is_on(optarg))
    		{
		    conf->cmdline_account->keepbcc = 1;
		}
		else if (is_off(optarg))
		{
		    conf->cmdline_account->keepbcc = 0;
		}
		else
		{
		    print_error(_("invalid argument %s for %s"), 
			    optarg, "--keepbcc");
		    error_code = 1;
		}
		conf->cmdline_account->mask |= ACC_KEEPBCC;
		break;

	    case 'X':
		free(conf->cmdline_account->logfile);
		if (*optarg)
		{
		    conf->cmdline_account->logfile = expand_tilde(optarg);
		}
		else
		{
		    conf->cmdline_account->logfile = NULL;
		}
		conf->cmdline_account->mask |= ACC_LOGFILE;
		break;

	    case LONGONLYOPT_SYSLOG:
		free(conf->cmdline_account->syslog);
		if (!optarg || is_on(optarg))
		{
		    conf->cmdline_account->syslog = 
			get_default_syslog_facility();
		}
		else if (is_off(optarg))
		{
		    conf->cmdline_account->syslog = NULL;
		}
		else
		{
		    if (check_syslog_arg(optarg) != 0)
		    {
			print_error(_("invalid argument %s for %s"), 
				optarg, "--syslog");
			error_code = 1;
		    }
		    else
		    {
			conf->cmdline_account->syslog = xstrdup(optarg);
		    }
		}
		conf->cmdline_account->mask |= ACC_SYSLOG;
		break;

	    case 't':
		conf->read_recipients = 1;
		break;

	    case 'b':
		/* only m makes sense */
		if (strcmp(optarg, "m") != 0)
		{
		    print_error(_("unsupported operation mode b%s"), optarg);
		    error_code = 1;
		}		    
		break;

	    case 'A':
	    case 'B':
	    case 'F':
	    case 'G':
	    case 'h':
	    case 'i':
	    case 'L':
	    case 'm':
	    case 'n':
	    case 'O':
	    case 'o':
	    case 'v':
		break;
		
	    /* unknown option */
	    default:
		error_code = 1;
		break;
	}
	if (error_code)
	{
	    break;
	}
    }
    if (error_code)
    {
	return EX_USAGE;
    }

    /* the list of recipients */
    rcptc = argc - optind;
    rcptv = &(argv[optind]);
    conf->recipients = list_new();
    lp = conf->recipients;
    for (i = 0; i < rcptc; i++)
    {
	list_insert(lp, xstrdup(rcptv[i]));
	lp = lp->next;
    }

    return EX_OK;
}


/*
 * msmtp_get_conffile_accounts()
 * Read the system and user configuration files and merge the data
 */

int msmtp_get_conffile_accounts(list_t **account_list, 
	int print_info, const char *user_conffile, 
	char **loaded_system_conffile, char **loaded_user_conffile)
{
    char *errstr;
    char *system_confdir;
    char *system_conffile;
    char *homedir;
    char *real_user_conffile;
    list_t *system_account_list;
    list_t *user_account_list;
    list_t *lps;
    list_t *lpu;
    int e;


    *loaded_system_conffile = NULL;
    *loaded_user_conffile = NULL;

    /* Read the system configuration file.
     * It is not an error if system_conffile cannot be opened,
     * but it is an error is the file content is invalid */ 
    system_confdir = get_sysconfdir();
    system_conffile = get_filename(system_confdir, SYSCONFFILE);
    free(system_confdir);
    if ((e = get_conf(system_conffile, 0, &system_account_list, &errstr)) 
	    != CONF_EOK)
    {
	if (e == CONF_ECANTOPEN)
	{
	    if (print_info)
	    {
		printf(_("ignoring system configuration file %s: %s\n"),
			system_conffile, msmtp_sanitize_string(errstr));
	    }
	}
	else
	{
	    print_error("%s: %s", system_conffile, 
		    msmtp_sanitize_string(errstr));
	    return (e == CONF_EIO) ? EX_IOERR : EX_CONFIG;
	}
    }
    else
    {
	if (print_info)
	{
	    printf(_("loaded system configuration file %s\n"), system_conffile);
	}
	*loaded_system_conffile = xstrdup(system_conffile);
    }
    free(system_conffile);

    /* Read the user configuration file.
     * It is not an error if user_conffile cannot be opened (unless it was
     * chosen with -C/--file), but it is an error is the file content is 
     * invalid */ 
    if (user_conffile)
    {
	real_user_conffile = xstrdup(user_conffile);
    }
    else
    {
	homedir = get_homedir();
	real_user_conffile = get_filename(homedir, USERCONFFILE);
	free(homedir);
    }
    if ((e = get_conf(real_user_conffile, 1, &user_account_list, &errstr)) 
	    != CONF_EOK)
    {
	if (e == CONF_ECANTOPEN)
	{
	    /* If the configuration file was set with -C/--file, it is an
	     * error if we cannot open it */
	    if (user_conffile)
	    {
		print_error("%s: %s", real_user_conffile, 
			msmtp_sanitize_string(errstr));
		return EX_IOERR;
	    }
	    /* otherwise, we can ignore it */
	    if (print_info)
	    {
		printf(_("ignoring user configuration file %s: %s\n"),
			real_user_conffile, msmtp_sanitize_string(errstr));
	    }
	}
	else
	{
	    print_error("%s: %s", real_user_conffile, 
		    msmtp_sanitize_string(errstr));
	    return (e == CONF_EIO) ? EX_IOERR : EX_CONFIG;
	}
    }
    else
    {
	if (print_info)
	{
	    printf(_("loaded user configuration file %s\n"), 
		    real_user_conffile);
	}
	*loaded_user_conffile = xstrdup(real_user_conffile);
    }
    free(real_user_conffile);
    
    /* Merge system_account_list and user_account_list into account_list.
     * If an account exist in both files, only the one from the user conffile is
     * kept. It is important that the order of accounts is maintained, so that
     * --from can choose the *first* account with a matching envelope from 
     * address. */
    if (*loaded_system_conffile && *loaded_user_conffile)
    {
	lpu = user_account_list;
	lps = system_account_list;
	while (!list_is_empty(lps))
	{
	    lps = lps->next;
	    if (!find_account(user_account_list, ((account_t *)lps->data)->id))
	    {
		list_insert(lpu, account_copy(lps->data));
		lpu = lpu->next;
	    }
	}
	*account_list = user_account_list;
	list_xfree(system_account_list, account_free);
    }
    else if (*loaded_system_conffile)
    {
	*account_list = system_account_list;
    }
    else if (*loaded_user_conffile)
    {
	*account_list = user_account_list;
    }
    else
    {
	*account_list = list_new();
    }

    return EX_OK;
}

    
/*
 * msmtp_print_conf
 *
 * Print configuration information, for example for --pretend
 */

void msmtp_print_conf(msmtp_cmdline_conf_t conf, account_t *account)
{
    if (account->id && account->conffile)
    {
	printf(_("using account %s from %s\n"), 
		account->id, account->conffile);
    }
    printf("host            = %s\n"
	    "port            = %d\n",
	    account->host,
	    account->port);
    printf("timeout         = ");
    if (account->timeout <= 0)
    {
	printf(_("off\n"));
    }
    else
    {
	if (account->timeout > 1)
	{
	    printf(_("%d seconds\n"), account->timeout);
	}
	else
	{
	    printf(_("1 second\n"));
	}
    }
    printf("protocol        = %s\n"
	    "domain          = %s\n", 
	    account->protocol == SMTP_PROTO_SMTP ? "smtp" : "lmtp",
	    account->domain);
    printf("auth            = ");
    if (!account->auth_mech)
    {
	printf(_("none\n"));
    }
    else if (account->auth_mech[0] == '\0')
    {
	printf(_("choose\n"));
    }
    else
    {
	printf("%s\n", account->auth_mech);
    }
    printf("user            = %s\n"
	    "password        = %s\n"
	    "ntlmdomain      = %s\n"
	    "tls             = %s\n"
	    "tls_starttls    = %s\n"
	    "tls_trust_file  = %s\n"
	    "tls_key_file    = %s\n"
	    "tls_cert_file   = %s\n"
	    "tls_certcheck   = %s\n"
	    "tls_force_sslv3 = %s\n",
	    account->username ? account->username : _("(not set)"),
	    account->password ? "*" : _("(not set)"),
	    account->ntlmdomain ? account->ntlmdomain : _("(not set)"),
	    account->tls ? _("on") : _("off"), 
	    account->tls_nostarttls ? _("off") : _("on"),
	    account->tls_trust_file ? account->tls_trust_file : _("(not set)"),
	    account->tls_key_file ? account->tls_key_file : _("(not set)"),
	    account->tls_cert_file ? account->tls_cert_file : _("(not set)"),
	    account->tls_nocertcheck ? _("off") : _("on"),
	    account->tls_force_sslv3 ? _("on") : _("off"));
    if (conf.sendmail)
    {
	printf("auto_from       = %s\n"
		"maildomain      = %s\n"
		"from            = %s\n"
		"dsn_notify      = %s\n"
		"dsn_return      = %s\n"
		"keepbcc         = %s\n"
		"logfile         = %s\n"
		"syslog          = %s\n",
		account->auto_from ? _("on") : _("off"),
		account->maildomain ? account->maildomain : _("(not set)"),
		account->from ? account->from : _("(not set)"),
		account->dsn_notify ? account->dsn_notify : _("(not set)"),
		account->dsn_return ? account->dsn_return : _("(not set)"),
		account->keepbcc ? _("on") : _("off"),
		account->logfile ? account->logfile : _("(not set)"),
		account->syslog ? account->syslog : _("(not set)"));
	if (conf.read_recipients)
	{
	    printf(_("reading recipients from the command line "
			"and the mail\n"));
	}
	else
	{
	    printf(_("reading recipients from the command line\n"));
	}
    }
    if (conf.rmqs)
    {
	printf("RMQS argument   = %s\n", conf.rmqs_argument);
    }
}


/*
 * The main function.
 * It returns values from sysexits.h (like sendmail does).
 */

int main(int argc, char *argv[])
{
    msmtp_cmdline_conf_t conf;
    /* account information from the configuration file(s) */
    list_t *account_list = NULL;
    char *loaded_system_conffile = NULL;
    char *loaded_user_conffile = NULL;
    /* the account data that will be used */
    account_t *account = NULL;
    /* error handling */
    char *errstr;
    list_t *errmsg;
    int error_code;
    int e;
    list_t *lp;
    /* misc */
#ifdef HAVE_TLS
    int tls_lib_initialized = 0;
#endif
    int net_lib_initialized = 0;
    /* the size of a sent mail */
    long mailsize;
    /* special LMTP error info */
    list_t *lmtp_errstrs;
    list_t *lmtp_error_msgs;
    list_t *lp_lmtp_errstrs;
    list_t *lp_lmtp_error_msgs;
    /* log information */
    char *log_info;
    /* needed to get the default port */
    struct servent *se;    

    
    /* Avoid the side effects of text mode interpretations on DOS systems. */
#ifdef W32_NATIVE
    _setmode(_fileno(stdin), _O_BINARY);
    _fmode = _O_BINARY;
#elif defined DJGPP
    setmode(fileno(stdin), O_BINARY);
    _fmode = O_BINARY;
#endif

    errstr = NULL;
    errmsg = NULL;
    
    /* internationalization with gettext */
#ifdef ENABLE_NLS
    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif
		    
    /* the command line */
    if ((error_code = msmtp_cmdline(&conf, argc, argv)) != EX_OK)
    {
	goto exit;
    }

    if (conf.print_version)
    {
	msmtp_print_version();
    }
    if (conf.print_help)
    {
	msmtp_print_help();
    }
	    
    if (!conf.sendmail && !conf.serverinfo && !conf.rmqs && !conf.print_conf)
    {
	error_code = EX_OK;
	goto exit;
    }

    /* check the list of recipients */
    if (conf.sendmail && list_is_empty(conf.recipients) 
	    && !conf.read_recipients && !conf.pretend)
    {
	print_error(_("no recipients given"));
	error_code = EX_USAGE;
	goto exit;
    }
    if ((conf.serverinfo || conf.rmqs) && !list_is_empty(conf.recipients))
    {
	print_error(_("too many arguments"));
	error_code = EX_USAGE;
	goto exit;
    }

    /* get the account to be used, either from the conffile(s) or from the 
     * command line */
    if (!conf.cmdline_account->host)
    {	
	if ((error_code = msmtp_get_conffile_accounts(&account_list, 
			(conf.pretend || conf.debug), conf.user_conffile, 
			&loaded_system_conffile, &loaded_user_conffile)) 
		!= EX_OK)
	{
	    goto exit;
	}
	if (!conf.account_id)
	{
	    if (conf.cmdline_account->from)
	    {
		/* No account was chosen, but the envelope from address is 
		 * given. Choose the right account with this address.
		 */
		account = account_copy(find_account_by_envelope_from(
			    account_list, conf.cmdline_account->from));
	    }
	    if (!account)
	    {
		/* No envelope from address or no matching account. 
		 * Use default. */
		conf.account_id = "default";
	    }
	}
	if (!account && !(account = 
		    account_copy(find_account(account_list, conf.account_id))))
	{
	    if (loaded_system_conffile && loaded_user_conffile)
	    {		
		print_error(_("account %s not found in %s and %s"),
		     	conf.account_id, loaded_system_conffile, 
			loaded_user_conffile);
	    }
	    else if (loaded_system_conffile)
	    {
		print_error(_("account %s not found in %s"), conf.account_id, 
			loaded_system_conffile);
	    }
	    else if (loaded_user_conffile)
	    {
		print_error(_("account %s not found in %s"), conf.account_id, 
			loaded_user_conffile);
	    }
	    else /* no conffile was read */
	    {
		print_error(_("account %s not found: "
			    "no configuration file available"),
			conf.account_id);
	    }
	    error_code = EX_CONFIG;
    	    goto exit;
	}
	override_account(account, conf.cmdline_account);
    }
    else
    {
	account = account_copy(conf.cmdline_account);
    }
    
    /* OK, we're using the settings in 'account'. Complete them and check 
     * them. */
    if (account->port == 0)
    {
	if (account->protocol == SMTP_PROTO_SMTP)
	{
	    if (account->tls && account->tls_nostarttls)
	    {
		se = getservbyname("ssmtp", NULL);
		account->port = se ? ntohs(se->s_port) : 465;
	    }
	    else
	    {
		se = getservbyname("smtp", NULL);
		account->port = se ? ntohs(se->s_port) : 25;
	    }
	}
	else /* LMTP. Has no default port as of 2006-06-17. */
	{
	    se = getservbyname("lmtp", NULL);
	    if (se)
	    {
		account->port = ntohs(se->s_port);
	    }
	}
    }
    if (conf.sendmail && account->auto_from)
    {
	free(account->from);
	account->from = msmtp_construct_env_from(account->maildomain);
    }
    if (check_account(account, conf.sendmail, &errstr) != CONF_EOK)
    {
	if (account->id && account->conffile)
	{
	    print_error(_("account %s from %s: %s"), account->id, 
		    account->conffile, msmtp_sanitize_string(errstr));
	}
	else
	{
	    print_error("%s", msmtp_sanitize_string(errstr));
	}
	error_code = EX_CONFIG;
	goto exit;
    }

    /* print configuration */
    if (conf.print_conf)
    {
	msmtp_print_conf(conf, account);
    }

    /* stop if there's nothing to do */
    if (conf.pretend || (!conf.sendmail && !conf.serverinfo && !conf.rmqs))
    {
	error_code = EX_OK;
	goto exit;
    }
    
    /* initialize libraries */
#ifndef HAVE_SYSLOG
    if (conf.sendmail && account->syslog)
    {
	print_error(_("this platform does not support syslog logging"));
	error_code = EX_UNAVAILABLE;
	goto exit;
    }
#endif /* not HAVE_SYSLOG */
    if ((conf.sendmail || conf.rmqs) /* serverinfo does not use auth */
	    && account->auth_mech && (strcmp(account->auth_mech, "") != 0) 
	    && !smtp_client_supports_authmech(account->auth_mech))
    {
	print_error(_("support for authentication method %s "
		    "is not compiled in"),
		account->auth_mech);
	error_code = EX_UNAVAILABLE;
	goto exit;
    }
    if ((e = net_lib_init(&errstr)) != NET_EOK)
    {
	print_error(_("cannot initialize networking: %s"), 
		msmtp_sanitize_string(errstr));
	error_code = EX_SOFTWARE;
	goto exit;	
    }
    net_lib_initialized = 1;
    if (account->tls)
    {
#ifdef HAVE_TLS
	if ((e = tls_lib_init(&errstr)) != TLS_EOK)
	{
	    print_error(_("cannot initialize TLS library: %s"), 
		    msmtp_sanitize_string(errstr));
	    error_code = EX_SOFTWARE;
	    goto exit;
	}
	tls_lib_initialized = 1;
#else /* not HAVE_TLS */
	print_error(_("support for TLS is not compiled in"));
	error_code = EX_UNAVAILABLE;
	goto exit;
#endif /* not HAVE_TLS */
    }

    /* do the work */
    if (conf.sendmail)
    {
	if ((error_code = msmtp_sendmail(account, conf.recipients,
			conf.read_recipients, stdin, conf.debug, &mailsize, 
			&lmtp_errstrs, &lmtp_error_msgs,
			&errmsg, &errstr)) != EX_OK)
	{
	    if (account->protocol == SMTP_PROTO_LMTP && lmtp_errstrs)
	    {
		lp_lmtp_errstrs = lmtp_errstrs;
		lp_lmtp_error_msgs = lmtp_error_msgs;
		while (!list_is_empty(lp_lmtp_errstrs))
		{
		    lp_lmtp_errstrs = lp_lmtp_errstrs->next;
		    lp_lmtp_error_msgs = lp_lmtp_error_msgs->next;
		    if (lp_lmtp_errstrs->data)
		    {
			print_error("%s", msmtp_sanitize_string(
				    lp_lmtp_errstrs->data));
			if ((lp = lp_lmtp_error_msgs->data))
			{
			    while (!list_is_empty(lp))
			    {
				lp = lp->next;
				print_error(_("LMTP server message: %s"),
					msmtp_sanitize_string(lp->data));
			    }
			    list_xfree(lp_lmtp_error_msgs->data, free);
			}
		    }
		}
		list_xfree(lmtp_errstrs, free);
		list_free(lmtp_error_msgs);
		if (account->id && account->conffile)
		{
		    print_error(_("could not send mail to all recipients "
				"(account %s from %s)"), 
		    	    account->id, account->conffile);
		}
		else
		{
		    print_error(_("could not send mail to all recipients"));
		}
	    }
	    else
	    {
		if (errstr)
		{
		    print_error("%s", msmtp_sanitize_string(errstr));
		}
		if (errmsg)
		{
		    lp = errmsg;
		    while (!list_is_empty(lp))
		    {
			lp = lp->next;
			print_error(_("server message: %s"), 
				msmtp_sanitize_string(lp->data));
		    }
		}
		if (account->id && account->conffile)
		{
		    print_error(_("could not send mail (account %s from %s)"), 
		    	    account->id, account->conffile);
		}
		else
		{
		    print_error(_("could not send mail"));
		}
	    }
	}
	if (account->logfile || account->syslog)
	{
	    if (account->protocol == SMTP_PROTO_LMTP && lmtp_errstrs)
	    {
		/* errstr is NULL; print short info to it */
		errstr = xasprintf(
			_("delivery to one or more recipients failed"));
		/* we know that errmsg is NULL. that's ok. */
	    }
	    log_info = msmtp_get_log_info(account, conf.recipients, mailsize, 
		    errmsg, errstr, error_code);
	    if (account->logfile)
	    {
		msmtp_log_to_file(account->logfile, log_info);
	    }
#ifdef HAVE_SYSLOG
	    if (account->syslog)
	    {
		msmtp_log_to_syslog(account->syslog, log_info, 
			(error_code != EX_OK));
	    }
#endif
	    free(log_info);
	}
    }
    else if (conf.serverinfo)
    {
	if ((error_code = msmtp_serverinfo(account, conf.debug, 
			&errmsg, &errstr)) != EX_OK)
	{
	    if (errstr)
	    {
		print_error("%s", msmtp_sanitize_string(errstr));
	    }
	    if (errmsg)
	    {
		lp = errmsg;
		while (!list_is_empty(lp))
		{
		    lp = lp->next;
		    print_error(_("server message: %s"), 
			    msmtp_sanitize_string(lp->data));
		}
	    }		    
	}
    }
    else /* rmqs */
    {
	if ((error_code = msmtp_rmqs(account, conf.debug, conf.rmqs_argument, 
			&errmsg, &errstr)) != EX_OK)
	{
	    if (errstr)
	    {
		print_error("%s", msmtp_sanitize_string(errstr));
	    }
	    if (errmsg)
	    {
		lp = errmsg;
		while (!list_is_empty(lp))
		{
		    lp = lp->next;
		    print_error(_("server message: %s"), 
			    msmtp_sanitize_string(lp->data));
		}
	    }		    
	}
    }

    
exit:

    /* clean up */
    free(loaded_system_conffile);
    free(loaded_user_conffile);
#ifdef HAVE_TLS
    if (tls_lib_initialized) 
    {
	tls_lib_deinit();
    }
#endif /* HAVE_TLS */
    if (net_lib_initialized)
    {
	net_lib_deinit();
    }
    if (account_list)
    {
	list_xfree(account_list, account_free);
    }
    account_free(conf.cmdline_account);
    account_free(account);
    if (conf.recipients)
    {
	list_xfree(conf.recipients, free);
    }
    free(errstr);
    if (errmsg)
    {
	list_xfree(errmsg, free);
    }

    return error_code;
}


syntax highlighted by Code2HTML, v. 0.9.1