httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From soc-r...@apache.org
Subject svn commit: r290254 - in /httpd/mod_smtpd/trunk/modules/queue/smtp: ./ Makefile mod_smtpd_queue_smtp.c
Date Mon, 19 Sep 2005 20:16:35 GMT
Author: soc-rian
Date: Mon Sep 19 13:16:30 2005
New Revision: 290254

URL: http://svn.apache.org/viewcvs?rev=290254&view=rev
Log:
Initial check-in of mod_smptd_queue_smtp, Submitted by: Garrett Rooney,
rooneg at electricjellyfish dot com, with some minor alterations by
Rian Hunter.

Added:
    httpd/mod_smtpd/trunk/modules/queue/smtp/
    httpd/mod_smtpd/trunk/modules/queue/smtp/Makefile
    httpd/mod_smtpd/trunk/modules/queue/smtp/mod_smtpd_queue_smtp.c

Added: httpd/mod_smtpd/trunk/modules/queue/smtp/Makefile
URL: http://svn.apache.org/viewcvs/httpd/mod_smtpd/trunk/modules/queue/smtp/Makefile?rev=290254&view=auto
==============================================================================
--- httpd/mod_smtpd/trunk/modules/queue/smtp/Makefile (added)
+++ httpd/mod_smtpd/trunk/modules/queue/smtp/Makefile Mon Sep 19 13:16:30 2005
@@ -0,0 +1,19 @@
+include ../../../include.mk
+
+CFLAGS=-Wall
+APXSFLAGS=-I../../..
+SRCS=mod_smtpd_queue_smtp.c
+
+all: mod_smtpd_queue_smtp.la
+
+mod_smtpd_queue_smtp.la: $(SRCS)
+	$(APXS) -Wc,"$(CFLAGS)" $(APXSFLAGS) -o \
+	mod_smtpd_queue_smtp.la -c $(SRCS)
+
+install: all
+	$(APXS) -i -a mod_smtpd_queue_smtp.la
+
+clean-objs:
+	rm -rf *.lo *.o *.slo *~ *.la .libs
+
+clean: clean-objs

Added: httpd/mod_smtpd/trunk/modules/queue/smtp/mod_smtpd_queue_smtp.c
URL: http://svn.apache.org/viewcvs/httpd/mod_smtpd/trunk/modules/queue/smtp/mod_smtpd_queue_smtp.c?rev=290254&view=auto
==============================================================================
--- httpd/mod_smtpd/trunk/modules/queue/smtp/mod_smtpd_queue_smtp.c (added)
+++ httpd/mod_smtpd/trunk/modules/queue/smtp/mod_smtpd_queue_smtp.c Mon Sep 19 13:16:30 2005
@@ -0,0 +1,520 @@
+/* Copyright 2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+
+#include "apr_version.h"
+
+#include "mod_smtpd.h"
+
+/* mod_smtpd_queue_smtp - a mod_smtpd queue handler that sends messages
+ * to an alternative SMTP server for delivery, so mod_smtpd can easily be
+ * integrated into an existing mail system.  */
+
+#define BUFFER_LEN 1024
+
+/* old versions of APR don't have APR_ARRAY_IDX, so define our own copy if
+ * it's not there already. */
+#ifndef APR_ARRAY_IDX
+#define APR_ARRAY_IDX(ary,i,type) (((type *)(ary)->elts)[i])
+#endif
+
+/* APR 1.0 changed the arguments for apr_socket_create, mask the difference
+ * by using apr_socket_create_ex in its place. */
+#if APR_MAJOR_VERSION < 1
+#define apr_socket_create apr_socket_create_ex
+#endif
+
+module AP_MODULE_DECLARE_DATA smtpd_queue_smtp_module;
+
+typedef struct {
+    const char *host;
+    int port;
+    int on; 
+} smtpd_queue_smtp_config;
+
+static void *smtpd_queue_smtp_create_server_config(apr_pool_t *p,
+                                                   server_rec *s)
+{
+  return apr_pcalloc(p, sizeof(smtpd_queue_smtp_config));
+}
+
+/* pull the message body out of the spool file and write it to a socket,
+ * one line at a time, doing the smtp dot encoding as we go. */
+static apr_status_t send_body(smtpd_conn_rec *scr,
+                              apr_socket_t *sock,
+                              apr_pool_t *pool)
+{
+    apr_status_t rv = APR_SUCCESS;
+    apr_bucket_brigade *bb, *tb;
+    char line[BUFFER_LEN];
+    apr_size_t nbytes;
+    apr_finfo_t finfo;
+
+    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, scr->transaction->tfp);
+    if (rv)
+        return rv;
+
+    bb = apr_brigade_create(pool, scr->c->bucket_alloc);
+    tb = apr_brigade_create(pool, scr->c->bucket_alloc);
+
+    APR_BRIGADE_INSERT_TAIL(
+        bb, apr_bucket_file_create(scr->transaction->tfp,
+                                   scr->transaction->body_offset,
+                                   finfo.size - scr->transaction->body_offset,
+                                   pool, scr->c->bucket_alloc)
+    );
+
+    /* loop over the lines in the file, writing them to the socket and
+     * dealing with single-period lines as we see them. */
+    for (;;) {
+        if (APR_BRIGADE_EMPTY(bb))
+            break;
+
+        memset(line, 0, sizeof(line));
+
+        rv = apr_brigade_split_line(tb, bb, APR_BLOCK_READ, sizeof(line));
+        if (rv)
+            break;
+
+        nbytes = sizeof(line);
+
+        apr_brigade_flatten(tb, line, &nbytes);
+
+        if (line[0] == '.' && line[1] == '\0') {
+            nbytes = 2;
+
+            rv = apr_socket_send(sock, "..", &nbytes);
+        } else {
+            nbytes = strlen(line);
+
+            rv = apr_socket_send(sock, line, &nbytes);
+        }
+
+        if (rv)
+            break;
+
+        apr_brigade_cleanup(tb);
+    }
+
+    apr_brigade_destroy(bb);
+    apr_brigade_destroy(tb);
+
+    /* now finish things off with a single period. */
+    if (! rv) {
+        nbytes = 3;
+
+        rv = apr_socket_send(sock, ".\r\n", &nbytes);
+    }
+
+    return rv;
+}
+
+/* read and parse a line of response from the server, returning the response
+ * code in *respcode and the response text in *resptext.  the line is placed
+ * in buffer, which is buflen bytes long. */
+static apr_status_t read_response(int *respcode,
+                                  char **resptext,
+                                  apr_bucket_brigade *bb,
+                                  apr_bucket_brigade *tb,
+                                  char *buffer,
+                                  apr_size_t buflen)
+{
+    apr_status_t rv = APR_SUCCESS;
+    apr_size_t nbytes;
+    char *text;
+
+    rv = apr_brigade_split_line(tb, bb, APR_BLOCK_READ, buflen);
+    if (rv)
+        return rv;
+
+    nbytes = buflen;
+
+    apr_brigade_flatten(tb, buffer, &nbytes);
+
+    apr_brigade_cleanup(tb);
+
+    *respcode = atoi(buffer);
+
+    text = strchr(buffer, ' ');
+
+    if (text)
+      *resptext = text + 1;
+    else
+      *resptext = NULL;
+
+    return rv;
+}
+
+/* Queue a message by sending it to a remote server for delivery.
+ *
+ * TODO: errors when sending mail on to the remote server should propogate
+ *       back to the sender. */
+static smtpd_retcode queue_message(smtpd_conn_rec *scr, smtpd_return_data *in)
+{
+    apr_status_t apr_err = APR_SUCCESS;
+    smtpd_retcode rv = SMTPD_DECLINED;
+    apr_pool_t *subpool;
+
+    smtpd_queue_smtp_config *cfg = ap_get_module_config(
+        scr->s->module_config, &smtpd_queue_smtp_module
+    );
+
+    if (! cfg->on)
+        return SMTPD_DECLINED;
+    else {
+        if (cfg->host == NULL) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "SmtpQueueSmtp enabled but host not set");
+            return SMTPD_DECLINED;
+        }
+
+        if (cfg->port == 0) {
+           ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                        "SmtpQueueSmtp enabled but port not set");
+           return SMTPD_DECLINED;
+        }
+
+        ap_log_error(APLOG_MARK, APLOG_INFO, 0, scr->s,
+                     "Queuing message to smtp://%s:%d", cfg->host, cfg->port);
+    }
+
+    apr_pool_create(&subpool, scr->p);
+
+    /* note: this is perhaps the stupidest SMTP client implementation
+     *       ever, i appologize for it profusely, but it's the minimum
+     *       we can get away with and still have this work. */
+
+    do {
+        apr_bucket_brigade *bb, *tb;
+        char line[BUFFER_LEN];
+        apr_sockaddr_t *saddr;
+        apr_socket_t *sock;
+        apr_size_t nbytes;
+        char *text;
+        int resp;
+        int i;
+
+        apr_err = apr_sockaddr_info_get(&saddr, cfg->host, APR_UNSPEC,
+                                        cfg->port, 0, subpool);
+        if (apr_err)
+            break;
+
+        /* open a connection to our target server. */
+        apr_err = apr_socket_create(&sock, saddr->family, SOCK_STREAM, 0,
+                                    subpool);
+        if (apr_err)
+            break;
+
+        apr_err = apr_socket_connect(sock, saddr);
+        if (apr_err)
+            break;
+
+        /* wrap a bucket brigade around our socket so we can easily read
+         * a line at a time from it. */
+        bb = apr_brigade_create(subpool, scr->c->bucket_alloc);
+
+        APR_BRIGADE_INSERT_TAIL(
+            bb, apr_bucket_socket_create(sock, scr->c->bucket_alloc)
+        );
+
+        tb = apr_brigade_create(subpool, scr->c->bucket_alloc);
+
+        apr_err = read_response(&resp, &text, bb, tb, line, sizeof(line));
+        if (apr_err)
+            break;
+
+        if (resp != 220) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "bad response from smtp server: "
+                         "got %d, expected 220", resp);
+
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "server said '%s'", text);
+            break;
+        }
+
+        nbytes = 16;
+
+        /* say hello to the server. */
+
+        {
+          struct iovec vec[3];
+
+          vec[0].iov_base = "HELO ";
+          vec[0].iov_len = 5;
+
+          vec[1].iov_base = scr->s->server_hostname;
+          vec[1].iov_len = strlen(scr->s->server_hostname);
+
+          vec[2].iov_base = "\r\n";
+          vec[2].iov_len = 2;
+
+          apr_err = apr_socket_sendv(sock, vec, 3, &nbytes);
+          if (apr_err)
+              break;
+        }
+
+        apr_err = read_response(&resp, &text, bb, tb, line, sizeof(line));
+        if (apr_err)
+            break;
+
+        if (resp != 250) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "bad response from smtp server: "
+                         "got %d, expected 250", resp);
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "server said '%s'", text);
+            break;
+        }
+
+        /* identify the sender. */
+
+        {
+            struct iovec vec[4];
+
+            vec[0].iov_base = "MAIL FROM:";
+            vec[0].iov_len = 10;
+
+            vec[1].iov_base = "<";
+            vec[1].iov_len = 1;
+
+            vec[2].iov_base = scr->transaction->mail_from;
+            vec[2].iov_len = strlen(scr->transaction->mail_from);
+
+            vec[3].iov_base = ">\r\n";
+            vec[3].iov_len = 3;
+
+            apr_err = apr_socket_sendv(sock, vec, 4, &nbytes);
+            if (apr_err)
+                break;
+        }
+
+        apr_err = read_response(&resp, &text, bb, tb, line, sizeof(line));
+        if (apr_err)
+            break;
+
+        if (resp != 250) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "bad response from smtp server: "
+                         "got %d, expected 250", resp);
+
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "server said '%s'", text);
+            break;
+        }
+
+        /* tell the server who we're sending the message to. */
+
+        for (i = 0; i < scr->transaction->rcpt_to->nelts; ++i) {
+            struct iovec vec[4];
+
+            char *to = APR_ARRAY_IDX(scr->transaction->rcpt_to, i, char *);
+
+            vec[0].iov_base = "RCPT TO:";
+            vec[0].iov_len = 8;
+
+            vec[1].iov_base = "<";
+            vec[1].iov_len = 1;
+
+            vec[2].iov_base = to;
+            vec[2].iov_len = strlen(to);
+
+            vec[3].iov_base = ">\r\n";
+            vec[3].iov_len = 3;
+
+            apr_err = apr_socket_sendv(sock, vec, 4, &nbytes);
+            if (apr_err)
+                break;
+
+            apr_err = read_response(&resp, &text, bb, tb, line, sizeof(line));
+            if (apr_err)
+                break;
+
+            if (resp != 250 && resp != 251) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                             "bad response from smtp server: "
+                             "got %d, expected 250 or 251", resp);
+
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                             "server said '%s'", text);
+
+                apr_err = APR_EINVAL;
+
+                break;
+            }
+        }
+
+        /* if we ended up with an error from the loop bail. */
+        if (apr_err)
+            break;
+
+        /* now start the actual contents of the message. */
+
+        nbytes = 6;
+
+        apr_err = apr_socket_send(sock, "DATA\r\n", &nbytes);
+        if (apr_err)
+            break;
+
+        apr_err = read_response(&resp, &text, bb, tb, line, sizeof(line));
+        if (apr_err)
+            break;
+
+        if (resp != 354) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "bad response from smtp server: "
+                         "got %d, expected 354", resp);
+
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "server said '%s'", text);
+            break;
+        }
+
+        /* first we send the headers. */
+
+        {
+            const apr_array_header_t *arr;
+            const apr_table_entry_t *elts;
+
+            arr = apr_table_elts(scr->transaction->headers);
+            elts = (const apr_table_entry_t *) arr->elts;
+
+            for (i = 0; i < arr->nelts; ++i) {
+                struct iovec vec[4];
+
+                vec[0].iov_base = elts[i].key;
+                vec[0].iov_len = strlen(elts[i].key);
+
+                vec[1].iov_base = ": ";
+                vec[1].iov_len = 2;
+
+                vec[2].iov_base = elts[i].val;
+                vec[2].iov_len = strlen(elts[i].val);
+
+                vec[3].iov_base = "\r\n";
+                vec[3].iov_len = 2;
+
+                apr_err = apr_socket_sendv(sock, vec, 4, &nbytes);
+                if (apr_err)
+                    break;
+            }
+
+            if (apr_err)
+                break;
+        }
+
+        /* then finally the body. */
+
+        apr_err = send_body(scr, sock, subpool);
+        if (apr_err)
+            break;
+
+        apr_err = read_response(&resp, &text, bb, tb, line, sizeof(line));
+        if (apr_err)
+            break;
+
+        if (resp != 250) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "bad response from smtp server: "
+                         "got %d, expected 250", resp);
+
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, scr->s,
+                         "server said '%s'", text);
+            break;
+        }
+
+        /* if we made it this far the message has been sent, call it a day */
+        rv = SMTPD_OK;
+    } while (0);
+
+    if (apr_err)
+        ap_log_error(APLOG_MARK, APLOG_ERR, apr_err, scr->s,
+                     "Error queuing to smtp server");
+
+    apr_pool_destroy(subpool);
+
+    return rv;
+}
+
+static void smtpd_queue_smtp_register_hooks(apr_pool_t * p)
+{
+    APR_OPTIONAL_HOOK(smtpd, queue, queue_message, NULL, NULL, APR_HOOK_FIRST);
+}
+
+static const char *enable_queue_smtp(cmd_parms *cmd,
+                                     void *baton,
+                                     int arg)
+{
+    smtpd_queue_smtp_config *cfg = ap_get_module_config(
+        cmd->server->module_config, &smtpd_queue_smtp_module
+    );
+
+    cfg->on = arg;
+
+    return NULL;
+}
+
+static const char *set_queue_smtp_host(cmd_parms *cmd,
+                                       void *baton,
+                                       const char *arg)
+{
+    smtpd_queue_smtp_config *cfg = ap_get_module_config(
+        cmd->server->module_config, &smtpd_queue_smtp_module
+    );
+
+    cfg->host = arg;
+
+    return NULL;
+}
+
+static const char *set_queue_smtp_port(cmd_parms *cmd,
+                                       void *baton,
+                                       const char *arg)
+{
+    smtpd_queue_smtp_config *cfg = ap_get_module_config(
+        cmd->server->module_config, &smtpd_queue_smtp_module
+    );
+
+    cfg->port = atoi(arg);
+
+    return NULL;
+}
+
+static const command_rec smtpd_queue_smtp_cmds[] = {
+    AP_INIT_FLAG("SmtpQueueSmtp", enable_queue_smtp, NULL, RSRC_CONF,
+                 "Enable queuing to another SMTP server.  Default: Off"),
+
+    AP_INIT_TAKE1("SmtpQueueSmtpHost", set_queue_smtp_host, NULL, RSRC_CONF,
+                  "Set the hostname for the remote SMTP server."),
+
+    AP_INIT_TAKE1("SmtpQueueSmtpPort", set_queue_smtp_port, NULL, RSRC_CONF,
+                  "Set the port for the remote SMTP server."),
+
+    { NULL }
+};
+
+module AP_MODULE_DECLARE_DATA smtpd_queue_smtp_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,
+    NULL,
+    smtpd_queue_smtp_create_server_config,
+    NULL,
+    smtpd_queue_smtp_cmds,
+    smtpd_queue_smtp_register_hooks
+};



Mime
View raw message