Commit 761fe519 authored by IKEDA Soji's avatar IKEDA Soji
Browse files

Issue #201: Move smtpc to sympa-contribs to Move smtpc to independent repository

Now smtpc is there: https://github.com/ikedas/smtpc.git
parent f4442824
......@@ -24,9 +24,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
SUBDIRS = src default doc po www
if SMTPC
SUBDIRS += src/smtpc
endif
check_SCRIPTS = \
t/Language.t \
......@@ -92,11 +89,6 @@ authorcheck:
$(PERL) -MTest::Harness -e 'runtests @ARGV' $$TEST_FILES
install-data-hook: installdir installconfig nextstep
if SMTPC
-@chown $(USER) $(DESTDIR)$(bindir)/sympa_smtpc
-@chgrp $(GROUP) $(DESTDIR)$(bindir)/sympa_smtpc
-@chmod 750 $(DESTDIR)$(bindir)/sympa_smtpc
endif
installdir:
@echo "Creating plugin directory"
......
......@@ -118,22 +118,6 @@ AC_SUBST(localedir)
AC_SUBST(confdir)
AC_SUBST(docdir)
## Disable smtpc command line utility
AC_ARG_ENABLE(
smtpc,
AS_HELP_STRING(
[--disable-smtpc],
[do not install smtpc command line utility (default is yes)]
),
[case "$enableval" in
yes) smtpc=true;;
no) smtpc=false;;
*) smtpc=true;;
esac],
[smtpc=true]
)
AM_CONDITIONAL([SMTPC], [test x$smtpc = xtrue])
# allow user to redefine some of them
AC_ARG_WITH(
confdir,
......@@ -617,8 +601,4 @@ AC_CONFIG_FILES([
www/Makefile
])
AC_CONFIG_SUBDIRS([src/smtpc])
SMTPC_CONFIGURE_OPTIONS="--program-prefix=sympa_"
export SMTPC_CONFIGURE_OPTIONS
AC_OUTPUT
......@@ -172,10 +172,6 @@ L<wwsympa-wrapper(8)>
TBD.
=item L<sympa_smtpc(1)>
SMTP / LMTP client
=back
=head2 Configuration files
......
*.1
*.o
Makefile
Makefile.in
/.deps
/aclocal.m4
/autom4te.cache
/compile
/config.h
/config.h.in
/config.log
/config.status
/configure
/depcomp
/install-sh
/missing
/smtpc.1
/stamp-h1
/smtpc
# $Id$
ACLOCAL_AMFLAGS = -I m4
bin_PROGRAMS = smtpc
smtpc_SOURCES = \
smtpc.c \
sockstr.c \
sockstr.h \
utf8.c \
utf8.h
dist_doc_DATA = smtpc.1.md
EXTRA_DIST = configure.gnu
CLEANFILES = $(bin_PROGRAMS) *~ *.bak core.*
smtpc.o: sockstr.h utf8.h
# -*- Autoconf -*-
# $Id$
AC_PREREQ([2.59])
AC_INIT([smtpc], [0.3], [sympa-developpers@listes.renater.fr])
AM_INIT_AUTOMAKE([foreign -Wall -Werror 1.9 tar-pax])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_HEADERS([config.h])
# Check options
# If you bundle me in other package, use --disable-smtpc not to build me.
AC_ARG_ENABLE(
smtpc, [],
[case "$enableval" in
yes) smtpc=true;;
no) smtpc=false;;
*) smtpc=true;;
esac],
[smtpc=true]
)
# Checks for programs.
AC_PROG_CC
if test x$smtpc = xtrue; then
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h netdb.h stdlib.h string.h sys/socket.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_CHECK_SIZEOF([unsigned int])
AC_CHECK_SIZEOF([unsigned long])
# Checks for library functions.
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([memset select socket strdup strerror])
AC_FUNC_SNPRINTF
AC_CHECK_FUNC([getaddrinfo], [],
[AC_CHECK_LIB([socket], [getaddrinfo], [LIBS="-lsocket -lnsl $LIBS"], [],
[-lnsl])])
fi
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
# $Id$
args=
for arg; do
case "$arg" in
*\'*)
arg=`cat <<EOF | sed -e "s/'/'\\''/g"`
$arg
EOF
;;
*)
;;
esac
args="$args '$arg'"
done
cat <<EOF
configure.gnu: running ./configure $args $SMTPC_CONFIGURE_OPTIONS
EOF
eval "./configure $args $SMTPC_CONFIGURE_OPTIONS"
dnl @synopsis AC_FUNC_SNPRINTF
dnl
dnl Checks for a fully C99 compliant snprintf, in particular checks
dnl whether it does bounds checking and returns the correct string
dnl length; does the same check for vsnprintf. If no working snprintf
dnl or vsnprintf is found, request a replacement and warn the user
dnl about it. Note: the mentioned replacement is freely available and
dnl may be used in any project regardless of it's licence (just like
dnl the autoconf special exemption).
dnl
dnl @category C
dnl @author Rüdiger Kuhlmann <info@ruediger-kuhlmann.de>
dnl @version 2002-09-26
dnl @license AllPermissive
AC_DEFUN([AC_FUNC_SNPRINTF],
[AC_CHECK_FUNCS(snprintf vsnprintf)
AC_MSG_CHECKING(for working snprintf)
AC_CACHE_VAL(ac_cv_have_working_snprintf,
[AC_TRY_RUN(
[#include <stdio.h>
int main(void)
{
char bufs[5] = { 'x', 'x', 'x', '\0', '\0' };
char bufd[5] = { 'x', 'x', 'x', '\0', '\0' };
int i;
i = snprintf (bufs, 2, "%s", "111");
if (strcmp (bufs, "1")) exit (1);
if (i != 3) exit (1);
i = snprintf (bufd, 2, "%d", 111);
if (strcmp (bufd, "1")) exit (1);
if (i != 3) exit (1);
exit(0);
}], ac_cv_have_working_snprintf=yes, ac_cv_have_working_snprintf=no, ac_cv_have_working_snprintf=cross)])
AC_MSG_RESULT([$ac_cv_have_working_snprintf])
AC_MSG_CHECKING(for working vsnprintf)
AC_CACHE_VAL(ac_cv_have_working_vsnprintf,
[AC_TRY_RUN(
[#include <stdio.h>
#include <stdarg.h>
int my_vsnprintf (char *buf, const char *tmpl, ...)
{
int i;
va_list args;
va_start (args, tmpl);
i = vsnprintf (buf, 2, tmpl, args);
va_end (args);
return i;
}
int main(void)
{
char bufs[5] = { 'x', 'x', 'x', '\0', '\0' };
char bufd[5] = { 'x', 'x', 'x', '\0', '\0' };
int i;
i = my_vsnprintf (bufs, "%s", "111");
if (strcmp (bufs, "1")) exit (1);
if (i != 3) exit (1);
i = my_vsnprintf (bufd, "%d", 111);
if (strcmp (bufd, "1")) exit (1);
if (i != 3) exit (1);
exit(0);
}], ac_cv_have_working_vsnprintf=yes, ac_cv_have_working_vsnprintf=no, ac_cv_have_working_vsnprintf=cross)])
AC_MSG_RESULT([$ac_cv_have_working_vsnprintf])
if test x$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf != "xyesyes"; then
AC_LIBOBJ(snprintf)
AC_MSG_WARN([Replacing missing/broken (v)snprintf() with version from http://www.ijs.si/software/snprintf/.])
AC_DEFINE(PREFER_PORTABLE_SNPRINTF, 1, "enable replacement (v)snprintf if system (v)snprintf is broken")
fi])
%SMTPC(1)
# NAME
smtpc - SMTP / LMTP client
# SYNOPSIS
`smtpc` `--esmtp` _host_`:`_port_ `-f` _envelope_@_sen.der_
[ _options_... ] [ `--` ] _recipient_@_add.ress_ ...
`smtpc` `--lmtp` _host_`:`_port_ `-f` _envelope_@_sen.der_
[ _options_... ] [ `--` ] _recipient_@_add.ress_ ...
`smtpc` `--lmtp` _path_ `-f` _envelope_@_sen.der_
[ _options_... ] [ `--` ] _recipient_@_add.ress_ ...
# DESCRIPTION
**smtpc** is an email client.
It reads a message from standard input and submits it to email server through
socket.
## Options
Any options not listed here are silently ignored.
* `--dump`
Show dialog in the session.
* `--esmtp` _host_[:_port_]
Uses TCP socket and ESMTP protocol to submit message.
Either this option or `--lmtp` option is required.
If _host_ is the IPv6 address, it must be enclosed in [...]
to avoid confusion with colon separating _host_ and _port_,
e.g. "`[::1]`", "`[::1]:587`".
If _port_ is omitted, "25" is used.
* `-f` _envelope_@_sen.der_, `-f`_envelope_@_sen.der_
Specifies envelope sender.
This option is required.
To specify "null envelope sender", use a separate empty argument or "`<>`".
* `--iam` _host.name_
Specifies host name or IP address literal used in EHLO or LHLO request.
Default is "`localhost`".
* `--lmtp` _host_[:_port_], `--lmtp` _path_
Uses TCP or Unix domain socket and LMTP protocol to submit message.
Either this option or `--esmtp` option is required.
If _port_ is omitted, "24" is used.
_path_ must be full path to socket file.
* `-N` _dsn_, `-N`_dsn_
Controls delivery status notification.
_dsn_ may be single word "`NEVER`" or one or more of words "`SUCCESS`",
"`FAILURE`" and "`DELAY`" separated by comma.
If this option is not given, delivery status notification will be controlled
by server.
* `--smtputf8`
Enables support for SMTPUTF8 extension.
**smtpc** detects valid UTF-8 sequence in envelope and message header,
then requests this extension as necessity.
* `-V` _envid_, `-V`_envid_
Specifies envelope ID.
* `--verbose`
Output the last response from the server to standard output.
* `--`
Terminates options.
Remainder of command line arguments are considered to be recipient
addresses, even if any of them begin with "`-`".
* _recipient_@_add.ress_ ...
Recipients to whom the message would be delivered.
At least one recipient is required.
## Exit status
* `0`
Message was successfully submitted.
* `253`
Message was rejected by server.
* `254`
The server returns malformed or illegal response.
* `255`
Network error occurred.
## SMTP extensions
**smtpc** supports following extensions.
* **8-bit MIME Transport** (RFC 6152)
**smtpc** requests this extension if message contains octets with high bit.
* **Delivery Status Notification** (RFC 3461)
**smtpc** issues ORCPT parameters.
See also `-N` and `-V` options.
* **Internationalized Email** (RFC 6531)
Experimentally supported.
See `--smtputf8` option.
* **Message Size Declaration** (RFC 1870)
Estimated size of the message is informed to the server.
# LIMITATIONS
**smtpc** provides the feature of SMTP / LMTP client submitting messages
to particular server.
It will never provide extensive features such as message queuing, retry after
temporary failure, routing using MX DNS record and so on.
Once the server rejects delivery, **smtpc** exits and message is discarded.
# KNOWN BUGS
* If NUL octets (\\0) are included in messages, they are transmitted to the
server.
# SEE ALSO
sendmail(1)
# HISTORY
**smtpc** was initially written for Sympa project by
IKEDA Soji <ikeda@conversion.co.jp>.
This diff is collapsed.
/* $Id$ */
/*
* Sympa - SYsteme de Multi-Postage Automatique
*
* Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel
* Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER
*
* 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 2 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/>.
*/
/*
* sockstr.c was originally written by IKEDA Soji <ikeda@conversion.co.jp>
* as a part of smtpc utility for Sympa project.
*
* 2015-05-17 IKEDA Soji: Initial checkin to source repository.
*/
#include "config.h"
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/un.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include "sockstr.h"
/** Constructor
* Creats new instance of sockstr object.
* @param[in] nodename Hostname of the server. Default is "localhost".
* @param[in] servname Port number or service name. Default is "smtp".
* @returns New instance.
* If error occurred, sets errno and returns NULL.
*/
sockstr_t *sockstr_new(char *nodename, char *servname, char *path)
{
sockstr_t *self;
self = (sockstr_t *) malloc(sizeof(sockstr_t));
if (self == NULL)
return NULL;
self->_errstr[0] = '\0';
self->_sock = -1;
self->_bufcnt = 0;
self->_bufptr = self->_buf;
self->timeout = 300;
if (path != NULL && *path) {
self->path = strdup(path);
if (self->path == NULL) {
free(self);
return NULL;
}
self->nodename = NULL;
self->servname = NULL;
} else {
self->path = NULL;
if (!nodename || !*nodename)
nodename = "localhost";
if (!servname || !*servname)
servname = "25";
self->nodename = strdup(nodename);
self->servname = strdup(servname);
if (self->nodename == NULL || self->servname == NULL) {
if (self->nodename != NULL)
free(self->nodename);
if (self->servname != NULL)
free(self->servname);
free(self);
return NULL;
}
}
return self;
}
/** Destructor
* Unallocate memory for the instance.
* @retuns None.
*/
void sockstr_destroy(sockstr_t * self)
{
if (self == NULL)
return;
if (0 <= self->_sock) {
shutdown(self->_sock, SHUT_RDWR);
close(self->_sock);
}
if (self->nodename != NULL)
free(self->nodename);
if (self->servname != NULL)
free(self->servname);
if (self->path != NULL)
free(self->path);
free(self);
}
static void sockstr_set_error(sockstr_t * self, int errnum,
const char *errstr)
{
self->_errnum = errnum;
if (errstr == NULL) {
char *buf;
buf = strerror(errnum);
if (buf == NULL)
snprintf(self->_errstr, SOCKSTR_ERRSIZE, "(%d) Error", errnum);
else
snprintf(self->_errstr, SOCKSTR_ERRSIZE, "(%d) %s", errnum,
buf);
} else
snprintf(self->_errstr, SOCKSTR_ERRSIZE, "(%d) %s", errnum,
errstr);
}
/** Last error
* Gets error by the last operation.
* @returns String, or if the last operation was success, NULL.
*/
char *sockstr_errstr(sockstr_t * self)
{
return self->_errstr;
}
static int _connect_socket(sockstr_t * self, int sock,
struct sockaddr *ai_addr, socklen_t ai_addrlen,
int blocking)
{
long flags;
flags = fcntl(sock, F_GETFL, NULL);
if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
sockstr_set_error(self, errno, NULL);
return -1;
}
if (connect(sock, ai_addr, ai_addrlen) < 0) {
if (errno == EINPROGRESS) {
struct timeval tv;
fd_set rfd, wfd;
int rc, errnum;
socklen_t errlen;
do {
tv.tv_sec = self->timeout;
tv.tv_usec = 0;
FD_ZERO(&rfd);
FD_SET(sock, &rfd);
wfd = rfd;
rc = select(sock + 1, &rfd, &wfd, NULL, &tv);
} while (rc < 0 && errno == EINTR);
if (rc == 0) {
sockstr_set_error(self, ETIMEDOUT, NULL);
return -1;
} else if (FD_ISSET(sock, &rfd) || FD_ISSET(sock, &wfd)) {
errlen = sizeof(errnum);
getsockopt(sock, SOL_SOCKET, SO_ERROR,
(void *) &errnum, &errlen);
if (errnum) {
sockstr_set_error(self, errnum, NULL);
return -1;
}
} else {
sockstr_set_error(self, errno, NULL);
return -1;
}
} else {
sockstr_set_error(self, errno, NULL);
return -1;
}
}
if (blocking) {
if (fcntl(sock, F_SETFL, flags) < 0) {
sockstr_set_error(self, errno, NULL);
return -1;
}
}
return 0;
}
int socktcp_connect(sockstr_t * self)
{
struct addrinfo hints, *ai0, *ai;
int errnum;
int sock = -1;
if (0 <= self->_sock) {
sockstr_set_error(self, EISCONN, NULL);
return -1;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
errnum = getaddrinfo(self->nodename, self->servname, &hints, &ai0);
if (errnum) {
sockstr_set_error(self, errnum, gai_strerror(errnum));
return -1;
}
for (ai = ai0; ai != NULL; ai = ai->ai_next) {
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {