can get without running an interactive debugger program, as described
in the next section.
+See the next section on how to automatically attach a program to
+a Postfix daemon.
+
5 - Running daemon programs under an interactive debugger
=========================================================
Whenever the suspect daemon process is started, a debugger window
pops up and you can watch in detail what happens.
+
+6 - Unreasonable behavior
+=========================
+
+Sometimes the behavior exhibit by Postfix just does not match the
+source code. Why can a program deviate from the instructions given
+by its author? There are two possibilities.
+
+1 - The compiler has messed up.
+
+2 - The hardware has messed up.
+
+In both cases, the program being executed is not the program that
+was supposed to be executed, so anything can happen.
+
+Hardware-related failures happen erratically, and they usually do
+not reproduce after power cycling and rebooting the system. There's
+little I can do about bad hardware. Be sure to use hardware that
+at the very least can detect memory errors. Otherwise, Postfix will
+just be a sitting duck waiting to be hit by a bit error. Critical
+systems deserve real hardware.
+
+When a compiler messes up, the problem can be reproduced whenever
+the resulting program is run. Compiler errors are most likely to
+happen in the code optimizer. If a problem is reproducible across
+power cycles and system reboots, it can be worthwhile to rebuild
+Postfix with optimization disabled, and to see if optimization
+makes a difference.
+
+In order to compile Postfix with optimizations turned off:
+
+ % make tidy
+ % make makefiles OPT=
+
+This produces a set of Makefiles that do not request compiler
+optomization.
+
+Once the makefiles are set up, build the software:
+
+ % make
+ % su
+ # make install
+
+And see if the problem reproduces. If the problem goes away, talk
+to your vendor.
Feature: permit_auth_destination restriction based on code
by Jesper Skriver @ skriver.dk.
- Code cleanup: the transport table now can override local
- deliveries. Postfix no longer uses the "empty next-hop
- hostname hack" to remember that a destination is local.
+ Code cleanup: the transport table now can override all
+ deliveries, including local ones.
+
+19991116
+
+ Code cleanup: a new "local_transports" configuration
+ parameter explicitly lists all transports that deliver
+ mail locally. The first name listed there is the default
+ local transport. This is the end of the "empty next-hop
+ hostname" hack to indicate that a destination is local.
+ Files: trivial-rewrite/resolve.c, global/local_transport.[hc]
+
+ Feature: "postconf -m" shows what lookup table types are
+ available. Code by Scott Cotton, Internet Consultants
+ Group, Inc.
+
+ Feature: "postconf -e" edits any number of main.cf parameters.
+ The edit is done on a copy, and the copy is renamed into
+ the place of the original. File: postconf/postconf.c,
+ util/readlline.[hc].
dns_lookup.o: ../include/vbuf.h
dns_lookup.o: ../include/msg.h
dns_lookup.o: ../include/valid_hostname.h
+dns_lookup.o: ../include/stringops.h
dns_lookup.o: dns.h
dns_rr.o: dns_rr.c
dns_rr.o: ../include/sys_defs.h
static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
char *rr_name, DNS_FIXED *fixed)
{
- char temp[DNS_NAME_LEN];
+ unsigned char temp[DNS_NAME_LEN];
int data_len;
unsigned pref = 0;
unsigned char *src;
SRCS = been_here.c bounce.c canon_addr.c cleanup_strerror.c clnt_stream.c \
debug_peer.c debug_process.c defer.c deliver_completed.c \
deliver_flock.c deliver_pass.c deliver_request.c domain_list.c \
- dot_lockfile.c file_id.c header_opts.c is_header.c mail_addr.c \
+ dot_lockfile.c dot_lockfile_as.c ext_prop.c file_id.c \
+ header_opts.c is_header.c local_transport.c mail_addr.c \
mail_addr_crunch.c mail_addr_find.c mail_addr_map.c \
mail_command_read.c mail_command_write.c mail_conf.c \
mail_conf_bool.c mail_conf_int.c mail_conf_raw.c mail_conf_str.c \
recipient_list.c record.c remove.c resolve_clnt.c resolve_local.c \
rewrite_clnt.c sent.c smtp_stream.c split_addr.c string_list.c \
sys_exits.c timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \
- tok822_resolve.c tok822_rewrite.c tok822_tree.c ext_prop.c \
- dot_lockfile_as.c
+ tok822_resolve.c tok822_rewrite.c tok822_tree.c
OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
debug_peer.o debug_process.o defer.o deliver_completed.o \
deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
- dot_lockfile.o file_id.o header_opts.o is_header.o mail_addr.o \
+ dot_lockfile.o dot_lockfile_as.o ext_prop.o file_id.o \
+ header_opts.o is_header.o local_transport.o mail_addr.o \
mail_addr_crunch.o mail_addr_find.o mail_addr_map.o \
mail_command_read.o mail_command_write.o mail_conf.o \
mail_conf_bool.o mail_conf_int.o mail_conf_raw.o mail_conf_str.o \
recipient_list.o record.o remove.o resolve_clnt.o resolve_local.o \
rewrite_clnt.o sent.o smtp_stream.o split_addr.o string_list.o \
sys_exits.o timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
- tok822_resolve.o tok822_rewrite.o tok822_tree.o ext_prop.o \
- dot_lockfile_as.o
+ tok822_resolve.o tok822_rewrite.o tok822_tree.o
HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \
- dot_lockfile.h file_id.h header_opts.h is_header.h mail_addr.h \
+ dot_lockfile.h dot_lockfile_as.h ext_prop.h file_id.h \
+ header_opts.h is_header.h local_transport.h mail_addr.h \
mail_addr_crunch.h mail_addr_find.h mail_addr_map.h mail_conf.h \
mail_copy.h mail_date.h mail_error.h mail_flush.h mail_open_ok.h \
mail_params.h mail_proto.h mail_queue.h mail_run.h mail_scan_dir.h \
quote_822_local.h rec_streamlf.h rec_type.h recipient_list.h \
record.h resolve_clnt.h resolve_local.h rewrite_clnt.h sent.h \
smtp_stream.h split_addr.h string_list.h sys_exits.h timed_ipc.h \
- tok822.h ext_prop.h dot_lockfile_as.h
+ tok822.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
mail_addr_map mail_date maps mynetworks mypwd namadr_list \
off_cvt quote_822_local rec2stream recdump resolve_clnt \
- resolve_local rewrite_clnt stream2rec string_list tok822_parse
+ resolve_local rewrite_clnt stream2rec string_list tok822_parse \
+ local_transport
LIBS = ../lib/libutil.a
LIB_DIR = ../lib
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
mv junk $@.o
+local_transport: $(LIB) $(LIBS)
+ mv $@.o junk
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+ mv junk $@.o
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
is_header.o: is_header.c
is_header.o: ../include/sys_defs.h
is_header.o: is_header.h
+local_transport.o: local_transport.c
+local_transport.o: ../include/sys_defs.h
+local_transport.o: ../include/msg.h
+local_transport.o: ../include/mymalloc.h
+local_transport.o: string_list.h
+local_transport.o: mail_params.h
+local_transport.o: local_transport.h
mail_addr.o: mail_addr.c
mail_addr.o: ../include/sys_defs.h
mail_addr.o: ../include/stringops.h
--- /dev/null
+/*++
+/* NAME
+/* local_transport 3
+/* SUMMARY
+/* determine if transport delivers locally
+/* SYNOPSIS
+/* #include <local_transport.h>
+/*
+/* const char *def_local_transport()
+/*
+/* int local_transport(transport)
+/* const char *transport;
+/* DESCRIPTION
+/* This module uses the information kept in the "local_transports"
+/* configuration parameter, which lists the names of the default
+/* local transport followed by zero or more other transports that
+/* deliver locally.
+/*
+/* def_local_transport() returns the name of the default local
+/* transport, that is, the first transport name specified with
+/* the "local_transports" configuration parameter.
+/*
+/* local_transport() determines if the named transport is listed
+/* in the "local_transports" configuration parameter.
+/* SEE ALSO
+/* resolve_local(3), see if address resolves locally.
+/* 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 <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <string_list.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <local_transport.h>
+
+/* Application-specific */
+
+static STRING_LIST *local_transport_list;
+static char *local_transport_name;
+
+/* local_transport_init - initialize lookup table */
+
+static void local_transport_init(void)
+{
+ char *myname = "local_transport_init";
+
+ /*
+ * Sanity check.
+ */
+ if (local_transport_list || local_transport_name)
+ msg_panic("local_transport_init: duplicate initialization");
+
+ /*
+ * Initialize.
+ */
+ local_transport_list = string_list_init(var_local_transports);
+ local_transport_name = mystrndup(var_local_transports,
+ strcspn(var_local_transports, ", \t\r\n"));
+
+ /*
+ * Sanity check.
+ */
+ if (!local_transport(local_transport_name))
+ msg_panic("%s: unable to intialize", myname);
+}
+
+/* def_local_transport - determine default local transport */
+
+const char *def_local_transport(void)
+{
+
+ /*
+ * Initialize on the fly.
+ */
+ if (local_transport_list == 0)
+ local_transport_init();
+
+ /*
+ * Return the first transport listed.
+ */
+ return (local_transport_name);
+}
+
+/* local_transport - match address against list of local destinations */
+
+int local_transport(const char *transport)
+{
+
+ /*
+ * Initialize on the fly.
+ */
+ if (local_transport_list == 0)
+ local_transport_init();
+
+ /*
+ * Compare the transport against the list of transports that are listed
+ * as delivering locally.
+ */
+ return (string_list_match(local_transport_list, transport));
+}
+
+#ifdef TEST
+
+#include <vstream.h>
+#include <mail_conf.h>
+
+int main(int argc, char **argv)
+{
+ if (argc != 2)
+ msg_fatal("usage: %s transport", argv[0]);
+ mail_conf_read();
+ vstream_printf("%s\n", local_transport(argv[1]) ? "yes" : "no");
+ vstream_fflush(VSTREAM_OUT);
+}
+
+#endif
--- /dev/null
+#ifndef _LOCAL_TRANSPORT_H_INCLUDED_
+#define _LOCAL_TRANSPORT_H_INCLUDED_
+
+/*++
+/* NAME
+/* local_transport 3h
+/* SUMMARY
+/* determine if transport delivers locally
+/* SYNOPSIS
+/* #include <local_transport.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * External interface.
+ */
+extern const char *def_local_transport(void);
+extern int local_transport(const char *);
+
+/* 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
/* time_t var_starttime;
/* int var_ownreq_special;
/* int var_daemon_timeout;
+/* char *var_local_transports;
/*
/* void mail_params_init()
/* DESCRIPTION
time_t var_starttime;
int var_ownreq_special;
int var_daemon_timeout;
+char *var_local_transports;
/* check_myhostname - lookup hostname and validate */
VAR_DB_TYPE, DEF_DB_TYPE, &var_db_type, 1, 0,
VAR_HASH_QUEUE_NAMES, DEF_HASH_QUEUE_NAMES, &var_hash_queue_names, 1, 0,
VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim, 0, 1,
+ VAR_LOCAL_TRANSP, DEF_LOCAL_TRANSP, &var_local_transports, 1, 0,
0,
};
static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
#define VAR_MYDOMAIN "mydomain" /* my domain name */
extern char *var_mydomain;
+ /*
+ * Transports that deliver locally. Order matters.
+ */
+#define VAR_LOCAL_TRANSP "local_transports"
+#define DEF_LOCAL_TRANSP "local"
+extern char *var_local_transports;
+
/*
* Where to send postmaster copies of bounced mail, and other notices.
*/
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-19991115"
+#define DEF_MAIL_VERSION "Snapshot-19991116"
extern char *var_mail_version;
/* LICENSE
resolve.o: ../include/rewrite_clnt.h
resolve.o: ../include/tok822.h
resolve.o: ../include/mail_params.h
+resolve.o: ../include/local_transport.h
resolve.o: local.h
resolve.o: ../include/been_here.h
resolve.o: ../include/deliver_request.h
#include <rewrite_clnt.h>
#include <tok822.h>
#include <mail_params.h>
-#include <resolve_local.h>
+#include <local_transport.h>
/* Application-specific. */
* ugly code to force local recursive alias expansions on a host with no
* authority over the local domain, but that code was just too unclean.
*/
- if (resolve_local(STR(reply.nexthop))) {
+ if (local_transport(STR(reply.transport))) {
status = deliver_recipient(state, usr_attr);
} else {
status = deliver_indirect(state);
* Skip blank lines and comment lines.
*/
do {
- if (readlline(buf, master_fp, &master_line) == 0) {
+ if (readlline(buf, master_fp, &master_line, READLL_STRIPNL) == 0) {
vstring_free(buf);
vstring_free(junk);
return (0);
* Add records to the database.
*/
lineno = 0;
- while (readlline(line_buffer, source_fp, &lineno)) {
+ while (readlline(line_buffer, source_fp, &lineno, READLL_STRIPNL)) {
/*
* Skip comments.
/* Postfix configuration utility
/* SYNOPSIS
/* .fi
-/* \fBpostconf\fR [\fB-c \fIconfig_dir\fR] [\fB-d\fR] [\fB-h\fR]
-/* [\fB-n\fR] [\fB-v\fR] [\fIparameter ...\fR]
+/* \fBpostconf\fR [\fB-dhnv\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fIparameter ...\fR]
+/*
+/* \fBpostconf\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fIparameter=value ...\fR]
/* DESCRIPTION
/* The \fBpostconf\fR command prints the actual value of
/* \fIparameter\fR (all known parameters by default), one
-/* parameter per line.
+/* parameter per line, changes its value, or prints other
+/* information about the Postfix mail system.
/*
/* Options:
/* .IP "\fB-c \fIconfig_dir\fR"
/* The \fBmain.cf\fR configuration file is in the named directory.
/* .IP \fB-d\fR
/* Print default parameter settings instead of actual settings.
+/* .IP \fB-e\fR
+/* Edit the \fBmain.cf\fR configuration file. The file is copied
+/* to a temporary file then renamed into place. Parameters and
+/* values are specified on the command line. Use quotes in order
+/* to protect shell metacharacters and whitespace.
/* .IP \fB-h\fR
/* Show parameter values only, not the ``name = '' label
/* that normally precedes the value.
+/* .IP \fB-m\fR
+/* List the names of all supported lookup table types.
/* .IP \fB-n\fR
/* Print non-default parameter settings only.
/* .IP \fB-v\fR
#include <sys_defs.h>
#include <sys/stat.h>
+#include <stdio.h> /* rename() */
#include <pwd.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+#include <ctype.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#include <dict.h>
#include <safe.h>
#include <mymalloc.h>
-#include <line_wrap.h>
+#include <argv.h>
+#include <split_at.h>
+#include <readlline.h>
+#include <myflock.h>
/* Global library. */
#include <mail_addr.h>
/*
- * What output we should generate.
+ * What we're supposed to be doing.
*/
#define SHOW_NONDEF (1<<0) /* show non-default settings */
#define SHOW_DEFS (1<<1) /* show default setting */
#define SHOW_NAME (1<<2) /* show parameter name */
+#define SHOW_MAPS (1<<3) /* show map types */
+#define EDIT_MAIN (1<<4) /* edit main.cf */
/*
* Lookup table for in-core parameter info.
return (mynetworks());
}
+/* edit_parameters - edit parameter file */
+
+static void edit_parameters(int argc, char **argv)
+{
+ char *config_dir;
+ char *path;
+ char *temp;
+ VSTREAM *src;
+ VSTREAM *dst;
+ VSTRING *buf = vstring_alloc(100);
+ VSTRING *key = vstring_alloc(10);
+ char *cp;
+ char *value;
+ HTABLE *table;
+ int first = 1;
+ struct cvalue {
+ char *value;
+ int found;
+ };
+ struct cvalue *cvalue;
+ HTABLE_INFO **ht_info;
+ HTABLE_INFO **ht;
+
+ /*
+ * Store command-line parameters for quick lookup.
+ */
+ table = htable_create(argc);
+ while ((cp = *argv++) != 0) {
+ if ((value = split_at(cp, '=')) == 0
+ || *(cp += strspn(cp, " \t\r\n")) == 0)
+ msg_fatal("edit requires \"key = value\" arguments");
+ cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
+ cvalue->value = value;
+ cvalue->found = 0;
+ htable_enter(table, mystrtok(&cp, " \t\r\n"), (char *) cvalue);
+ }
+
+ /*
+ * XXX Avoid code duplication by better code decomposition.
+ */
+ if (var_config_dir)
+ myfree(var_config_dir);
+ var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ?
+ config_dir : DEF_CONFIG_DIR); /* XXX */
+ set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
+
+ /*
+ * Open the original file for input.
+ */
+ path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
+ if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0)
+ msg_fatal("open %s for reading: %m", path);
+
+ /*
+ * Open a temp file for the result. We use a fixed name so we don't leave
+ * behind thrash with random names. Lock the temp file to avoid
+ * accidents. Truncate the file only after we have an exclusive lock.
+ */
+ temp = concatenate(path, ".tmp", (char *) 0);
+ if ((dst = vstream_fopen(temp, O_CREAT | O_WRONLY, 0644)) == 0)
+ msg_fatal("open %s: %m", temp);
+ if (myflock(vstream_fileno(dst), MYFLOCK_EXCLUSIVE) < 0)
+ msg_fatal("lock %s: %m", temp);
+ if (ftruncate(vstream_fileno(dst), 0) < 0)
+ msg_fatal("truncate %s: %m", temp);
+
+ /*
+ * Copy original file to temp file, while replacing parameters on the
+ * fly. Issue warnings for names found multiple times.
+ */
+#define STR(x) vstring_str(x)
+
+ while (readlline(buf, src, (int *) 0, READLL_KEEPNL)) {
+ cp = STR(buf);
+ if (first) {
+ first = 0;
+ if (ISSPACE(*cp))
+ msg_fatal("%s: file starts with whitespace", path);
+ }
+ if (*cp == '#') {
+ vstream_fputs(STR(buf), dst);
+ continue;
+ }
+ cp += strspn(cp, " \t\r\n");
+ vstring_strncpy(key, cp, strcspn(cp, " \t\r\n="));
+ cvalue = (struct cvalue *) htable_find(table, STR(key));
+ if (cvalue == 0) {
+ vstream_fputs(STR(buf), dst);
+ } else {
+ if (cvalue->found++ == 1)
+ msg_warn("%s: multiple entries for key %s", path, STR(key));
+ vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
+ }
+ }
+
+ /*
+ * Generate new entries for parameters that were not found.
+ */
+ for (ht_info = ht = htable_list(table); *ht; ht++) {
+ cvalue = (struct cvalue *) ht[0]->value;
+ if (cvalue->found == 0)
+ vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value);
+ }
+ myfree((char *) ht_info);
+
+ /*
+ * When all is well, rename the temp file to the original one.
+ */
+ if (vstream_fclose(src))
+ msg_fatal("read %s: %m", path);
+ if (vstream_fclose(dst))
+ msg_fatal("write %s: %m", temp);
+ if (rename(temp, path) < 0)
+ msg_fatal("rename %s to %s: %m", temp, path);
+
+ /*
+ * Cleanup.
+ */
+ myfree(path);
+ myfree(temp);
+ vstring_free(buf);
+ vstring_free(key);
+ htable_free(table, myfree);
+}
+
/* read_parameters - read parameter info from file */
static void read_parameters(void)
return (strcmp(ap[0]->key, bp[0]->key));
}
+/* show_maps - show available maps */
+
+static void show_maps(void)
+{
+ ARGV *maps_argv;
+ int i;
+
+ maps_argv = dict_mapnames();
+ for (i = 0; i < maps_argv->argc; i++)
+ vstream_printf("%s\n", maps_argv->argv[i]);
+ argv_free(maps_argv);
+}
+
/* show_parameters - show parameter info */
static void show_parameters(int mode, char **names)
int ch;
int fd;
struct stat st;
+ int junk;
/*
* To minimize confusion, make sure that the standard file descriptors
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "c:dhnv")) > 0) {
+ while ((ch = GETOPT(argc, argv, "c:dehnmv")) > 0) {
switch (ch) {
case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
msg_fatal("out of memory");
break;
case 'd':
- if (mode & SHOW_NONDEF)
- msg_fatal("specify one of -d and -n");
mode |= SHOW_DEFS;
break;
+ case 'e':
+ mode |= EDIT_MAIN;
+ break;
case 'h':
mode &= ~SHOW_NAME;
break;
+ case 'm':
+ mode |= SHOW_MAPS;
+ break;
case 'n':
- if (mode & SHOW_DEFS)
- msg_fatal("specify one of -d and -n");
mode |= SHOW_NONDEF;
break;
case 'v':
msg_verbose++;
break;
default:
- msg_fatal("usage: %s [-c config_dir] [-d (defaults)] [-h (no names)] [-n (non-defaults)] [-v] name...", argv[0]);
+ msg_fatal("usage: %s [-c config_dir] [-d (defaults)] [-e (edit)] [-h (no names)] [-m (map types) [-n (non-defaults)] [-v] [name...]", argv[0]);
}
}
/*
- * If showing non-default values, read main.cf.
+ * Sanity check.
+ */
+ junk = (mode & (SHOW_DEFS | SHOW_NONDEF | SHOW_MAPS | EDIT_MAIN));
+ if (junk != 0 && junk != SHOW_DEFS && junk != SHOW_NONDEF
+ && junk != SHOW_MAPS && junk != EDIT_MAIN)
+ msg_fatal("specify one of -d, -e, -n and -m");
+
+ /*
+ * If showing map types, show them and exit
+ */
+ if (mode & SHOW_MAPS) {
+ show_maps();
+ }
+
+ /*
+ * Edit main.cf.
*/
- if ((mode & SHOW_DEFS) == 0)
- read_parameters();
+ if (mode & EDIT_MAIN) {
+ edit_parameters(argc - optind, argv + optind);
+ }
/*
- * Throw together all parameters and show the asked values.
+ * If showing non-default values, read main.cf.
*/
- hash_parameters();
- show_parameters(mode, argv + optind);
+ else {
+ if ((mode & SHOW_DEFS) == 0)
+ read_parameters();
+
+ /*
+ * Throw together all parameters and show the asked values.
+ */
+ hash_parameters();
+ show_parameters(mode, argv + optind);
+ }
vstream_fflush(VSTREAM_OUT);
exit(0);
}
* Add records to the database.
*/
lineno = 0;
- while (readlline(line_buffer, source_fp, &lineno)) {
+ while (readlline(line_buffer, source_fp, &lineno, READLL_STRIPNL)) {
/*
* Skip comments.
#include <deliver_completed.h>
#include <mail_addr_find.h>
#include <opened.h>
-#include <resolve_local.h>
+#include <local_transport.h>
/* Client stubs. */
/*
* Bounce mail to non-existent users in virtual domains.
*/
- if (!resolve_local(STR(reply.nexthop))
+ if (!local_transport(STR(reply.transport))
&& qmgr_virtual != 0
&& (at = strrchr(recipient->address, '@')) != 0) {
domain = lowercase(mystrdup(at + 1));
* job requires knowledge of local aliases. Yuck! I don't want to
* duplicate delivery-agent specific knowledge in the queue manager.
*/
- if (resolve_local(STR(reply.nexthop))) {
+ if (local_transport(STR(reply.transport))) {
vstring_strcpy(reply.nexthop, STR(reply.recipient));
(void) split_at_right(STR(reply.nexthop), '@');
#if 0
smtpd_check.o: ../include/mail_conf.h
smtpd_check.o: ../include/maps.h
smtpd_check.o: ../include/mail_addr_find.h
+smtpd_check.o: ../include/local_transport.h
smtpd_check.o: smtpd.h
smtpd_check.o: ../include/mail_stream.h
smtpd_check.o: smtpd_check.h
#include <mail_conf.h>
#include <maps.h>
#include <mail_addr_find.h>
+#include <local_transport.h>
/* Application-specific. */
* Permit if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
- if (resolve_local(STR(reply.nexthop))
+ if (local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
* Permit if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
- if (resolve_local(STR(reply.nexthop))
+ if (local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
* Pass if destination is local. XXX This must be generalized for
* per-domain user tables and for non-UNIX local delivery agents.
*/
- if (resolve_local(STR(reply.nexthop))
+ if (local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_DUNNO);
domain += 1;
* If the destination is local, it is acceptable, because we are
* supposedly MX for our own address.
*/
- if (resolve_local(STR(reply.nexthop))
+ if (local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
/*
* Skip local destinations and non-DNS forms.
*/
- if (resolve_local(STR(reply.nexthop))
+ if (local_transport(STR(reply.transport))
|| (domain = strrchr(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_DUNNO);
domain += 1;
return (SMTPD_CHECK_DUNNO);
domain += 1;
if (domain[0] == '#' || domain[0] == '[')
- if (!resolve_local(STR(reply.nexthop)))
+ if (!local_transport(STR(reply.transport)))
return (SMTPD_CHECK_DUNNO);
/*
#include <mail_conf.h>
#include <quote_822_local.h>
#include <tok822.h>
+#include <local_transport.h>
/* Application-specific. */
}
/*
- * The transport map, if specified, overrides default routing.
+ * Make sure the resolved envelope recipient has the user@domain form. If
+ * no domain was specified in the address, assume the local machine. See
+ * above for what happens with an empty localpart.
*/
- if (*var_transport_maps == 0
- || (tok822_internalize(addr_buf, domain->next, TOK822_STR_DEFL),
- transport_lookup(STR(addr_buf), channel, nexthop)) == 0) {
-
- /*
- * Non-local delivery. Use the default transport specified in
- * var_def_transport. If no default mail relay is specified in
- * var_relayhost, forward to the domain's mail exchanger.
- */
- if (domain != 0) {
- vstring_strcpy(channel, var_def_transport);
- if (*var_relayhost)
- vstring_strcpy(nexthop, var_relayhost);
- else
- tok822_internalize(nexthop, domain->next, TOK822_STR_DEFL);
- }
-
- /*
- * Local delivery. Use the default transport and next-hop hostname.
- * If no domain was specified, assume the local machine. See above
- * for what happens with an empty localpart.
- */
- else {
- vstring_strcpy(channel, MAIL_SERVICE_LOCAL);
- vstring_strcpy(nexthop, var_myhostname);
- if (saved_domain) {
- tok822_sub_append(tree, saved_domain);
- saved_domain = 0;
- } else {
- tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
- tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0));
- }
+ if (domain == 0) {
+ if (saved_domain) {
+ tok822_sub_append(tree, saved_domain);
+ saved_domain = 0;
+ } else {
+ tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
+ tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0));
}
}
tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
+ /*
+ * The transport map overrides the default transport and next-hop host
+ * info that was set up just moments ago. For a long time, it was not
+ * possible to override routing of mail that resolves locally, because
+ * Postfix used a zero-length next-hop hostname result to indicate local
+ * delivery, and transport maps cannot return zero-length hostnames.
+ */
+ if (*var_transport_maps
+ && transport_lookup(strrchr(STR(nextrcpt), '@') + 1, channel, nexthop)) {
+ /* void */ ;
+ }
+
+ /*
+ * Non-local delivery, presumably. Set up the default remote transport
+ * specified with var_local_transports. Use the destination's mail
+ * exchanger unless a default mail relay is specified with var_relayhost.
+ */
+ else if (domain != 0) {
+ vstring_strcpy(channel, var_def_transport);
+ if (*var_relayhost)
+ vstring_strcpy(nexthop, var_relayhost);
+ else
+ tok822_internalize(nexthop, domain->next, TOK822_STR_DEFL);
+ }
+
+ /*
+ * Local delivery. Set up the default local transport and the default
+ * next-hop hostname.
+ */
+ else {
+ vstring_strcpy(channel, def_local_transport());
+ vstring_strcpy(nexthop, var_myhostname);
+ }
+
/*
* Clean up.
*/
buf = vstring_alloc(100);
lineno = 0;
- while (readlline(buf, fp, &lineno)) {
+ while (readlline(buf, fp, &lineno, READLL_STRIPNL)) {
start = STR(buf);
SKIP(start, member, ISSPACE(*member)); /* find member begin */
if (*member == 0 || *member == '#')
* Utility library.
*/
#include <vstream.h>
+#include <argv.h>
/*
* Generic dictionary interface - in reality, a dictionary extends this
#define DICT_SEQ_FUN_FIRST 0 /* set cursor to first record */
#define DICT_SEQ_FUN_NEXT 1 /* set cursor to next record */
+ /*
+ * Interface for dictionary types.
+ */
+extern ARGV *dict_mapnames(void);
+
/*
* High-level interface, with logical dictionary names.
*/
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
+/**INDENT** Error@47: Unmatched #endif */
#endif
htable_enter(dict_open_hash, dp->type, (char *) dp);
}
+/* dict_mapnames - return an ARGV of available map_names */
+
+ARGV *dict_mapnames()
+{
+ HTABLE_INFO **ht_info;
+ HTABLE_INFO **ht;
+ DICT_OPEN_INFO *dp;
+ ARGV *mapnames;
+
+ if (dict_open_hash == 0)
+ dict_open_init();
+ mapnames = argv_alloc(dict_open_hash->used + 1);
+ for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) {
+ dp = (DICT_OPEN_INFO *) ht[0]->value;
+ argv_add(mapnames, dp->type, ARGV_END);
+ }
+ myfree((char *) ht_info);
+ argv_terminate(mapnames);
+ return mapnames;
+}
+
#ifdef TEST
/*
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) {
msg_fatal("open %s: %m", map);
}
- while (readlline(line_buffer, map_fp, &lineno)) {
+ while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) {
if (*vstring_str(line_buffer) == '#') /* Skip comments */
continue;
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) {
msg_fatal("open %s: %m", map);
}
- while (readlline(line_buffer, map_fp, &lineno)) {
+ while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) {
p = vstring_str(line_buffer);
if (*p == '#') /* Skip comments */
/* SYNOPSIS
/* #include <readlline.h>
/*
-/* VSTRING *readlline(buf, fp, lineno)
+/* VSTRING *readlline(buf, fp, lineno, stripnl)
/* VSTRING *buf;
/* VSTREAM *fp;
/* int *lineno;
+/* int stripnl;
/* DESCRIPTION
/* readlline() reads one logical line from the named stream.
/* A line that starts with whitespace is a continuation of
-/* the previous line. The newline between continued lines
+/* the previous line. When the stripnl argument is non-zero,
+/* the newline between continued lines
/* is deleted from the input. The result value is the input
/* buffer argument or a null pointer when no input is found.
/*
/* .IP lineno
/* A null pointer, or a pointer to an integer that is incremented
/* after reading a newline.
+/* .IP stripnl
+/* Non-zero to strip newlines. readlline.h provides the symbolic
+/* constants READLL_STRIPNL and READLL_KEEPNL for convenience.
/* LICENSE
/* .ad
/* .fi
/* readlline - read one logical line */
-VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno)
+VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno, int stripnl)
{
int ch;
int next;
VSTRING_RESET(buf);
while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) {
if (ch == '\n') {
+ if (stripnl == 0)
+ VSTRING_ADDCH(buf, ch);
if (lineno)
*lineno += 1;
if ((next = VSTREAM_GETC(fp)) == ' ' || next == '\t') {
/*
* External interface.
*/
-extern VSTRING *readlline(VSTRING *, VSTREAM *, int *);
+extern VSTRING *readlline(VSTRING *, VSTREAM *, int *, int);
+
+#define READLL_STRIPNL 1
+#define READLL_KEEPNL 0
/* LICENSE
/* .ad