]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-20011015
authorWietse Venema <wietse@porcupine.org>
Mon, 15 Oct 2001 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:27:28 +0000 (06:27 +0000)
29 files changed:
postfix/HISTORY
postfix/conf/sample-filter.cf
postfix/src/bounce/bounce.c
postfix/src/cleanup/cleanup.c
postfix/src/cleanup/cleanup.h
postfix/src/cleanup/cleanup_api.c
postfix/src/cleanup/cleanup_message.c
postfix/src/cleanup/cleanup_state.c
postfix/src/flush/flush.c
postfix/src/global/bounce.c
postfix/src/global/defer.c
postfix/src/global/mail_attr.h [new file with mode: 0644]
postfix/src/global/mail_command_read.c [new file with mode: 0644]
postfix/src/global/mail_command_write.c [new file with mode: 0644]
postfix/src/global/mail_connect.c
postfix/src/global/mail_stream.c
postfix/src/global/mail_stream.h
postfix/src/global/mail_version.h
postfix/src/postdrop/postdrop.c
postfix/src/qmqpd/qmqpd.c
postfix/src/qmqpd/qmqpd.h
postfix/src/qmqpd/qmqpd_state.c
postfix/src/sendmail/sendmail.c
postfix/src/smtpd/smtpd.c
postfix/src/util/Makefile.in
postfix/src/util/attr_print.c
postfix/src/util/attr_scan.c
postfix/src/util/base64_code.c [new file with mode: 0644]
postfix/src/util/base64_code.h [new file with mode: 0644]

index 827a7fba102aee0fb1d06cf1cfb9d63acda29af0..a9b3636bfb594ba5845c1b8fbbc97b5ba7fd631a 100644 (file)
@@ -5490,7 +5490,17 @@ Apologies for any names omitted.
 20011010-14
 
        Replaced the internal protocols by (name,value) attribute
-       lists. This is more extensible.
+       lists. This gives better error detection when we start
+       making changes to internal protocols.
+
+20011015
+
+       Put base 64 encoding into place on the replced internal
+       protocols.  Files: util/base64_code.[hc].
+
+       Feature: header/body REJECT rules can now end in text that
+       is sent to the originator. Files: cleanup/cleanup.c,
+       cleanup/cleanup_message.c, conf/sample-filter.cf.
 
 Open problems:
 
index 9093de5745a02bc71428df390497e5594d43db57..af68d4b3d9ca7827745aa14796f51cbaa13a07a0 100644 (file)
@@ -13,6 +13,8 @@
 #
 # REJECT the entire message is rejected.
 #
+# REJECT text.... The text is sent to the originator.
+#
 # IGNORE the header line is silently discarded.
 #
 # OK   Nothing happens. the message will still be rejected when some 
index 8cfcf7380e4c665e47f0a763bbeca5d299d23c77..6e85b0dfe7bbe64353aa057185ee1d9bbd43362c 100644 (file)
@@ -142,11 +142,12 @@ static int bounce_append_proto(char *service_name, VSTREAM *client)
     /*
      * Read the and validate the client request.
      */
-    if (mail_command_server(client, "%d %s %s %s",
+    if (mail_command_server(client,
                            ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
-                           ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
                            ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
-                           ATTR_TYPE_STR, MAIL_ATTR_WHY, why, 0) != 4) {
+                           ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
+                           ATTR_TYPE_END) != 4) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -181,11 +182,12 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush)
     /*
      * Read and validate the client request.
      */
-    if (mail_command_server(client, ATTR_FLAG_MISSING,
+    if (mail_command_server(client,
                            ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
-                        ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, 0) != 4) {
+                           ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_END) != 4) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -230,7 +232,8 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client, int flush)
                  ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
                  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
                  ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
-                 ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims, 0) != 5) {
+                 ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims,
+                 ATTR_TYPE_END) != 5) {
        msg_warn("malformed request");
        return (-1);
     }
index 1802c1a86cb215cb921d04f89aa755ae854f3dc0..cd8091898faf6bdd845364bfda4956424cbbdb04 100644 (file)
@@ -235,8 +235,10 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
      */
     attr_print(src, ATTR_FLAG_NONE,
               ATTR_TYPE_NUM, MAIL_ATTR_STATUS, cleanup_close(state),
-              ATTR_TYPE_STR, MAIL_ATTR_WHY, "",
+              ATTR_TYPE_STR, MAIL_ATTR_WHY, state->why_rejected ?
+              vstring_str(state->why_rejected) : "",
               ATTR_TYPE_END);
+    cleanup_free(state);
 
     /*
      * Cleanup.
index d5ae9c3cd712da90b631ff3fb26abe29b5ac48b6..5e2f38548682c588034e44e1b580bfc688adc496 100644 (file)
@@ -59,6 +59,7 @@ typedef struct CLEANUP_STATE {
     off_t   xtra_offset;               /* start of extra segment */
     int     end_seen;                  /* REC_TYPE_END seen */
     int     rcpt_count;                        /* recipient count */
+    VSTRING *why_rejected;             /* REJECT reason */
 } CLEANUP_STATE;
 
  /*
@@ -104,6 +105,7 @@ extern void cleanup_state_free(CLEANUP_STATE *);
 extern CLEANUP_STATE *cleanup_open(void);
 extern void cleanup_control(CLEANUP_STATE *, int);
 extern int cleanup_close(CLEANUP_STATE *);
+extern void cleanup_free(CLEANUP_STATE *);
 extern void cleanup_all(void);
 extern void cleanup_pre_jail(char *, char **);
 extern void cleanup_post_jail(char *, char **);
index 3d1e4d9cc231efa8b05a6cd437e6c81e8a47da88..37239fbbfe727175d67f4ac047c25e25acfa402d 100644 (file)
@@ -20,6 +20,9 @@
 /*
 /*     int     cleanup_close(state)
 /*     CLEANUP_STATE *state;
+/*
+/*     int     cleanup_free(state)
+/*     CLEANUP_STATE *state;
 /* DESCRIPTION
 /*     This module implements a callable interface to the cleanup service
 /*     for processing one message and for writing it to queue file.
@@ -27,7 +30,8 @@
 /*
 /*     cleanup_open() creates a new queue file and performs other
 /*     per-message initialization. The result is a handle that should be
-/*     given to the cleanup_control(), cleanup_record() and cleanup_close()
+/*     given to the cleanup_control(), cleanup_record(), cleanup_close()
+/*     and cleanup_close()
 /*     routines. The name of the queue file is in the queue_id result
 /*     structure member.
 /*
 /*     The result is false when further message processing is futile.
 /*     In that case, it is safe to call cleanup_close() immediately.
 /*
-/*     cleanup_close() finishes a queue file. In case of any errors,
+/*     cleanup_close() closes a queue file. In case of any errors,
 /*     the file is removed. The result value is non-zero in case of
 /*     problems. Use cleanup_strerror() to translate the result into
 /*     human_readable text.
+/*
+/*     cleanup_free() destroys its argument.
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8).
 /* SEE ALSO
@@ -161,6 +167,7 @@ int     cleanup_close(CLEANUP_STATE *state)
 {
     char   *junk;
     int     status;
+    const char *reason;
 
     /*
      * See if there are any errors. For example, the message is incomplete,
@@ -181,7 +188,7 @@ int     cleanup_close(CLEANUP_STATE *state)
      * copy of the message.
      */
     if ((state->errs & CLEANUP_STAT_LETHAL) == 0)
-       state->errs |= mail_stream_finish(state->handle);
+       state->errs |= mail_stream_finish(state->handle, (VSTRING *) 0);
     else
        mail_stream_cleanup(state->handle);
     state->handle = 0;
@@ -216,11 +223,14 @@ int     cleanup_close(CLEANUP_STATE *state)
 
     if (state->errs & CLEANUP_STAT_LETHAL) {
        if (CAN_BOUNCE()) {
+           reason = cleanup_strerror(state->errs);
+           if (reason == cleanup_strerror(CLEANUP_STAT_CONT))
+               reason = vstring_str(state->why_rejected);
            if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
                              state->recip ? state->recip : "unknown",
                              "cleanup", state->time,
                              "Message processing aborted: %s",
-                             cleanup_strerror(state->errs)) == 0
+                             reason) == 0
                && bounce_flush(BOUNCE_FLAG_CLEAN, MAIL_QUEUE_INCOMING,
                                state->queue_id, state->sender) == 0) {
                state->errs = 0;
@@ -249,6 +259,12 @@ int     cleanup_close(CLEANUP_STATE *state)
     if (msg_verbose)
        msg_info("cleanup_close: status %d", state->errs);
     status = state->errs & CLEANUP_STAT_LETHAL;
-    cleanup_state_free(state);
     return (status);
 }
+
+/* cleanup_close - pay the last respects */
+
+void    cleanup_free(CLEANUP_STATE *state)
+{
+    cleanup_state_free(state);
+}
index 2944e75daa99b5287e23e9dd366d40cbb3a49a65..6843d6c358ba706ff81cc447083f9ddeb91b8153 100644 (file)
@@ -254,6 +254,38 @@ static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
        cleanup_fold_header(state);
 }
 
+/* cleanup_parse_reject - parse REJECT liune and pick up the reason */
+
+static const char *cleanup_parse_reject(CLEANUP_STATE *state, const char *value)
+{
+    const char *reason;
+
+    /*
+     * See if they spelled REJECT right.
+     */
+    if (strcasecmp(value, "REJECT") == 0) {
+       reason = "Content rejected";
+    } else if (strncasecmp(value, "REJECT ", 7) == 0
+              || strncasecmp(value, "REJECT\t", 7) == 0) {
+       reason = value + 7;
+       while (*reason && ISSPACE(*reason))
+           reason++;
+       if (*reason == 0)
+           reason = "Content rejected";
+    } else {
+       return (0);
+    }
+
+    /*
+     * Update the remembered reason if none was stored.
+     */
+    if (state->why_rejected == 0) {
+       state->why_rejected = vstring_alloc(10);
+       vstring_strcpy(state->why_rejected, reason);
+    }
+    return (reason);
+}
+
 /* cleanup_header - process one complete header line */
 
 static void cleanup_header(CLEANUP_STATE *state)
@@ -267,12 +299,13 @@ static void cleanup_header(CLEANUP_STATE *state)
     if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_header_checks) {
        char   *header = vstring_str(state->header_buf);
        const char *value;
+       const char *reason;
 
        if ((value = maps_find(cleanup_header_checks, header, 0)) != 0) {
-           if (strcasecmp(value, "REJECT") == 0) {
-               msg_info("%s: reject: header %.200s; from=<%s> to=<%s>",
+           if ((reason = cleanup_parse_reject(state, value)) != 0) {
+               msg_info("%s: reject: header %.200s; from=<%s> to=<%s>: %s",
                         state->queue_id, header, state->sender,
-                        state->recip ? state->recip : "unknown");
+                        state->recip ? state->recip : "unknown", reason);
                state->errs |= CLEANUP_STAT_CONT;
            } else if (strcasecmp(value, "IGNORE") == 0) {
                return;
@@ -547,12 +580,13 @@ static void cleanup_message_body(CLEANUP_STATE *state, int type, char *buf, int
         */
        if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_body_checks) {
            const char *value;
+           const char *reason;
 
            if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) {
-               if (strcasecmp(value, "REJECT") == 0) {
-                   msg_info("%s: reject: body %.200s; from=<%s> to=<%s>",
+               if ((reason = cleanup_parse_reject(state, value)) != 0) {
+                   msg_info("%s: reject: body %.200s; from=<%s> to=<%s>: %s",
                             state->queue_id, buf, state->sender,
-                            state->recip ? state->recip : "unknown");
+                          state->recip ? state->recip : "unknown", reason);
                    state->errs |= CLEANUP_STAT_CONT;
                } else if (strcasecmp(value, "IGNORE") == 0) {
                    return;
index c7e5b74b6c207b267607019def4662668fb0a586..615238e48d91bcd50cb01740016af7ba478a8274 100644 (file)
@@ -84,6 +84,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
     state->xtra_offset = -1;
     state->end_seen = 0;
     state->rcpt_count = 0;
+    state->why_rejected = 0;
     return (state);
 }
 
@@ -113,5 +114,7 @@ void    cleanup_state_free(CLEANUP_STATE *state)
     if (state->queue_id)
        myfree(state->queue_id);
     been_here_free(state->dups);
+    if (state->why_rejected)
+       vstring_free(state->why_rejected);
     myfree((char *) state);
 }
index ffabf3329406e60533af09b569f1f392707b32e7..8cfb68a5eeb7653021c8aa3d2082ec7150d2e716 100644 (file)
@@ -547,8 +547,8 @@ static void flush_service(VSTREAM *client_stream, char *unused_service,
            site = vstring_alloc(10);
            queue_id = vstring_alloc(10);
            if (attr_scan(client_stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
-                    ATTR_TYPE_STR, MAIL_ATTR_SITE, site, ATTR_FLAG_MISSING,
-                         ATTR_TYPE_STR, MAIL_ATTR_SITE, queue_id,
+                         ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
+                         ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
                          ATTR_TYPE_END) == 2
                && mail_queue_id_ok(STR(queue_id)))
                status = flush_add_service(lowercase(STR(site)), STR(queue_id));
index 044c7b8e48f5aa2ad12120c650da1f8843a45dff..805d4c78710a62e5985ff81b68c7ae1083c81d2d 100644 (file)
@@ -144,7 +144,7 @@ int     vbounce_append(int flags, const char *id, const char *recipient,
     vstring_vsprintf(why, fmt, ap);
     if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
                            MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE,
-                           ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_APPEND,
+                           ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
                            ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                            ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
@@ -177,7 +177,7 @@ int     bounce_flush(int flags, const char *queue, const char *id,
     if (var_soft_bounce)
        return (-1);
     if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE,
-                           ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_FLUSH,
+                           ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH,
                            ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
index 6eaa2bd8ebdd27ec1fcee0f701b1816b0a32163a..40130cfe37dde4dccd97e64a8a08f7459eff549a 100644 (file)
@@ -146,7 +146,7 @@ int     vdefer_append(int flags, const char *id, const char *recipient,
 
     vstring_vsprintf(why, fmt, ap);
     if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
-                           ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_APPEND,
+                           ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
                            ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                            ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
@@ -180,7 +180,7 @@ int     defer_flush(int flags, const char *queue, const char *id,
                            const char *sender)
 {
     if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
-                          ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_FLUSH,
+                          ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH,
                           ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
                           ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
@@ -199,7 +199,7 @@ int     defer_warn(int flags, const char *queue, const char *id,
                           const char *sender)
 {
     if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
-                          ATTR_TYPE_NUM, MAIL_ATTR_REQ, BOUNCE_CMD_WARN,
+                          ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_WARN,
                           ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
                           ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
diff --git a/postfix/src/global/mail_attr.h b/postfix/src/global/mail_attr.h
new file mode 100644 (file)
index 0000000..8e1d706
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _MAIL_ATTR_H_INCLUDED_
+#define _MAIL_ATTR_H_INCLUDED_
+
+/*++
+/* NAME
+/*     mail_attr 3h
+/* SUMMARY
+/*     mail internal IPC support
+/* SYNOPSIS
+/*     #include <mail_attr.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Request attribute. Values are defined by individual applications.
+  */
+#define MAIL_REQUEST   "request"
+
+ /*
+  * Request completion status.
+  */
+#define MAIL_STATUS            "status"
+#define MAIL_STAT_OK           "success"
+#define MAIL_STAT_FAIL         "failed"
+#define MAIL_STAT_RETRY                "retry"
+#define MAIL_STAT_REJECT       "reject"
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/mail_command_read.c b/postfix/src/global/mail_command_read.c
new file mode 100644 (file)
index 0000000..74c7def
--- /dev/null
@@ -0,0 +1,69 @@
+/*++
+/* NAME
+/*     mail_command_read 3
+/* SUMMARY
+/*     single-command server
+/* SYNOPSIS
+/*     #include <mail_proto.h>
+/*
+/*     int     mail_command_read(stream, format, ...)
+/*     VSTREAM *stream;
+/*     char    *format;
+/* DESCRIPTION
+/*     This module implements the server interface for single-command
+/*     requests: a clients sends a single command and expects a single
+/*     completion status code.
+/*
+/*     Arguments:
+/* .IP stream
+/*     Server endpoint.
+/* .IP format
+/*     Format string understood by mail_print(3) and mail_scan(3).
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* SEE ALSO
+/*     mail_scan(3)
+/*     mail_command_write(3) client interface
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>            /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include "mail_proto.h"
+
+/* mail_command_read - read single-command request */
+
+int     mail_command_read(VSTREAM *stream, char *fmt,...)
+{
+    VSTRING *eof = vstring_alloc(10);
+    va_list ap;
+    int     count;
+
+    va_start(ap, fmt);
+    count = mail_vscan(stream, fmt, ap);
+    va_end(ap);
+    if (mail_scan(stream, "%s", eof) != 1 || strcmp(vstring_str(eof), MAIL_EOF))
+       count = -1;
+    vstring_free(eof);
+    return (count);
+}
diff --git a/postfix/src/global/mail_command_write.c b/postfix/src/global/mail_command_write.c
new file mode 100644 (file)
index 0000000..e6e8557
--- /dev/null
@@ -0,0 +1,82 @@
+/*++
+/* NAME
+/*     mail_command_write 3
+/* SUMMARY
+/*     single-command client
+/* SYNOPSIS
+/*     #include <mail_proto.h>
+/*
+/*     int     mail_command_write(class, name, format, ...)
+/*     const char *class;
+/*     const char *name;
+/*     const char *format;
+/* DESCRIPTION
+/*     This module implements a client interface for single-command
+/*     clients: a client that sends a single command and expects
+/*     a single completion status code.
+/*
+/*     Arguments:
+/* .IP class
+/*     Service type: MAIL_CLASS_PUBLIC or MAIL_CLASS_PRIVATE
+/* .IP name
+/*     Service name (master.cf).
+/* .IP format
+/*     Format string understood by mail_print(3).
+/* DIAGNOSTICS
+/*     The result is -1 if the request could not be sent, otherwise
+/*     the result is the status reported by the server.
+/*     Warnings: problems connecting to the requested service.
+/*     Fatal: out of memory.
+/* SEE ALSO
+/*     mail_command_read(3), server interface
+/*     mail_proto(5h), client-server protocol
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <vstream.h>
+
+/* Global library. */
+
+#include "mail_proto.h"
+
+/* mail_command_write - single-command transaction with completion status */
+
+int     mail_command_write(const char *class, const char *name,
+                                  const char *fmt,...)
+{
+    va_list ap;
+    VSTREAM *stream;
+    int     status;
+
+    /*
+     * Talk a little protocol with the specified service.
+     */
+    if ((stream = mail_connect(class, name, BLOCKING)) == 0)
+       return (-1);
+    va_start(ap, fmt);
+    status = mail_vprint(stream, fmt, ap);
+    va_end(ap);
+    if (status != 0
+       || mail_print(stream, "%s", MAIL_EOF) != 0
+       || vstream_fflush(stream) != 0
+       || mail_scan(stream, "%d", &status) != 1)
+       status = -1;
+    (void) vstream_fclose(stream);
+    return (status);
+}
index cd486f9bc407c8c62317ea3d70b5142d750a68f1..3e00d98387f6d3de61d0b356a2dd759e184e7258 100644 (file)
@@ -66,6 +66,7 @@
 #include <connect.h>
 #include <mymalloc.h>
 #include <iostuff.h>
+#include <stringops.h>
 
 /* Global library. */
 
@@ -79,6 +80,7 @@ VSTREAM *mail_connect(const char *class, const char *name, int block_mode)
     char   *path;
     VSTREAM *stream;
     int     fd;
+    char   *sock_name;
 
     path = mail_pathname(class, name);
     if ((fd = LOCAL_CONNECT(path, block_mode, 0)) < 0) {
@@ -90,9 +92,11 @@ VSTREAM *mail_connect(const char *class, const char *name, int block_mode)
            msg_info("connect to subsystem %s", path);
        stream = vstream_fdopen(fd, O_RDWR);
        timed_ipc_setup(stream);
+       sock_name = concatenate("socket ", path, (char *) 0);
        vstream_control(stream,
-                       VSTREAM_CTL_PATH, path,
+                       VSTREAM_CTL_PATH, sock_name,
                        VSTREAM_CTL_END);
+       myfree(sock_name);
     }
     myfree(path);
     return (stream);
index b624e7ff35535aa3d09d0ae8d6cf2873e59d0f15..02d50707b1e7c7175a76415c86fa8bb9e3450048 100644 (file)
@@ -116,7 +116,7 @@ void    mail_stream_cleanup(MAIL_STREAM * info)
 
 /* mail_stream_finish_file - finish file mail stream */
 
-static int mail_stream_finish_file(MAIL_STREAM * info)
+static int mail_stream_finish_file(MAIL_STREAM * info, VSTRING *unused_why)
 {
     int     status = 0;
     static char wakeup[] = {TRIGGER_REQ_WAKEUP};
@@ -161,7 +161,7 @@ static int mail_stream_finish_file(MAIL_STREAM * info)
 
 /* mail_stream_finish_ipc - finish IPC mail stream */
 
-static int mail_stream_finish_ipc(MAIL_STREAM * info)
+static int mail_stream_finish_ipc(MAIL_STREAM * info, VSTRING *why)
 {
     int     status = CLEANUP_STAT_WRITE;
 
@@ -169,7 +169,9 @@ static int mail_stream_finish_ipc(MAIL_STREAM * info)
      * Receive the peer's completion status.
      */
     if (attr_scan(info->stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
-                 ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status, 0) != 1)
+                 ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                 ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
+                 ATTR_TYPE_END) != 2)
        status = CLEANUP_STAT_WRITE;
 
     /*
@@ -181,9 +183,9 @@ static int mail_stream_finish_ipc(MAIL_STREAM * info)
 
 /* mail_stream_finish - finish action */
 
-int     mail_stream_finish(MAIL_STREAM * info)
+int     mail_stream_finish(MAIL_STREAM * info, VSTRING *why)
 {
-    return (info->finish(info));
+    return (info->finish(info, why));
 }
 
 /* mail_stream_file - destination is file */
index 3ae6c624ac88f5dc64e0f919e8cde400b8d4a618..8a3286661b10539b1427cded532ab4faa6781972 100644 (file)
@@ -22,7 +22,7 @@
   */
 typedef struct MAIL_STREAM MAIL_STREAM;
 
-typedef int (*MAIL_STREAM_FINISH_FN) (MAIL_STREAM *);
+typedef int (*MAIL_STREAM_FINISH_FN) (MAIL_STREAM *, VSTRING *);
 typedef int (*MAIL_STREAM_CLOSE_FN) (VSTREAM *);
 
 struct MAIL_STREAM {
@@ -38,7 +38,7 @@ extern MAIL_STREAM *mail_stream_file(const char *, const char *, const char *);
 extern MAIL_STREAM *mail_stream_service(const char *, const char *);
 extern MAIL_STREAM *mail_stream_command(const char *);
 extern void mail_stream_cleanup(MAIL_STREAM *);
-extern int mail_stream_finish(MAIL_STREAM *);
+extern int mail_stream_finish(MAIL_STREAM *, VSTRING *);
 
 
 /* LICENSE
index 0a2b83bd487b21a859b04744639c41b13e53ffa7..6508da3b3d03955bfcec33aab2a6b086ec848637 100644 (file)
@@ -15,7 +15,7 @@
   * Version of this program.
   */
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "Snapshot-20011014"
+#define DEF_MAIL_VERSION       "Snapshot-20011015"
 extern char *var_mail_version;
 
 /* LICENSE
index 8a8e974ea73ff4dc52031bad0ac3d7ceb9af9e28..07475df6b58bb0e0746bbd485028599462e16cee 100644 (file)
@@ -289,7 +289,7 @@ int     main(int argc, char **argv)
     /*
      * Finish the file.
      */
-    if ((status = mail_stream_finish(dst)) != 0)
+    if ((status = mail_stream_finish(dst, (VSTRING *) 0)) != 0)
        msg_fatal("uid=%ld: %s", (long) uid, cleanup_strerror(status));
 
     /*
index 85a12284787a87041515e86fa99c54de2121cd08..f1d599578b5aef7dc1f2149936f095c2ae77392d 100644 (file)
@@ -394,7 +394,7 @@ static void qmqpd_close_file(QMQPD_STATE *state)
      * Finish the queue file or finish the cleanup conversation.
      */
     if (state->err == 0)
-       state->err = mail_stream_finish(state->dest);
+       state->err = mail_stream_finish(state->dest, state->why_rejected);
     else
        mail_stream_cleanup(state->dest);
     state->dest = 0;
@@ -453,7 +453,7 @@ static int qmqpd_send_status(QMQPD_STATE *state)
                    "Error: too many hops");
     } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
        qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
-                   "Error: content rejected");
+                   "Error: %s", STR(state->why_rejected));
     } else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
        qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
                    "Error: queue file write error");
index 9bccc74964895b04238e09f82f03d52d9c598675..aaacb424254b26bbdcf2b3e5c8a0b34f176220af 100644 (file)
@@ -45,6 +45,7 @@ typedef struct {
     char   *recipient;                 /* recipient address */
     char   *protocol;                  /* protocol name */
     char   *where;                     /* protocol state */
+    VSTRING *why_rejected;             /* REJECT reason */
 } QMQPD_STATE;
 
  /*
index f4eaf27f6baad88ac2a15e5ed0d6be70f0d8125d..8e5b46c9c4e850e3a4afc6a4f142be75f408c7b4 100644 (file)
@@ -74,6 +74,7 @@ QMQPD_STATE *qmqpd_state_alloc(VSTREAM *stream)
     state->recipient = 0;
     state->protocol = "QMQP";
     state->where = "initializing client connection";
+    state->why_rejected = vstring_alloc(10);
     return (state);
 }
 
@@ -92,5 +93,6 @@ void qmqpd_state_free(QMQPD_STATE *state)
        myfree(state->sender);
     if (state->recipient)
        myfree(state->recipient);
+    vstring_free(state->why_rejected);
     myfree((char *) state);
 }
index 1c7984f4054e9040deb145d8b941458ab5d9f7b9..694e99a558f139fe982383260d9cb9b27769b00c 100644 (file)
@@ -516,7 +516,7 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
     if (vstream_ferror(VSTREAM_IN))
        msg_fatal("%s(%ld): error reading input: %m",
                  saved_sender, (long) uid);
-    if ((status = mail_stream_finish(handle)) != 0)
+    if ((status = mail_stream_finish(handle, buf)) != 0)
        msg_fatal("%s(%ld): %s", saved_sender,
                  (long) uid, cleanup_strerror(status));
     if (sendmail_path) {
index 67f61bead533f07944c45d43d298e8f01853bdb9..0ecd36fcddb663e8c871f55f4420ef46b1eae567 100644 (file)
@@ -891,6 +891,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
     int     curr_rec_type;
     int     prev_rec_type;
     int     first = 1;
+    VSTRING *why = 0;
 
     /*
      * Sanity checks. With ESMTP command pipelining the client can send DATA
@@ -999,7 +1000,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      * Finish the queue file or finish the cleanup conversation.
      */
     if (state->err == 0)
-       state->err |= mail_stream_finish(state->dest);
+       state->err |= mail_stream_finish(state->dest, why = vstring_alloc(10));
     else
        mail_stream_cleanup(state->dest);
     state->dest = 0;
@@ -1040,7 +1041,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        smtpd_chat_reply(state, "554 Error: too many hops");
     } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
        state->error_mask |= MAIL_ERROR_POLICY;
-       smtpd_chat_reply(state, "552 Error: content rejected");
+       smtpd_chat_reply(state, "552 Error: %s", STR(why));
     } else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
        state->error_mask |= MAIL_ERROR_RESOURCE;
        smtpd_chat_reply(state, "451 Error: queue file write error");
@@ -1062,6 +1063,8 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      */
     mail_reset(state);
     rcpt_reset(state);
+    if (why)
+       vstring_free(why);
     return (state->err);
 }
 
index f3fab48e7b76d59fa162388fa970225675cf67c8..0d51b9b21428599c68becfdd9fd865fdb13e68d6 100644 (file)
@@ -24,7 +24,7 @@ SRCS  = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \
        sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
        hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
        sane_socketpair.c myrand.c netstring.c ctable.c attr_print.c intv.c \
-       attr_scan.c attr_table.c
+       attr_scan.c attr_table.c base64_code.c
 OBJS   = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
        close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
        dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
@@ -50,7 +50,7 @@ OBJS  = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
        sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
        hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
        sane_socketpair.o myrand.o netstring.o ctable.o attr_print.o intv.o \
-       attr_scan.o attr_table.o
+       attr_scan.o attr_table.o base64_code.o
 HDRS   = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
        dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
        dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
@@ -67,7 +67,7 @@ HDRS  = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
        dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \
        watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \
        sane_time.h sane_socketpair.h myrand.h netstring.h ctable.h \
-       intv.h
+       intv.h base64_code.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -84,7 +84,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        mystrtok sigdelay translit valid_hostname vstream_popen \
        vstring vstring_vstream doze select_bug stream_test mac_expand \
        watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \
-       inet_addr_list attr_print attr_scan attr_table
+       inet_addr_list attr_print attr_scan attr_table base64_code
 
 LIB_DIR        = ../../lib
 INC_DIR        = ../../include
@@ -303,6 +303,11 @@ attr_table: $(LIB) $@.o
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
+base64_code: $(LIB) $@.o
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
        set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -315,7 +320,7 @@ stream_test: stream_test.c $(LIB)
        $(CC) $(CFLAGS)  -o $@ $@.c $(LIB) $(SYSLIBS)
 
 tests: valid_hostname_test mac_expand_test dict_test unescape_test \
-       hex_quote_test ctable_test inet_addr_list_test
+       hex_quote_test ctable_test inet_addr_list_test base64_code_test
 
 valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref
        ./valid_hostname <valid_hostname.in 2>valid_hostname.tmp
@@ -348,6 +353,9 @@ inet_addr_list_test: inet_addr_list
        diff inet_addr_list.ref inet_addr_list.tmp
        rm -f inet_addr_list.tmp
 
+base64_code_test: base64_code
+       ./base64_code
+
 DB_TYPE        = `../postconf/postconf -h default_database_type`
 
 dict_test: dict_open testdb dict_test.in dict_test.ref
@@ -387,6 +395,8 @@ attr_print.o: mymalloc.h
 attr_print.o: vstream.h
 attr_print.o: vbuf.h
 attr_print.o: htable.h
+attr_print.o: base64_code.h
+attr_print.o: vstring.h
 attr_print.o: attr.h
 attr_scan.o: attr_scan.c
 attr_scan.o: sys_defs.h
@@ -397,6 +407,7 @@ attr_scan.o: vbuf.h
 attr_scan.o: vstring.h
 attr_scan.o: argv.h
 attr_scan.o: intv.h
+attr_scan.o: base64_code.h
 attr_scan.o: attr.h
 attr_scan.o: htable.h
 attr_table.o: attr_table.c
@@ -411,6 +422,13 @@ attr_table.o: vstring_vstream.h
 attr_table.o: argv.h
 attr_table.o: intv.h
 attr_table.o: attr.h
+base64_code.o: base64_code.c
+base64_code.o: sys_defs.h
+base64_code.o: msg.h
+base64_code.o: mymalloc.h
+base64_code.o: vstring.h
+base64_code.o: vbuf.h
+base64_code.o: base64_code.h
 basename.o: basename.c
 basename.o: sys_defs.h
 basename.o: stringops.h
index 15fecbcacc42a4e4f87409a0d79b7065cfa16353..4680747e0c0bb4cf5b67b651b81f2781af344765 100644 (file)
 #include <mymalloc.h>
 #include <vstream.h>
 #include <htable.h>
+#include <base64_code.h>
 #include <attr.h>
 
-/* attr_fprintf - encode attribute information on the fly */
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
 
-static void PRINTFLIKE(2, 3) attr_fprintf(VSTREAM *fp, const char *format,...)
+/* attr_print_str - encode and send attribute information */
+
+static void attr_print_str(VSTREAM *fp, const char *str, int len)
 {
-    va_list ap;
+    static VSTRING *base64_buf;
 
-    va_start(ap, format);
-    vstream_vfprintf(fp, format, ap);
-    va_end(ap);
+    if (base64_buf == 0)
+       base64_buf = vstring_alloc(10);
+
+    base64_encode(base64_buf, str, len);
+    vstream_fputs(STR(base64_buf), fp);
+}
+
+static void attr_print_num(VSTREAM *fp, unsigned num)
+{
+    static VSTRING *plain;
+
+    if (plain == 0)
+       plain = vstring_alloc(10);
+
+    vstring_sprintf(plain, "%u", num);
+    attr_print_str(fp, STR(plain), LEN(plain));
 }
 
 /* attr_vprint - send attribute list to stream */
@@ -136,46 +153,52 @@ int     attr_vprint(VSTREAM *fp, int flags, va_list ap)
        switch (attr_type) {
        case ATTR_TYPE_NUM:
            attr_name = va_arg(ap, char *);
-           attr_fprintf(fp, "%s", attr_name);
+           attr_print_str(fp, attr_name, strlen(attr_name));
            int_val = va_arg(ap, int);
-           attr_fprintf(fp, ":%u", (unsigned) int_val);
+           VSTREAM_PUTC(':', fp);
+           attr_print_num(fp, (unsigned) int_val);
            if (msg_verbose)
-               msg_info("send attr name %s value %u", attr_name, int_val);
+               msg_info("send attr %s = %u", attr_name, int_val);
            break;
        case ATTR_TYPE_STR:
            attr_name = va_arg(ap, char *);
-           attr_fprintf(fp, "%s", attr_name);
+           attr_print_str(fp, attr_name, strlen(attr_name));
            str_val = va_arg(ap, char *);
-           attr_fprintf(fp, ":%s", str_val);
+           VSTREAM_PUTC(':', fp);
+           attr_print_str(fp, str_val, strlen(str_val));
            if (msg_verbose)
-               msg_info("send attr name %s value %s", attr_name, str_val);
+               msg_info("send attr %s = %s", attr_name, str_val);
            break;
        case ATTR_TYPE_NUM_ARRAY:
            attr_name = va_arg(ap, char *);
-           attr_fprintf(fp, "%s", attr_name);
+           attr_print_str(fp, attr_name, strlen(attr_name));
            ip_val = va_arg(ap, int *);
            count_val = va_arg(ap, int);
-           for (i = 0; i < count_val; i++)
-               attr_fprintf(fp, ":%u", (unsigned) *ip_val++);
+           for (i = 0; i < count_val; i++) {
+               VSTREAM_PUTC(':', fp);
+               attr_print_num(fp, (unsigned) *ip_val++);}
            if (msg_verbose)
-               msg_info("send attr name %s values %d", attr_name, count_val);
+               msg_info("send attr %s values %d", attr_name, count_val);
            break;
        case ATTR_TYPE_STR_ARRAY:
            attr_name = va_arg(ap, char *);
-           attr_fprintf(fp, "%s", attr_name);
+           attr_print_str(fp, attr_name, strlen(attr_name));
            cpp_val = va_arg(ap, char **);
            count_val = va_arg(ap, int);
            for (i = 0; i < count_val; i++) {
                str_val = *cpp_val++;
-               attr_fprintf(fp, ":%s", str_val);
+               VSTREAM_PUTC(':', fp);
+               attr_print_str(fp, str_val, strlen(str_val));
            }
            if (msg_verbose)
-               msg_info("send attr name %s values %d", attr_name, count_val);
+               msg_info("send attr %s values %d", attr_name, count_val);
            break;
        case ATTR_TYPE_HASH:
            ht_info_list = htable_list(va_arg(ap, HTABLE *));
            for (ht = ht_info_list; *ht; ht++) {
-               attr_fprintf(fp, "%s:%s", ht[0]->key, ht[0]->value);
+               attr_print_str(fp, ht[0]->key, strlen(ht[0]->key));
+               VSTREAM_PUTC(':', fp);
+               attr_print_str(fp, ht[0]->value, strlen(ht[0]->value));
                if (msg_verbose)
                    msg_info("send attr name %s value %s",
                             ht[0]->key, ht[0]->value);
index 392fee766d6b3fff62294a3f1177fd7640b37e5b..3039f1a94265be14f64c1bcb7fae6bedd0e73315 100644 (file)
@@ -83,7 +83,7 @@
 /*     For convenience, this value requests none of the above.
 /* .RE
 /* .IP type
-/*     The type determines the arguments that follow.
+/*     The type argument determines the arguments that follow.
 /* .RS
 /* .IP "ATTR_TYPE_NUM (char *, int *)"
 /*     This argument is followed by an attribute name and an integer pointer.
 /*     This is used for recovering a string array attribute value.
 /*     Values from the input stream are appended to the array.
 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
-/*     All further attributes are stored into the given hash table as simple
-/*     string-valued attributes, under keys equal to the attribute name.
+/*     All further input attributes are required to be simple string or 
+/*     integer attributes. 
+/*     Their string values are stored in the specified hash table under 
+/*     keys equal to the attribute name (obtained from the input stream).
 /*     Values from the input stream are added to the hash table, but existing
 /*     hash table entries are not replaced.
 /* .sp
-/*     N.B. This must be followed by an ATTR_TYPE_END argument.
+/*     N.B. This construct must be followed by an ATTR_TYPE_END argument.
 /* .IP ATTR_TYPE_END
-/*     This terminates the requested attribute list.
+/*     This argument terminates the requested attribute list.
 /* .RE
+/* BUGS
+/*     ATTR_TYPE_HASH accepts attributes with arbitrary names from an 
+/*     untrusted source. This is safe only if the resulting table is
+/*     queried for specific names.
 /* DIAGNOSTICS
 /*     The result value is the number of attributes that were successfully
 /*     recovered from the input stream (an array-valued attribute counts
 #include <vstring.h>
 #include <argv.h>
 #include <intv.h>
+#include <base64_code.h>
 #include <attr.h>
 
 /* Application specific. */
 static int attr_scan_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
 {
     static VSTRING *base64_buf = 0;
+
+#if 0
     extern int var_line_limit;         /* XXX */
     int     limit = var_line_limit * 5 / 4;
+
+#endif
     int     ch;
 
     if (base64_buf == 0)
@@ -170,14 +181,16 @@ static int attr_scan_string(VSTREAM *fp, VSTRING *plain_buf, const char *context
            return (-1);
        }
        VSTRING_ADDCH(base64_buf, ch);
+#if 0
        if (LEN(base64_buf) > limit) {
            msg_warn("string length > %d characters from %s while reading %s",
                     limit, VSTREAM_PATH(fp), context);
            return (-1);
        }
+#endif
     }
     VSTRING_TERMINATE(base64_buf);
-    if (BASE64_DECODE(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
+    if (base64_decode(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
        msg_warn("malformed base64 data from %s: %.100s",
                 VSTREAM_PATH(fp), STR(base64_buf));
        return (-1);
@@ -335,7 +348,7 @@ int     attr_vscan(VSTREAM *fp, int flags, va_list ap)
                                       "attribute value")) < 0)
                return (conversions);
            if (ch != '\n') {
-               msg_warn("too many values for number attribute %s from %s",
+               msg_warn("multiple values for attribute %s from %s",
                         STR(name_buf), VSTREAM_PATH(fp));
                return (conversions);
            }
@@ -350,7 +363,7 @@ int     attr_vscan(VSTREAM *fp, int flags, va_list ap)
            if ((ch = attr_scan_string(fp, string, "attribute value")) < 0)
                return (conversions);
            if (ch != '\n') {
-               msg_warn("too many values for string attribute %s from %s",
+               msg_warn("multiple values for attribute %s from %s",
                         STR(name_buf), VSTREAM_PATH(fp));
                return (conversions);
            }
@@ -364,7 +377,7 @@ int     attr_vscan(VSTREAM *fp, int flags, va_list ap)
            if ((ch = attr_scan_string(fp, str_buf, "attribute value")) < 0)
                return (conversions);
            if (ch != '\n') {
-               msg_warn("too many values for string attribute %s from %s",
+               msg_warn("multiple values for attribute %s from %s",
                         STR(name_buf), VSTREAM_PATH(fp));
                return (conversions);
            }
diff --git a/postfix/src/util/base64_code.c b/postfix/src/util/base64_code.c
new file mode 100644 (file)
index 0000000..443bf43
--- /dev/null
@@ -0,0 +1,200 @@
+/*++
+/* NAME
+/*     base64_code 3
+/* SUMMARY
+/*     encode/decode data, base 64 style
+/* SYNOPSIS
+/*     #include <base64_code.h>
+/*
+/*     VSTRING *base64_encode(result, in, len)
+/*     VSTRING *result;
+/*     const char *in;
+/*     int     len;
+/*
+/*     VSTRING *base64_decode(result, in, len)
+/*     VSTRING *result;
+/*     const char *in;
+/*     int     len;
+/* DESCRIPTION
+/*     base64_encode() takes a block of len bytes and encodes it as one
+/*     null-terminated string.  The result value is the result argument.
+/*
+/*     base64_decode() performs the opposite transformation. The result
+/*     value is the result argument. The result is null terminated, whether 
+/*     or not that makes sense.
+/* DIAGNOSTICS
+/*     base64_decode () returns a null pointer when the input contains
+/*     characters not in the base 64 alphabet.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <base64_code.h>
+
+/* Application-specific. */
+
+static unsigned char to_b64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define UNSIG_CHAR_PTR(x) ((unsigned char *)(x))
+
+/* base64_encode - raw data to encoded */
+
+VSTRING *base64_encode(VSTRING *result, const char *in, int len)
+{
+    const unsigned char *cp;
+    int     count;
+
+    /*
+     * Encode 3 -> 4.
+     */
+    VSTRING_RESET(result);
+    for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 3, cp += 3) {
+       VSTRING_ADDCH(result, to_b64[cp[0] >> 2]);
+       if (count > 1) {
+           VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4 | cp[1] >> 4]);
+           if (count > 2) {
+               VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2 | cp[2] >> 6]);
+               VSTRING_ADDCH(result, to_b64[cp[2] & 0x3f]);
+           } else {
+               VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2]);
+               VSTRING_ADDCH(result, '=');
+               break;
+           }
+       } else {
+           VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4]);
+           VSTRING_ADDCH(result, '=');
+           VSTRING_ADDCH(result, '=');
+           break;
+       }
+    }
+    VSTRING_TERMINATE(result);
+    return (result);
+}
+
+/* base64_decode - encoded data to raw */
+
+VSTRING *base64_decode(VSTRING *result, const char *in, int len)
+{
+    static char *un_b64 = 0;
+    const unsigned char *cp;
+    int     count;
+    int     ch0;
+    int     ch1;
+    int     ch2;
+    int     ch3;
+
+#define CHARS_PER_BYTE 256
+#define INVALID                0xff
+
+    /*
+     * Sanity check.
+     */
+    if (len % 4)
+       return (0);
+
+    /*
+     * Once: initialize the decoding lookup table on the fly.
+     */
+    if (un_b64 == 0) {
+       un_b64 = mymalloc(CHARS_PER_BYTE);
+       memset(un_b64, INVALID, CHARS_PER_BYTE);
+       for (cp = to_b64; cp < to_b64 + sizeof(to_b64); cp++)
+           un_b64[*cp] = cp - to_b64;
+    }
+
+    /*
+     * Decode 4 -> 3.
+     */
+    VSTRING_RESET(result);
+    for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) {
+       if ((ch0 = un_b64[*cp++]) == INVALID
+           || (ch1 = un_b64[*cp++]) == INVALID)
+           return (0);
+       VSTRING_ADDCH(result, ch0 << 2 | ch1 >> 4);
+       if ((ch2 = *cp++) == '=')
+           break;
+       if ((ch2 = un_b64[ch2]) == INVALID)
+           return (0);
+       VSTRING_ADDCH(result, ch1 << 4 | ch2 >> 2);
+       if ((ch3 = *cp++) == '=')
+           break;
+       if ((ch3 = un_b64[ch3]) == INVALID)
+           return (0);
+       VSTRING_ADDCH(result, ch2 << 6 | ch3);
+    }
+    VSTRING_TERMINATE(result);
+    return (result);
+}
+
+#ifdef TEST
+
+ /*
+  * Proof-of-concept test program: convert to base 64 and back.
+  */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+int     main(int unused_argc, char **unused_argv)
+{
+    VSTRING *b1 = vstring_alloc(1);
+    VSTRING *b2 = vstring_alloc(1);
+    char   *test = "this is a test";
+
+#define DECODE(b,s,l) { \
+       if (base64_decode((b),(s),(l)) == 0) \
+           msg_panic("bad base64: %s", (s)); \
+    }
+#define VERIFY(b,t) { \
+       if (strcmp((b), (t)) != 0) \
+           msg_panic("bad test: %s", (b)); \
+    }
+
+    base64_encode(b1, test, strlen(test));
+    DECODE(b2, STR(b1), LEN(b1));
+    VERIFY(STR(b2), test);
+
+    base64_encode(b1, test, strlen(test));
+    base64_encode(b2, STR(b1), LEN(b1));
+    base64_encode(b1, STR(b2), LEN(b2));
+    DECODE(b2, STR(b1), LEN(b1));
+    DECODE(b1, STR(b2), LEN(b2));
+    DECODE(b2, STR(b1), LEN(b1));
+    VERIFY(STR(b2), test);
+
+    base64_encode(b1, test, strlen(test));
+    base64_encode(b2, STR(b1), LEN(b1));
+    base64_encode(b1, STR(b2), LEN(b2));
+    base64_encode(b2, STR(b1), LEN(b1));
+    base64_encode(b1, STR(b2), LEN(b2));
+    DECODE(b2, STR(b1), LEN(b1));
+    DECODE(b1, STR(b2), LEN(b2));
+    DECODE(b2, STR(b1), LEN(b1));
+    DECODE(b1, STR(b2), LEN(b2));
+    DECODE(b2, STR(b1), LEN(b1));
+    VERIFY(STR(b2), test);
+
+    vstring_free(b1);
+    vstring_free(b2);
+    return (0);
+}
+
+#endif
diff --git a/postfix/src/util/base64_code.h b/postfix/src/util/base64_code.h
new file mode 100644 (file)
index 0000000..d018947
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _BASE64_CODE_H_INCLUDED_
+#define _BASE64_CODE_H_INCLUDED_
+
+/*++
+/* NAME
+/*     base64_code 3h
+/* SUMMARY
+/*     encode/decode data, base 64 style
+/* SYNOPSIS
+/*     #include <base64_code.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+  * External interface.
+  */
+extern VSTRING *base64_encode(VSTRING *, const char *, int);
+extern VSTRING *base64_decode(VSTRING *, const char *, int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif