-TMIME_INFO
-TMIME_STACK
-TMIME_STATE
+-TMIME_STATE_DETAIL
-TMIME_TOKEN
-TMKMAP
-TMKMAP_DB
dsn_vstring_update() module. File: global/dsn_util.h,
global/pipe_command.c.
+20050401
+
+ Cleanup: ignore incorrect enhanced status codes (such as
+ 5xx reply followed by a 4.x.x status), and don't look for
+ enhanced status codes unless the server replies with a
+ [245]XX reply. Files: smtp/smtp_chat.c, lmtp/lmtp_chat.c.
+
+20050402
+
+ Feature: enhanced status code support for errors found by
+ the MIME processor. Files: global/mime_state.c,
+ cleanup/cleanup_message.c, smtp/smtp_proto.c.
+
+ Cleanup: updated error messages about MIME processing
+ errors in the SMTP client. These errors are no longer
+ specific to 8bit->7bit conversion; they can also happen
+ with generic address mapping. File: smtp/smtp_proto.c.
+
Open problems:
Med: disable header address rewriting after XCLIENT?
const char *buf, int len)
{
char *myname = "cleanup_message_headerbody";
+ MIME_STATE_DETAIL *detail;
/*
* Copy text record to the output.
state->mime_errs &= ~MIME_ERR_TRUNC_HEADER;
if (state->mime_errs && state->reason == 0) {
state->errs |= CLEANUP_STAT_CONT;
- state->reason = mystrdup(mime_state_error(state->mime_errs));
+ detail = mime_state_detail(state->mime_errs);
+ state->reason = dsn_prepend(detail->dsn, detail->text);
}
state->mime_state = mime_state_free(state->mime_state);
if ((state->xtra_offset = vstream_ftell(state->dst)) < 0)
* Patches change the patchlevel and the release date. Snapshots change the
* release date only.
*/
-#define MAIL_RELEASE_DATE "20050401"
+#define MAIL_RELEASE_DATE "20050402"
#define MAIL_VERSION_NUMBER "2.3"
#define VAR_MAIL_VERSION "mail_version"
/*
/* const char *mime_state_error(error_code)
/* int error_code;
+/*
+/* typedef struct {
+/* .in +4
+/* const int code; /* internal error code */
+/* const char *dsn; /* RFC 3463 */
+/* const char *text; /* descriptive text */
+/* .in -4
+/* } MIME_STATE_DETAIL;
+/*
+/* MIME_STATE_DETAIL *mime_state_detail(error_code)
+/* int error_code;
/* DESCRIPTION
/* This module implements a one-pass MIME processor with optional
/* 8-bit to quoted-printable conversion.
/* specified error code. When multiple errors are specified it
/* reports what it deems the most serious one.
/*
+/* mime_state_detail() returns a table entry with error
+/* information for the specified error code. When multiple
+/* errors are specified it reports what it deems the most
+/* serious one.
+/*
/* Arguments:
/* .IP body_out
/* The output routine for body lines. It receives unmodified input
}
}
+ /*
+ * Mime error to (DSN, text) mapping. Order matters; more serious errors
+ * must precede less serious errors, because the error-to-text conversion
+ * can report only one error.
+ */
+static MIME_STATE_DETAIL mime_err_detail[] = {
+ MIME_ERR_NESTING, "5.6.0", "MIME nesting exceeds safety limit",
+ MIME_ERR_TRUNC_HEADER, "5.6.0", "message header length exceeds safety limit",
+ MIME_ERR_8BIT_IN_HEADER, "5.6.0", "improper use of 8-bit data in message header",
+ MIME_ERR_8BIT_IN_7BIT_BODY, "5.6.0", "improper use of 8-bit data in message body",
+ MIME_ERR_ENCODING_DOMAIN, "5.6.0", "invalid message/* or multipart/* encoding domain",
+ 0,
+};
+
/* mime_state_error - error code to string */
const char *mime_state_error(int error_code)
{
+ MIME_STATE_DETAIL *mp;
+
if (error_code == 0)
msg_panic("mime_state_error: there is no error");
- if (error_code & MIME_ERR_NESTING)
- return ("MIME nesting exceeds safety limit");
- if (error_code & MIME_ERR_TRUNC_HEADER)
- return ("message header length exceeds safety limit");
- if (error_code & MIME_ERR_8BIT_IN_HEADER)
- return ("improper use of 8-bit data in message header");
- if (error_code & MIME_ERR_8BIT_IN_7BIT_BODY)
- return ("improper use of 8-bit data in message body");
- if (error_code & MIME_ERR_ENCODING_DOMAIN)
- return ("invalid message/* or multipart/* encoding domain");
+ for (mp = mime_err_detail; mp->code; mp++)
+ if (mp->code & error_code)
+ return (mp->text);
msg_panic("mime_state_error: unknown error code %d", error_code);
}
+/* mime_state_detail - error code to table entry with assorted data */
+
+MIME_STATE_DETAIL *mime_state_detail(int error_code)
+{
+ MIME_STATE_DETAIL *mp;
+
+ if (error_code == 0)
+ msg_panic("mime_state_detail: there is no error");
+ for (mp = mime_err_detail; mp->code; mp++)
+ if (mp->code & error_code)
+ return (mp);
+ msg_panic("mime_state_detail: unknown error code %d", error_code);
+}
+
#ifdef TEST
+#include <stdlib.h>
#include <stringops.h>
#include <vstream.h>
#include <msg_vstream.h>
extern MIME_STATE *mime_state_alloc(int, MIME_STATE_HEAD_OUT, MIME_STATE_ANY_END, MIME_STATE_BODY_OUT, MIME_STATE_ANY_END, MIME_STATE_ERR_PRINT, void *);
extern int mime_state_update(MIME_STATE *, int, const char *, int);
extern MIME_STATE *mime_state_free(MIME_STATE *);
-extern const char *mime_state_error(int);
/*
* Processing options.
/*
* Processing errors, not necessarily lethal.
*/
+typedef struct {
+ const int code; /* internal error code */
+ const char *dsn; /* RFC 3463 */
+ const char *text; /* descriptive text */
+} MIME_STATE_DETAIL;
+
#define MIME_ERR_NESTING (1<<0)
#define MIME_ERR_TRUNC_HEADER (1<<1)
#define MIME_ERR_8BIT_IN_HEADER (1<<2)
#define MIME_ERR_8BIT_IN_7BIT_BODY (1<<3)
#define MIME_ERR_ENCODING_DOMAIN (1<<4)
+extern MIME_STATE_DETAIL *mime_state_detail(int);
+extern const char *mime_state_error(int);
+
/*
* Header classes. Look at the header_opts argument to find out if something
* is a MIME header in a primary or nested section.
/*
* Extract RFC 821 reply code and RFC 2034 detail code. Use a default
* detail code if none was given.
+ *
+ * Ignore out-of-protocol enhanced status codes: codes that accompany 3XX
+ * replies, or codes whose initial digit is out of sync with the reply
+ * code.
*/
DSN_CLASS(rdata.dsn) = 0;
if (three_digs != 0) {
rdata.code = atoi(STR(state->buffer));
- for (cp = STR(state->buffer) + 4; *cp == ' '; cp++)
- /* void */ ;
- if ((len = dsn_valid(cp)) > 0) {
- DSN_UPDATE(rdata.dsn, cp, len);
- } else if (strchr("245", STR(state->buffer)[0]) != 0) {
- DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
- DSN_CLASS(rdata.dsn) = STR(state->buffer)[0];
+ if (strchr("245", STR(state->buffer)[0]) != 0) {
+ for (cp = STR(state->buffer) + 4; *cp == ' '; cp++)
+ /* void */ ;
+ if ((len = dsn_valid(cp)) > 0 && *cp == *STR(state->buffer)) {
+ DSN_UPDATE(rdata.dsn, cp, len);
+ } else {
+ DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
+ DSN_CLASS(rdata.dsn) = STR(state->buffer)[0];
+ }
}
} else {
rdata.code = 0;
/*
* Extract RFC 821 reply code and RFC 2034 detail. Use a default detail
* code if none was given.
+ *
+ * Ignore out-of-protocol enhanced status codes: codes that accompany 3XX
+ * replies, or codes whose initial digit is out of sync with the reply
+ * code.
*/
DSN_CLASS(rdata.dsn) = 0;
if (three_digs != 0) {
rdata.code = atoi(STR(session->buffer));
- for (cp = STR(session->buffer) + 4; *cp == ' '; cp++)
- /* void */ ;
- if ((len = dsn_valid(cp)) > 0) {
- DSN_UPDATE(rdata.dsn, cp, len);
- } else if (strchr("245", STR(session->buffer)[0]) != 0) {
- DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
- DSN_CLASS(rdata.dsn) = STR(session->buffer)[0];
+ if (strchr("245", STR(session->buffer)[0]) != 0) {
+ for (cp = STR(session->buffer) + 4; *cp == ' '; cp++)
+ /* void */ ;
+ if ((len = dsn_valid(cp)) > 0 && *cp == *STR(session->buffer)) {
+ DSN_UPDATE(rdata.dsn, cp, len);
+ } else {
+ DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
+ DSN_CLASS(rdata.dsn) = STR(session->buffer)[0];
+ }
}
} else {
rdata.code = 0;
NOCLOBBER int mail_from_rejected;
NOCLOBBER int downgrading;
int mime_errs;
+ MIME_STATE_DETAIL *detail;
/*
* Macros for readability.
vstring_str(session->scratch),
VSTRING_LEN(session->scratch));
if (mime_errs) {
- smtp_mesg_fail(state, "5.6.5", 554,
- "MIME 7-bit conversion failed: %s",
- mime_state_error(mime_errs));
+ detail = mime_state_detail(mime_errs);
+ smtp_mesg_fail(state, detail->dsn, 554, "%s",
+ detail->text);
RETURN(0);
}
}
* ending in newline via /usr/sbin/sendmail while MIME input
* processing is turned off, and MIME 8bit->7bit conversion
* is requested upon delivery.
+ *
+ * Or some error while doing generic address mapping.
*/
mime_errs =
mime_state_update(session->mime_state, rec_type, "", 0);
if (mime_errs) {
- smtp_mesg_fail(state, "5.6.5", 554,
- "MIME 7-bit conversion failed: %s",
- mime_state_error(mime_errs));
+ detail = mime_state_detail(mime_errs);
+ smtp_mesg_fail(state, detail->dsn, 554, "%s", detail->text);
RETURN(0);
}
} else if (prev_type == REC_TYPE_CONT) /* missing newline */
stream_test: stream_test.c $(LIB)
$(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS)
+gcctest: gccw.c gccw.ref
+ rm -f gccw.o
+ make gccw.o 2>&1 | sed "s/\`/'/g; s/return-/return /" | sort >gccw.tmp
+ diff gccw.ref gccw.tmp
+ rm -f gccw.o gccw.tmp
+
tests: valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
--- /dev/null
+ /*
+ * This is is a regression test for all the things that gcc is meant to warn
+ * about.
+ *
+ * gcc version 3 breaks several tests:
+ *
+ * -W does not report missing return value
+ *
+ * -Wunused does not report unused parameter
+ */
+
+#include <stdio.h>
+#include <setjmp.h>
+
+jmp_buf jbuf;
+
+ /* -Wmissing-prototypes: no previous prototype for 'test1' */
+ /* -Wimplicit: return type defaults to `int' */
+test1(void)
+{
+ /* -Wunused: unused variable `foo' */
+ int foo;
+
+ /* -Wparentheses: suggest parentheses around && within || */
+ printf("%d\n", 1 && 2 || 3 && 4);
+ /* -W: statement with no effect */
+ 0;
+ /* BROKEN in gcc 3 */
+ /* -W: control reaches end of non-void function */
+}
+
+
+ /* -W??????: unused parameter `foo' */
+void test2(int foo)
+{
+ enum {
+ a = 10, b = 15} moe;
+ int bar;
+
+ /* -Wuninitialized: 'bar' might be used uninitialized in this function */
+ /* -Wformat: format argument is not a pointer (arg 2) */
+ printf("%s\n", bar);
+ /* -Wformat: too few arguments for format */
+ printf("%s%s\n", "bar");
+ /* -Wformat: too many arguments for format */
+ printf("%s\n", "bar", "bar");
+
+ /* -Wswitch: enumeration value `b' not handled in switch */
+ switch (moe) {
+ case a:
+ return;
+ }
+}
+
+ /* -Wstrict-prototypes: function declaration isn't a prototype */
+void test3()
+{
+}
--- /dev/null
+gcc -Wall -Wno-comment -Wformat -Wimplicit -Wmissing-prototypes -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized -Wunused -DUSE_TLS -DHAS_PCRE -I/usr/local/include -DSNAPSHOT -g -O -I. -DFREEBSD5 -c gccw.c
+gccw.c: At top level:
+gccw.c: At top level:
+gccw.c: In function 'test1':
+gccw.c: In function 'test2':
+gccw.c:20: warning: no previous prototype for 'test1'
+gccw.c:20: warning: return type defaults to 'int'
+gccw.c:22: warning: unused variable 'foo'
+gccw.c:25: warning: suggest parentheses around && within ||
+gccw.c:27: warning: statement with no effect
+gccw.c:30: warning: control reaches end of non-void function
+gccw.c:35: warning: no previous prototype for 'test2'
+gccw.c:38: warning: 'bar' might be used uninitialized in this function
+gccw.c:42: warning: format argument is not a pointer (arg 2)
+gccw.c:44: warning: too few arguments for format
+gccw.c:46: warning: too many arguments for format
+gccw.c:52: warning: enumeration value 'b' not handled in switch
+gccw.c:57: warning: function declaration isn't a prototype