ftp.nice.ch/pub/next/unix/mail/smail3.1.20.s.tar.gz#/smail3.1.20/src/transports/tcpsmtp.c

This is tcpsmtp.c in view mode; [Download] [Up]

/* @(#)src/transports/tcpsmtp.c	1.2 24 Oct 1990 05:26:02 */

/*
 *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
 * 
 * See the file COPYING, distributed with smail, for restriction
 * and warranty information.
 */

/*
 * tcpsmtp.c:
 *	Send mail using the SMTP protocol.
 */
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <setjmp.h>
#include "defs.h"

#ifdef	CMC

# include <sys/CMC/longnames.h>
# include <sys/CMC/netdb.h>
# include <sys/CMC/types.h>	
# include <sys/CMC/socket.h>
# include <sys/CMC/in.h>

#else	/* not CMC */

# include <netdb.h>
# include <sys/types.h>
# ifdef	UNIX_CPC
#  include <h/types42.h>
#  include <h/socket.h>
# else	/* not UNIX_CPC */
#  include <sys/socket.h>
# endif	/* not UNIX_CPC */
# include <netinet/in.h>

#endif	/* not CMC */

#include "../smail.h"
#include "../dys.h"
#include "../addr.h"
#include "../transport.h"
#include "../spool.h"
#include "../exitcodes.h"
#include "../smailconf.h"
#include "../parse.h"
#include "tcpsmtp.h"
#include "smtplib.h"
#ifndef DEPEND
# include "../extern.h"
# include "../error.h"
# include "../debug.h"
#endif

/* functions local to this file */
static char *set_short_timeout();
static char *set_long_timeout();
static char *set_timeout();
static int tcpsmtp_connect();
static struct error *connect_failure();


/*
 * tpd_tcpsmtp - transport to remote machine using SMTP on top of TCP/IP.
 */
void
tpd_tcpsmtp(addr, succeed, defer, fail)
    struct addr *addr;			/* recipient addresses for transport */
    struct addr **succeed;		/* successful deliveries */
    struct addr **defer;		/* defer until a later queue run */
    struct addr **fail;			/* failed deliveries */
{
    struct transport *tp = addr->transport;
    struct tcpsmtp_private *priv;
    int s;				/* socket */
    int s2;				/* dup of s */
    char *error_text;			/* error message */
    struct error *error;		/* error structure */
    struct smtp smtpbuf;		/* SMTP description buffer */
    int success;
    char *service;

    priv = (struct tcpsmtp_private *)tp->private;

    DEBUG2(DBG_DRIVER_LO, "transport %s: connect to host %s ...",
	   addr->transport->name, addr->next_host);

    service = expand_string(priv->service, addr, (char *)0, (char *)0);
    if (service == NULL) {
	error_text = xprintf("failed to expand service, %s", priv->service);
	insert_addr_list(addr, defer,connect_failure(tp, error_text));
	return;
    }

    s = tcpsmtp_connect(addr->next_host, service, &error_text);
    if (s >= 0) {
	s2 = dup(s);
	if (s2 < 0) {
	    (void) close(s);
	    s = -1;
	}
    }
    if (s < 0) {
	insert_addr_list(addr, defer, connect_failure(tp, error_text));
	return;
    }

    smtpbuf.in = fdopen(s, "r");
    smtpbuf.out = fdopen(s2, "w");
    smtpbuf.short_timeout = priv->short_timeout;
    smtpbuf.long_timeout = priv->long_timeout;
    smtpbuf.nl = "\r\n";
    tp->flags |= PUT_CRLF;
    smtpbuf.tp = tp;

    DEBUG(DBG_DRIVER_LO, "connected\n");

    switch (smtp_startup(&smtpbuf, &error)) {

    case SMTP_FAIL:
	insert_addr_list(addr, fail, error);
	(void) fclose(smtpbuf.in);
	(void) fclose(smtpbuf.out);
	return;

    case SMTP_AGAIN:
	insert_addr_list(addr, defer, error);
	(void) fclose(smtpbuf.in);
	(void) fclose(smtpbuf.out);
	return;
    }

    if (dont_deliver) {
	insert_addr_list(addr, succeed, (struct error *)NULL);
	smtp_shutdown(&smtpbuf);
    } else {
	success = smtp_send(&smtpbuf, addr, succeed, defer, fail);
	if (success == SUCCEED) {
	    smtp_shutdown(&smtpbuf);
	}
    }

    /* all done */
    (void) fclose(smtpbuf.in);
    (void) fclose(smtpbuf.out);
    return;
}

/*
 * tpd_smtp - obsolescent name for tpd_tcpsmtp driver
 *
 * This routine exists for backward compatibility with Smail alpha
 * releases prior to version 3.1.12.
 */
void
tpd_smtp(addr, succeed, defer, fail)
    struct addr *addr;			/* recipient addresses for transport */
    struct addr **succeed;		/* successful deliveries */
    struct addr **defer;		/* defer until a later queue run */
    struct addr **fail;			/* failed deliveries */
{
    tpd_tcpsmtp(addr, succeed, defer, fail);
}


/*
 * tpb_tcpsmtp - read the configuration file attributes
 */
char *
tpb_tcpsmtp(tp, attrs)
    struct transport *tp;		/* transport entry being defined */
    struct attribute *attrs;		/* list of per-driver attributes */
{
    char *error;
    static struct attr_table tcpsmtp_attributes[] = {
	{ "short_timeout", t_proc, NULL, (tup *)set_short_timeout, 0 },
	{ "long_timeout", t_proc, NULL, (tup *)set_long_timeout, 0 },
	{ "service", t_string, NULL, NULL, OFFSET(tcpsmtp_private, service) },
    };
    static struct attr_table *end_tcpsmtp_attributes =
	ENDTABLE(tcpsmtp_attributes);
    static struct tcpsmtp_private tcpsmtp_template = {
	5 * 60,				/* short timeout, 5 minutes */
	2 * 60 * 60,			/* long timeout, 2 hours */
	"smtp",				/* use the "smtp" service */
    };
    struct tcpsmtp_private *priv;	/* new tcpsmtp_private structure */

    /* copy the template private data */
    priv = (struct tcpsmtp_private *)xmalloc(sizeof(*priv));
    (void) memcpy((char *)priv, (char *)&tcpsmtp_template, sizeof(*priv));

    tp->private = (char *)priv;
    /* fill in the attributes of the private data */
    error = fill_attributes((char *)priv,
			    attrs,
			    &tp->flags,
			    tcpsmtp_attributes,
			    end_tcpsmtp_attributes);

    if (error) {
	return error;
    }
    return NULL;
}

/*
 * tpb_smtp - obsolescent name for tcpsmtp driver
 *
 * This routine exists for backward compatibility with Smail alpha
 * releases prior to version 3.1.12.
 */
char *
tpb_smtp(tp, attrs)
    struct transport *tp;		/* transport entry being defined */
    struct attribute *attrs;		/* list of per-driver attributes */
{
    return tpb_tcpsmtp(tp, attrs);
}

static char *
set_short_timeout(struct_p, attr)
    char *struct_p;			/* passed private structure */
    struct attribute *attr;		/* parsed attribute */
{
    struct tcpsmtp_private *priv = (struct tcpsmtp_private *)struct_p;

    return set_timeout(&priv->short_timeout, attr);
}

static char *
set_long_timeout(struct_p, attr)
    char *struct_p;			/* passed private structure */
    struct attribute *attr;		/* parsed attribute */
{
    struct tcpsmtp_private *priv = (struct tcpsmtp_private *)struct_p;

    return set_timeout(&priv->long_timeout, attr);
}

static char *
set_timeout(timeout, attr)
    unsigned *timeout;			/* set this timeout variable */
    struct attribute *attr;		/* parsed attribute */
{
    long l;

    if (attr->value == on) {
	return xprintf("%s: boolean form for non-boolean attribute",
		       attr->name);
    }
    l = ivaltol(attr->value);
    if (l < 0) {
	return xprintf("%s: %s: malformed interval",
		       attr->name, attr->value);
    }
    *timeout = (unsigned)l;
    if (*timeout != l) {
	return xprintf("%s: %s: interval too large", attr->name, attr->value);
    }
    return NULL;
}


/*
 * tcpsmtp_connect - return a socket connected to the remote host
 *
 * if the remote host is of the form [192.2.12.3] then use an explicit
 * inet address.
 */
static int
tcpsmtp_connect(remote_host, service, error)
    char *remote_host;
    char *service;
    char **error;
{
    static int port = 0;		/* port to connect to */
    struct servent *smtp_service;	/* service entry */
    struct hostent *hostentp;		/* addr for remote host */
    struct sockaddr_in sin;		/* inet socket address */
    static char *save_error = NULL;	/* keep handle to free error msgs */
    int s;				/* socket */
    char *error_text;

    if (isdigit(*service)) {
	error_text = NULL;
	port = c_atol(service, &error_text);
	if (error_text) {
	    *error = xprintf("invalid port: %s", service, error_text);
	    return -1;
	}
    } else if (port == NULL) {
	smtp_service = getservbyname(service, "tcp");
	if (smtp_service == NULL) {
	    *error = xprintf("service name %s not found", service);
	    return -1;
	}
	port = smtp_service->s_port;
    }

    bzero((char *)&sin, sizeof(sin));
    if (remote_host[0] == '[') {
	/* INET addr literal address */
	char *p = index(remote_host, ']');
	unsigned long inet_number;

	if (p == NULL || p[1] != '\0') {
	    *error = "Invalid host address";
	    return -1;
	} else {
	    *p = '\0';
	    inet_number = inet_addr(remote_host + 1);
	    *p = ']';
	}
	sin.sin_addr.s_addr = inet_number;
	sin.sin_family = AF_INET;
    } else {
	hostentp = gethostbyname(remote_host);
	if (hostentp == NULL) {
	    *error = "Unknown hostname";
	    return -1;
	}
	bcopy(hostentp->h_addr, (char *)&sin.sin_addr, hostentp->h_length);
	sin.sin_family = hostentp->h_addrtype;
    }
    sin.sin_port = port;
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
	if (save_error) {
	    xfree(save_error);
	}
	*error = save_error = xprintf("socket: %s", strerrno());
	return -1;
    }

    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
	if (save_error) {
	    xfree(save_error);
	}
	*error = save_error = xprintf("connect: %s", strerrno());
	(void) close(s);
	return -1;
    }

    return s;
}

static struct error *
connect_failure(tp, connect_error_text)
    struct transport *tp;
    char *connect_error_text;
{
    char *error_text;

    /*
     * ERR_148 - smtp connection failure
     *
     * DESCRIPTION
     *      We failed to connect to the smtp service of the remote
     *      host.  The reason is stored in `error'.
     *
     * ACTIONS
     *      The input addresses are deferred.
     *
     * RESOLUTION
     *      Hopefully we will connect on a retry.
     */
    error_text = xprintf("transport %s: %s", tp->name, connect_error_text);
    DEBUG1(DBG_DRIVER_LO, "%s\n", error_text);
    return note_error(ERR_148, error_text);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.