aboutsummaryrefslogtreecommitdiff
path: root/mfd/savclt.c
diff options
context:
space:
mode:
Diffstat (limited to 'mfd/savclt.c')
-rw-r--r--mfd/savclt.c275
1 files changed, 275 insertions, 0 deletions
diff --git a/mfd/savclt.c b/mfd/savclt.c
new file mode 100644
index 00000000..447f6857
--- /dev/null
+++ b/mfd/savclt.c
@@ -0,0 +1,275 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff
+
+ 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, 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/>. */
+
+#define MF_SOURCE_NAME MF_SOURCE_SAVCLT
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "mailfromd.h"
+
+struct sockaddr *callout_server_sa;
+socklen_t callout_server_sa_len;
+time_t savclt_timeout = 3;
+
+/* Ideally, I'd want to use smtp_io functions here. But they are
+ based on Mailutils' TCP streams, which so far do not support
+ UNIX sockets. So, for the time being, I use the straightforward
+ I/O approach. */
+int
+savclt_getline(int fd, char **pbuf, size_t *pbufsize)
+{
+ char *buf = *pbuf;
+ size_t bufsize = *pbufsize;
+ int off = 0;
+#define DELTA 128
+ time_t start = time(NULL);
+
+ if (buf == NULL) {
+ bufsize = DELTA;
+ buf = xmalloc(bufsize);
+ }
+
+ do {
+ struct timeval tv;
+ int rc, flags;
+ time_t diff = time(NULL) - start;
+
+ if (diff > savclt_timeout) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ tv.tv_sec = savclt_timeout - diff;
+ tv.tv_usec = 0;
+ flags = MU_STREAM_READY_RD;
+ rc = mu_fd_wait(fd, &flags, &tv);
+ if (rc) {
+ debug1(1, "mu_fd_wait: %s", mu_strerror(rc));
+ errno = rc;
+ return -1;
+ }
+ if (!(flags & MU_STREAM_READY_RD)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (off + 1 == bufsize) {
+ bufsize += DELTA;
+ buf = xrealloc(buf, bufsize);
+ }
+
+ rc = read(fd, buf + off, 1);
+ if (rc == -1)
+ return -1;
+ if (rc == 0)
+ break;
+ off++;
+ } while (buf[off - 1] != '\n');
+
+ if (off + 1 == bufsize) {
+ bufsize++;
+ buf = xrealloc(buf, bufsize);
+ }
+ buf[off] = 0;
+ *pbuf = buf;
+ *pbufsize = bufsize;
+ transcript("savclt", "RECV:", buf);
+ return off;
+}
+
+static int
+send_line(int fd, const char *txt, size_t len)
+{
+ time_t start = time(NULL);
+
+ while (len) {
+ struct timeval tv;
+ int rc, flags;
+ time_t diff = time(NULL) - start;
+
+ if (diff > savclt_timeout) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ tv.tv_sec = savclt_timeout - diff;
+ tv.tv_usec = 0;
+ flags = MU_STREAM_READY_WR;
+ rc = mu_fd_wait(fd, &flags, &tv);
+ if (rc) {
+ debug1(1, "mu_fd_wait: %s", mu_strerror(rc));
+ errno = rc;
+ return -1;
+ }
+ if (!(flags & MU_STREAM_READY_WR)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ rc = write(fd, txt, len);
+ if (rc <= 0) {
+ if (errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ txt += rc;
+ len -= rc;
+ }
+ return 0;
+}
+
+int
+savclt_wrtline(int fd, char **pbuf, size_t *pbufsize,
+ const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rc = mu_vasnprintf(pbuf, pbufsize, fmt, ap);
+ va_end(ap);
+ if (rc == 0) {
+ transcript("savclt", "SEND:", *pbuf);
+ rc = send_line(fd, *pbuf, strlen(*pbuf));
+ }
+ return rc;
+}
+
+
+static int
+connect_callout_server()
+{
+ int fd;
+ int flags;
+ int rc;
+
+ if (!callout_server_sa)
+ return -1;
+ fd = socket(callout_server_sa->sa_family, SOCK_STREAM, 0);
+ if (fd == -1) {
+ mu_error("schedule_callout: socket: %s",
+ mu_strerror(errno));
+ return -1;
+ }
+
+ flags = fcntl(fd, F_GETFL);
+ flags |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, flags);
+
+ rc = connect(fd, callout_server_sa, callout_server_sa_len);
+ if (rc == -1) {
+ if (errno == EINPROGRESS) {
+ struct timeval tv;
+
+ tv.tv_sec = savclt_timeout;
+ tv.tv_usec = 0;
+ flags = MU_STREAM_READY_WR;
+ rc = mu_fd_wait(fd, &flags, &tv);
+ if (rc) {
+ debug1(1, "mu_fd_wait: %s", mu_strerror(rc));
+ close(fd);
+ return -1;
+ }
+
+ if (flags & MU_STREAM_READY_WR) {
+ socklen_t len = sizeof(flags);
+ rc = getsockopt(fd, SOL_SOCKET, SO_ERROR,
+ &flags, &len);
+ if (rc) {
+ mu_error("getsockopt: %s",
+ mu_strerror(rc));
+ close(fd);
+ return -1;
+ }
+ } else
+ flags = ETIMEDOUT;
+ } else
+ flags = errno;
+
+ if (flags) {
+ mu_error("connect: %s", mu_strerror(flags));
+ close(fd);
+ return -1;
+ }
+ }
+
+ if (rc) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+enum callout_server_state {
+ css_init,
+ css_send,
+ css_quit,
+};
+
+void
+schedule_callout(const char *email, const char *ehlo, const char *mailfrom,
+ const char *client_addr)
+{
+ size_t size = 0;
+ char *buf = NULL;
+ int fd = connect_callout_server();
+ enum callout_server_state state = css_init;
+ int rc;
+
+ while (state != css_quit) {
+ if (savclt_getline(fd, &buf, &size) < 0)
+ break;
+ trimcrlf(buf);
+ if (!(strlen(buf) > 2 && memcmp(buf, "OK", 2) == 0
+ && isspace(buf[2])))
+ break;
+
+ switch (state) {
+ case css_init:
+ if (client_addr)
+ rc = savclt_wrtline(fd, &buf, &size,
+ "SVRFY \"%s\" \"%s\" \"%s\" \"%s\"\r\n",
+ email, ehlo, mailfrom, client_addr);
+ else
+ rc = savclt_wrtline(fd, &buf, &size,
+ "VRFY \"%s\" \"%s\" \"%s\"\r\n",
+ email, ehlo, mailfrom);
+ state = (rc < 0) ? css_quit : css_send;
+ break;
+
+ case css_send:
+ savclt_wrtline(fd, &buf, &size, "QUIT\r\n");
+ break;
+
+ case css_quit:
+ break;
+ }
+ }
+ close(fd);
+ free(buf);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.