summaryrefslogtreecommitdiff
path: root/gnu/dist/postfix/src
diff options
context:
space:
mode:
authorperry <perry@NetBSD.org>2002-02-02 23:10:24 +0000
committerperry <perry@NetBSD.org>2002-02-02 23:10:24 +0000
commit6f59dc7aebbf705da79e61ef3fd24b48ed99d327 (patch)
treec6e9fd356c8aac78b966a9dd53f5d0d14d5e62cd /gnu/dist/postfix/src
parentecf43984517b4cfb837e78958769cc8207ea5288 (diff)
Postfix 1.1.2
(Postfix releases are now numbered -- 1.1.2 means 1.1, patchlevel 2.) Lots of new features, same great security.
Diffstat (limited to 'gnu/dist/postfix/src')
-rw-r--r--gnu/dist/postfix/src/bounce/Makefile.in22
-rw-r--r--gnu/dist/postfix/src/bounce/bounce.c92
-rw-r--r--gnu/dist/postfix/src/bounce/bounce_notify_service.c9
-rw-r--r--gnu/dist/postfix/src/bounce/bounce_notify_util.c30
-rw-r--r--gnu/dist/postfix/src/bounce/bounce_notify_verp.c210
-rw-r--r--gnu/dist/postfix/src/bounce/bounce_service.h7
-rw-r--r--gnu/dist/postfix/src/cleanup/Makefile.in4
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup.c25
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup.h13
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup_api.c89
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup_envelope.c19
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup_extracted.c24
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup_init.c17
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup_message.c113
-rw-r--r--gnu/dist/postfix/src/cleanup/cleanup_state.c3
-rw-r--r--gnu/dist/postfix/src/dns/Makefile.in1
-rw-r--r--gnu/dist/postfix/src/dns/dns.h1
-rw-r--r--gnu/dist/postfix/src/dns/dns_rr.c50
-rw-r--r--gnu/dist/postfix/src/flush/Makefile.in4
-rw-r--r--gnu/dist/postfix/src/flush/flush.c98
-rw-r--r--gnu/dist/postfix/src/global/abounce.c99
-rw-r--r--gnu/dist/postfix/src/global/abounce.h3
-rw-r--r--gnu/dist/postfix/src/global/bounce.c22
-rw-r--r--gnu/dist/postfix/src/global/bounce.h4
-rw-r--r--gnu/dist/postfix/src/global/bounce_log.c35
-rw-r--r--gnu/dist/postfix/src/global/bounce_log.h4
-rw-r--r--gnu/dist/postfix/src/global/cleanup_user.h18
-rw-r--r--gnu/dist/postfix/src/global/debug_peer.c5
-rw-r--r--gnu/dist/postfix/src/global/defer.c38
-rw-r--r--gnu/dist/postfix/src/global/deliver_pass.c32
-rw-r--r--gnu/dist/postfix/src/global/deliver_request.c52
-rw-r--r--gnu/dist/postfix/src/global/deliver_request.h11
-rw-r--r--gnu/dist/postfix/src/global/domain_list.c43
-rw-r--r--gnu/dist/postfix/src/global/domain_list.h14
-rw-r--r--gnu/dist/postfix/src/global/file_id.c2
-rw-r--r--gnu/dist/postfix/src/global/flush_clnt.c34
-rw-r--r--gnu/dist/postfix/src/global/flush_clnt.h1
-rw-r--r--gnu/dist/postfix/src/global/is_header.c7
-rw-r--r--gnu/dist/postfix/src/global/mail_command_client.c82
-rw-r--r--gnu/dist/postfix/src/global/mail_command_server.c67
-rw-r--r--gnu/dist/postfix/src/global/mail_conf.c59
-rw-r--r--gnu/dist/postfix/src/global/mail_conf.h3
-rw-r--r--gnu/dist/postfix/src/global/mail_connect.c6
-rw-r--r--gnu/dist/postfix/src/global/mail_copy.c28
-rw-r--r--gnu/dist/postfix/src/global/mail_copy.h5
-rw-r--r--gnu/dist/postfix/src/global/mail_params.c178
-rw-r--r--gnu/dist/postfix/src/global/mail_params.h191
-rw-r--r--gnu/dist/postfix/src/global/mail_proto.h46
-rw-r--r--gnu/dist/postfix/src/global/mail_queue.c16
-rw-r--r--gnu/dist/postfix/src/global/mail_stream.c57
-rw-r--r--gnu/dist/postfix/src/global/mail_stream.h7
-rw-r--r--gnu/dist/postfix/src/global/mail_task.c12
-rw-r--r--gnu/dist/postfix/src/global/mail_trigger.c2
-rw-r--r--gnu/dist/postfix/src/global/mail_version.h21
-rw-r--r--gnu/dist/postfix/src/global/mark_corrupt.c23
-rw-r--r--gnu/dist/postfix/src/global/match_parent_style.c72
-rw-r--r--gnu/dist/postfix/src/global/match_parent_style.h35
-rw-r--r--gnu/dist/postfix/src/global/namadr_list.c44
-rw-r--r--gnu/dist/postfix/src/global/namadr_list.h17
-rw-r--r--gnu/dist/postfix/src/global/off_cvt.c29
-rw-r--r--gnu/dist/postfix/src/global/pipe_command.c7
-rw-r--r--gnu/dist/postfix/src/global/pipe_command.h1
-rw-r--r--gnu/dist/postfix/src/global/post_mail.c38
-rw-r--r--gnu/dist/postfix/src/global/post_mail.h5
-rw-r--r--gnu/dist/postfix/src/global/qmqp_proto.h27
-rw-r--r--gnu/dist/postfix/src/global/rec_type.c3
-rw-r--r--gnu/dist/postfix/src/global/rec_type.h7
-rw-r--r--gnu/dist/postfix/src/global/recdump.c3
-rw-r--r--gnu/dist/postfix/src/global/resolve_clnt.c15
-rw-r--r--gnu/dist/postfix/src/global/rewrite_clnt.c10
-rw-r--r--gnu/dist/postfix/src/global/smtp_stream.c11
-rw-r--r--gnu/dist/postfix/src/global/string_list.c33
-rw-r--r--gnu/dist/postfix/src/global/string_list.h14
-rw-r--r--gnu/dist/postfix/src/global/tok822.h3
-rw-r--r--gnu/dist/postfix/src/global/tok822_node.c2
-rw-r--r--gnu/dist/postfix/src/global/tok822_parse.ref14
-rw-r--r--gnu/dist/postfix/src/global/verp_sender.c106
-rw-r--r--gnu/dist/postfix/src/global/verp_sender.h36
-rw-r--r--gnu/dist/postfix/src/lmtp/Makefile.in3
-rw-r--r--gnu/dist/postfix/src/lmtp/lmtp.c9
-rw-r--r--gnu/dist/postfix/src/lmtp/lmtp.h2
-rw-r--r--gnu/dist/postfix/src/lmtp/lmtp_chat.c2
-rw-r--r--gnu/dist/postfix/src/lmtp/lmtp_proto.c14
-rw-r--r--gnu/dist/postfix/src/lmtp/lmtp_sasl_proto.c16
-rw-r--r--gnu/dist/postfix/src/local/Makefile.in8
-rw-r--r--gnu/dist/postfix/src/local/alias.c15
-rw-r--r--gnu/dist/postfix/src/local/command.c17
-rw-r--r--gnu/dist/postfix/src/local/file.c23
-rw-r--r--gnu/dist/postfix/src/local/forward.c18
-rw-r--r--gnu/dist/postfix/src/local/local.c18
-rw-r--r--gnu/dist/postfix/src/local/local.h2
-rw-r--r--gnu/dist/postfix/src/local/mailbox.c43
-rw-r--r--gnu/dist/postfix/src/local/maildir.c28
-rw-r--r--gnu/dist/postfix/src/local/token.c7
-rw-r--r--gnu/dist/postfix/src/master/Makefile.in31
-rw-r--r--gnu/dist/postfix/src/master/mail_flow.c140
-rw-r--r--gnu/dist/postfix/src/master/mail_flow.h32
-rw-r--r--gnu/dist/postfix/src/master/mail_server.h2
-rw-r--r--gnu/dist/postfix/src/master/master.c17
-rw-r--r--gnu/dist/postfix/src/master/master_flow.c33
-rw-r--r--gnu/dist/postfix/src/master/master_proto.h12
-rw-r--r--gnu/dist/postfix/src/master/master_spawn.c17
-rw-r--r--gnu/dist/postfix/src/master/master_wakeup.c25
-rw-r--r--gnu/dist/postfix/src/master/multi_server.c29
-rw-r--r--gnu/dist/postfix/src/master/single_server.c16
-rw-r--r--gnu/dist/postfix/src/master/trigger_server.c12
-rw-r--r--gnu/dist/postfix/src/nqmgr/.printfck25
-rw-r--r--gnu/dist/postfix/src/nqmgr/Makefile.in296
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr.c582
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr.h431
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_active.c500
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_bounce.c79
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_defer.c164
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_deliver.c349
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_enable.c107
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_entry.c268
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_job.c912
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_message.c1025
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_move.c104
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_peer.c136
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_queue.c251
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_rcpt_list.c96
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_scan.c159
-rw-r--r--gnu/dist/postfix/src/nqmgr/qmgr_transport.c381
-rw-r--r--gnu/dist/postfix/src/pickup/Makefile.in5
-rw-r--r--gnu/dist/postfix/src/pickup/pickup.c61
-rw-r--r--gnu/dist/postfix/src/pipe/Makefile.in1
-rw-r--r--gnu/dist/postfix/src/pipe/pipe.c143
-rw-r--r--gnu/dist/postfix/src/postalias/Makefile.in31
-rw-r--r--gnu/dist/postfix/src/postalias/map-ABC1.ref1
-rw-r--r--gnu/dist/postfix/src/postalias/map-ABC2.ref1
-rw-r--r--gnu/dist/postfix/src/postalias/map-abc1.ref1
-rw-r--r--gnu/dist/postfix/src/postalias/map-abc2.ref1
-rw-r--r--gnu/dist/postfix/src/postalias/map-ghi1.ref1
-rw-r--r--gnu/dist/postfix/src/postalias/map-ghi2.ref1
-rw-r--r--gnu/dist/postfix/src/postalias/map.in2
-rw-r--r--gnu/dist/postfix/src/postalias/postalias.c168
-rw-r--r--gnu/dist/postfix/src/postconf/Makefile.in12
-rw-r--r--gnu/dist/postfix/src/postconf/install_table.h1
-rw-r--r--gnu/dist/postfix/src/postconf/install_vars.h1
-rw-r--r--gnu/dist/postfix/src/postconf/postconf.c73
-rw-r--r--gnu/dist/postfix/src/postdrop/Makefile.in3
-rw-r--r--gnu/dist/postfix/src/postdrop/postdrop.c134
-rw-r--r--gnu/dist/postfix/src/postfix/Makefile.in3
-rw-r--r--gnu/dist/postfix/src/postfix/postfix.c102
-rw-r--r--gnu/dist/postfix/src/postkick/Makefile.in2
-rw-r--r--gnu/dist/postfix/src/postkick/postkick.c2
-rw-r--r--gnu/dist/postfix/src/postmap/Makefile.in33
-rw-r--r--gnu/dist/postfix/src/postmap/map-ABC1.ref1
-rw-r--r--gnu/dist/postfix/src/postmap/map-ABC2.ref1
-rw-r--r--gnu/dist/postfix/src/postmap/map-abc1.ref1
-rw-r--r--gnu/dist/postfix/src/postmap/map-abc2.ref1
-rw-r--r--gnu/dist/postfix/src/postmap/map-ghi1.ref1
-rw-r--r--gnu/dist/postfix/src/postmap/map-ghi2.ref1
-rw-r--r--gnu/dist/postfix/src/postmap/map.in2
-rw-r--r--gnu/dist/postfix/src/postmap/postmap.c198
-rw-r--r--gnu/dist/postfix/src/postqueue/Makefile.in80
-rw-r--r--gnu/dist/postfix/src/postqueue/postqueue.c396
-rw-r--r--gnu/dist/postfix/src/postsuper/Makefile.in3
-rw-r--r--gnu/dist/postfix/src/postsuper/postsuper.c720
-rw-r--r--gnu/dist/postfix/src/qmgr/Makefile.in6
-rw-r--r--gnu/dist/postfix/src/qmgr/qmgr.c25
-rw-r--r--gnu/dist/postfix/src/qmgr/qmgr.h3
-rw-r--r--gnu/dist/postfix/src/qmgr/qmgr_active.c62
-rw-r--r--gnu/dist/postfix/src/qmgr/qmgr_deliver.c65
-rw-r--r--gnu/dist/postfix/src/qmgr/qmgr_move.c20
-rw-r--r--gnu/dist/postfix/src/qmqpd/.printfck25
-rw-r--r--gnu/dist/postfix/src/qmqpd/Makefile.in117
-rw-r--r--gnu/dist/postfix/src/qmqpd/qmqpd.c677
-rw-r--r--gnu/dist/postfix/src/qmqpd/qmqpd.h79
-rw-r--r--gnu/dist/postfix/src/qmqpd/qmqpd_peer.c183
-rw-r--r--gnu/dist/postfix/src/qmqpd/qmqpd_state.c98
-rw-r--r--gnu/dist/postfix/src/sendmail/Makefile.in3
-rw-r--r--gnu/dist/postfix/src/sendmail/sendmail.c388
-rw-r--r--gnu/dist/postfix/src/showq/Makefile.in1
-rw-r--r--gnu/dist/postfix/src/showq/showq.c49
-rw-r--r--gnu/dist/postfix/src/smtp/Makefile.in5
-rw-r--r--gnu/dist/postfix/src/smtp/smtp.c26
-rw-r--r--gnu/dist/postfix/src/smtp/smtp.h2
-rw-r--r--gnu/dist/postfix/src/smtp/smtp_chat.c2
-rw-r--r--gnu/dist/postfix/src/smtp/smtp_proto.c66
-rw-r--r--gnu/dist/postfix/src/smtp/smtp_sasl_glue.c4
-rw-r--r--gnu/dist/postfix/src/smtp/smtp_sasl_proto.c6
-rw-r--r--gnu/dist/postfix/src/smtp/smtp_state.c1
-rw-r--r--gnu/dist/postfix/src/smtpd/Makefile.in19
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd.h1
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_acl.in5
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_acl.ref8
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_chat.c7
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_check.in1
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_check.in21
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_check.ref2
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_check.ref22
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_check_access1
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_sasl_glue.c4
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_state.c1
-rw-r--r--gnu/dist/postfix/src/smtpd/smtpd_token.c2
-rw-r--r--gnu/dist/postfix/src/smtpstone/Makefile.in50
-rw-r--r--gnu/dist/postfix/src/smtpstone/qmqp-sink.c287
-rw-r--r--gnu/dist/postfix/src/smtpstone/qmqp-source.c593
-rw-r--r--gnu/dist/postfix/src/smtpstone/smtp-sink.c144
-rw-r--r--gnu/dist/postfix/src/smtpstone/smtp-source.c60
-rw-r--r--gnu/dist/postfix/src/trivial-rewrite/Makefile.in5
-rw-r--r--gnu/dist/postfix/src/trivial-rewrite/resolve.c12
-rw-r--r--gnu/dist/postfix/src/trivial-rewrite/rewrite.c9
-rw-r--r--gnu/dist/postfix/src/trivial-rewrite/transport.c10
-rw-r--r--gnu/dist/postfix/src/trivial-rewrite/trivial-rewrite.c10
-rw-r--r--gnu/dist/postfix/src/util/alldig.c48
-rw-r--r--gnu/dist/postfix/src/util/attr.h77
-rw-r--r--gnu/dist/postfix/src/util/attr_print0.c209
-rw-r--r--gnu/dist/postfix/src/util/attr_print64.c253
-rw-r--r--gnu/dist/postfix/src/util/attr_scan.ref36
-rw-r--r--gnu/dist/postfix/src/util/attr_scan0.c441
-rw-r--r--gnu/dist/postfix/src/util/attr_scan0.ref46
-rw-r--r--gnu/dist/postfix/src/util/attr_scan64.c506
-rw-r--r--gnu/dist/postfix/src/util/attr_scan64.ref46
-rw-r--r--gnu/dist/postfix/src/util/base64_code.c201
-rw-r--r--gnu/dist/postfix/src/util/base64_code.h36
-rw-r--r--gnu/dist/postfix/src/util/cache.in26
-rw-r--r--gnu/dist/postfix/src/util/dict.c36
-rw-r--r--gnu/dist/postfix/src/util/dict.h1
-rw-r--r--gnu/dist/postfix/src/util/dict_db.c17
-rw-r--r--gnu/dist/postfix/src/util/dict_ldap.c193
-rw-r--r--gnu/dist/postfix/src/util/dict_mysql.c9
-rw-r--r--gnu/dist/postfix/src/util/dict_open.c17
-rw-r--r--gnu/dist/postfix/src/util/dict_pcre.c8
-rw-r--r--gnu/dist/postfix/src/util/dict_regexp.c8
-rw-r--r--gnu/dist/postfix/src/util/dict_static.c72
-rw-r--r--gnu/dist/postfix/src/util/dict_static.h35
-rw-r--r--gnu/dist/postfix/src/util/dir_forest.c2
-rw-r--r--gnu/dist/postfix/src/util/events.c21
-rw-r--r--gnu/dist/postfix/src/util/events.h1
-rw-r--r--gnu/dist/postfix/src/util/fifo_trigger.c23
-rw-r--r--gnu/dist/postfix/src/util/inet_trigger.c49
-rw-r--r--gnu/dist/postfix/src/util/intv.c114
-rw-r--r--gnu/dist/postfix/src/util/intv.h38
-rw-r--r--gnu/dist/postfix/src/util/make_dirs.c20
-rw-r--r--gnu/dist/postfix/src/util/match_list.h4
-rw-r--r--gnu/dist/postfix/src/util/match_ops.h10
-rw-r--r--gnu/dist/postfix/src/util/msg.c26
-rw-r--r--gnu/dist/postfix/src/util/msg.h1
-rw-r--r--gnu/dist/postfix/src/util/mymalloc.c23
-rw-r--r--gnu/dist/postfix/src/util/myrand.c63
-rw-r--r--gnu/dist/postfix/src/util/myrand.h31
-rw-r--r--gnu/dist/postfix/src/util/netstring.c351
-rw-r--r--gnu/dist/postfix/src/util/netstring.h54
-rw-r--r--gnu/dist/postfix/src/util/rand_sleep.c6
-rw-r--r--gnu/dist/postfix/src/util/readlline.c98
-rw-r--r--gnu/dist/postfix/src/util/readlline.h5
-rw-r--r--gnu/dist/postfix/src/util/safe_open.c16
-rw-r--r--gnu/dist/postfix/src/util/sane_accept.c19
-rw-r--r--gnu/dist/postfix/src/util/split_nameval.c94
-rw-r--r--gnu/dist/postfix/src/util/stream_trigger.c51
-rw-r--r--gnu/dist/postfix/src/util/stringops.h2
-rw-r--r--gnu/dist/postfix/src/util/unix_trigger.c52
-rw-r--r--gnu/dist/postfix/src/util/vstream.c12
-rw-r--r--gnu/dist/postfix/src/util/vstream.h1
-rw-r--r--gnu/dist/postfix/src/util/vstring.c48
-rw-r--r--gnu/dist/postfix/src/util/vstring.h2
-rw-r--r--gnu/dist/postfix/src/util/vstring_vstream.c23
-rw-r--r--gnu/dist/postfix/src/util/vstring_vstream.h1
-rw-r--r--gnu/dist/postfix/src/virtual/.printfck25
-rw-r--r--gnu/dist/postfix/src/virtual/Makefile.in166
-rw-r--r--gnu/dist/postfix/src/virtual/deliver_attr.c74
-rw-r--r--gnu/dist/postfix/src/virtual/mailbox.c246
-rw-r--r--gnu/dist/postfix/src/virtual/maildir.c176
-rw-r--r--gnu/dist/postfix/src/virtual/recipient.c93
-rw-r--r--gnu/dist/postfix/src/virtual/unknown.c64
-rw-r--r--gnu/dist/postfix/src/virtual/virtual.c414
-rw-r--r--gnu/dist/postfix/src/virtual/virtual.h134
270 files changed, 18000 insertions, 1355 deletions
diff --git a/gnu/dist/postfix/src/bounce/Makefile.in b/gnu/dist/postfix/src/bounce/Makefile.in
index 4172afd76ce..66066c2330f 100644
--- a/gnu/dist/postfix/src/bounce/Makefile.in
+++ b/gnu/dist/postfix/src/bounce/Makefile.in
@@ -1,8 +1,8 @@
SHELL = /bin/sh
SRCS = bounce.c bounce_append_service.c bounce_notify_service.c \
- bounce_cleanup.c bounce_notify_util.c
+ bounce_cleanup.c bounce_notify_util.c bounce_notify_verp.c
OBJS = bounce.o bounce_append_service.o bounce_notify_service.o \
- bounce_cleanup.o bounce_notify_util.o
+ bounce_cleanup.o bounce_notify_util.o bounce_notify_verp.o
HDRS =
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -65,10 +65,12 @@ bounce.o: ../../include/vstream.h
bounce.o: ../../include/stringops.h
bounce.o: ../../include/mail_proto.h
bounce.o: ../../include/iostuff.h
+bounce.o: ../../include/attr.h
bounce.o: ../../include/mail_queue.h
bounce.o: ../../include/mail_params.h
bounce.o: ../../include/mail_conf.h
bounce.o: ../../include/bounce.h
+bounce.o: ../../include/mail_addr.h
bounce.o: ../../include/mail_server.h
bounce.o: bounce_service.h
bounce.o: ../../include/bounce_log.h
@@ -133,3 +135,19 @@ bounce_notify_util.o: ../../include/name_mask.h
bounce_notify_util.o: ../../include/bounce_log.h
bounce_notify_util.o: ../../include/mail_date.h
bounce_notify_util.o: bounce_service.h
+bounce_notify_verp.o: bounce_notify_verp.c
+bounce_notify_verp.o: ../../include/sys_defs.h
+bounce_notify_verp.o: ../../include/msg.h
+bounce_notify_verp.o: ../../include/vstream.h
+bounce_notify_verp.o: ../../include/vbuf.h
+bounce_notify_verp.o: ../../include/name_mask.h
+bounce_notify_verp.o: ../../include/mail_params.h
+bounce_notify_verp.o: ../../include/mail_queue.h
+bounce_notify_verp.o: ../../include/vstring.h
+bounce_notify_verp.o: ../../include/post_mail.h
+bounce_notify_verp.o: ../../include/cleanup_user.h
+bounce_notify_verp.o: ../../include/mail_addr.h
+bounce_notify_verp.o: ../../include/mail_error.h
+bounce_notify_verp.o: ../../include/verp_sender.h
+bounce_notify_verp.o: bounce_service.h
+bounce_notify_verp.o: ../../include/bounce_log.h
diff --git a/gnu/dist/postfix/src/bounce/bounce.c b/gnu/dist/postfix/src/bounce/bounce.c
index eeedb7b141b..3cb98cc0028 100644
--- a/gnu/dist/postfix/src/bounce/bounce.c
+++ b/gnu/dist/postfix/src/bounce/bounce.c
@@ -80,6 +80,11 @@
/* System library. */
#include <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
/* Utility library. */
@@ -95,6 +100,7 @@
#include <mail_params.h>
#include <mail_conf.h>
#include <bounce.h>
+#include <mail_addr.h>
/* Single-threaded server skeleton. */
@@ -122,6 +128,7 @@ static VSTRING *queue_id;
static VSTRING *queue_name;
static VSTRING *recipient;
static VSTRING *sender;
+static VSTRING *verp_delims;
static VSTRING *why;
#define STR vstring_str
@@ -135,8 +142,12 @@ static int bounce_append_proto(char *service_name, VSTREAM *client)
/*
* Read the and validate the client request.
*/
- if (mail_command_read(client, "%d %s %s %s",
- &flags, queue_id, recipient, why) != 4) {
+ if (mail_command_server(client,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
+ ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
+ ATTR_TYPE_END) != 4) {
msg_warn("malformed request");
return (-1);
}
@@ -171,8 +182,12 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush)
/*
* Read and validate the client request.
*/
- if (mail_command_read(client, "%d %s %s %s",
- &flags, queue_name, queue_id, sender) != 4) {
+ 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,
+ ATTR_TYPE_END) != 4) {
msg_warn("malformed request");
return (-1);
}
@@ -202,6 +217,65 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush)
STR(queue_id), STR(sender), flush));
}
+/* bounce_verp_proto - bounce_notify server protocol, VERP style */
+
+static int bounce_verp_proto(char *service_name, VSTREAM *client, int flush)
+{
+ char *myname = "bounce_verp_proto";
+ int flags;
+
+ /*
+ * Read and validate the client request.
+ */
+ 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,
+ ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims,
+ ATTR_TYPE_END) != 5) {
+ msg_warn("malformed request");
+ return (-1);
+ }
+ if (mail_queue_name_ok(STR(queue_name)) == 0) {
+ msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
+ return (-1);
+ }
+ if (mail_queue_id_ok(STR(queue_id)) == 0) {
+ msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
+ return (-1);
+ }
+ if (strlen(STR(verp_delims)) != 2) {
+ msg_warn("malformed verp delimiter string: %s",
+ printable(STR(verp_delims), '?'));
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("%s: service=%s queue=%s id=%s sender=%s delim=%s",
+ myname, service_name, STR(queue_name), STR(queue_id),
+ STR(sender), STR(verp_delims));
+
+ /*
+ * On request by the client, set up a trap to delete the log file in case
+ * of errors.
+ */
+ if (flags & BOUNCE_FLAG_CLEAN)
+ bounce_cleanup_register(service_name, STR(queue_id));
+
+ /*
+ * Execute the request. Fall back to traditional notification if a bounce
+ * was returned as undeliverable, because we don't want to VERPify those.
+ */
+ if (!*STR(sender) || !strcasecmp(STR(sender), mail_addr_double_bounce())) {
+ msg_warn("request to send VERP-style notification of bounced mail");
+ return (bounce_notify_service(service_name, STR(queue_name),
+ STR(queue_id), STR(sender), flush));
+ } else
+ return (bounce_notify_verp(service_name, STR(queue_name),
+ STR(queue_id), STR(sender),
+ STR(verp_delims), flush));
+}
+
/* bounce_service - parse bounce command type and delegate */
static void bounce_service(VSTREAM *client, char *service_name, char **argv)
@@ -225,9 +299,12 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv)
#define REALLY_BOUNCE 1
#define JUST_WARN 0
- if (mail_scan(client, "%d", &command) != 1) {
+ if (attr_scan(client, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
+ ATTR_TYPE_NUM, MAIL_ATTR_NREQ, &command, 0) != 1) {
msg_warn("malformed request");
status = -1;
+ } else if (command == BOUNCE_CMD_VERP) {
+ status = bounce_verp_proto(service_name, client, REALLY_BOUNCE);
} else if (command == BOUNCE_CMD_FLUSH) {
status = bounce_notify_proto(service_name, client, REALLY_BOUNCE);
} else if (command == BOUNCE_CMD_WARN) {
@@ -243,7 +320,9 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv)
* When the request has completed, send the completion status to the
* client.
*/
- mail_print(client, "%d", status);
+ attr_print(client, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
+ ATTR_TYPE_END);
vstream_fflush(client);
/*
@@ -271,6 +350,7 @@ static void post_jail_init(char *unused_name, char **unused_argv)
queue_name = vstring_alloc(10);
recipient = vstring_alloc(10);
sender = vstring_alloc(10);
+ verp_delims = vstring_alloc(10);
why = vstring_alloc(10);
}
diff --git a/gnu/dist/postfix/src/bounce/bounce_notify_service.c b/gnu/dist/postfix/src/bounce/bounce_notify_service.c
index 35e10a45cb9..284b6468ee5 100644
--- a/gnu/dist/postfix/src/bounce/bounce_notify_service.c
+++ b/gnu/dist/postfix/src/bounce/bounce_notify_service.c
@@ -138,8 +138,7 @@ int bounce_notify_service(char *service, char *queue_name,
postmaster = flush ? var_2bounce_rcpt : var_delay_rcpt;
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
- NULL_CLEANUP_FLAGS,
- "BOUNCE")) != 0) {
+ NULL_CLEANUP_FLAGS)) != 0) {
/*
* Double bounce to Postmaster. This is the last opportunity
@@ -163,8 +162,7 @@ int bounce_notify_service(char *service, char *queue_name,
*/
else {
if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
- NULL_CLEANUP_FLAGS,
- "BOUNCE")) != 0) {
+ NULL_CLEANUP_FLAGS)) != 0) {
/*
* Send the bounce message header, some boilerplate text that
@@ -203,8 +201,7 @@ int bounce_notify_service(char *service, char *queue_name,
postmaster = flush ? var_bounce_rcpt : var_delay_rcpt;
if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
postmaster,
- NULL_CLEANUP_FLAGS,
- "BOUNCE")) != 0) {
+ NULL_CLEANUP_FLAGS)) != 0) {
if (bounce_header(bounce, bounce_info, postmaster) == 0
&& bounce_diagnostic_log(bounce, bounce_info) == 0
&& bounce_header_dsn(bounce, bounce_info) == 0
diff --git a/gnu/dist/postfix/src/bounce/bounce_notify_util.c b/gnu/dist/postfix/src/bounce/bounce_notify_util.c
index cc5e15f6bf0..f2dc04156d9 100644
--- a/gnu/dist/postfix/src/bounce/bounce_notify_util.c
+++ b/gnu/dist/postfix/src/bounce/bounce_notify_util.c
@@ -199,7 +199,7 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
*/
if ((bounce_info->log_handle = bounce_log_open(bounce_info->service,
bounce_info->queue_id,
- O_RDONLY, 0)) == 0
+ O_RDWR, 0)) == 0
&& errno != ENOENT)
msg_fatal("open %s %s: %m", bounce_info->service,
bounce_info->queue_id);
@@ -222,14 +222,16 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name,
* corrupted just send whatever we can (remember this is a best effort,
* it does not have to be perfect).
*/
- while ((rec_type = rec_get(bounce_info->orig_fp,
- bounce_info->buf, 0)) > 0) {
- if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
- if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
- bounce_info->arrival_time = 0;
- } else if (rec_type == REC_TYPE_MESG) {
- bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
- break;
+ if (bounce_info->orig_fp != 0) {
+ while ((rec_type = rec_get(bounce_info->orig_fp,
+ bounce_info->buf, 0)) > 0) {
+ if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
+ if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
+ bounce_info->arrival_time = 0;
+ } else if (rec_type == REC_TYPE_MESG) {
+ bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
+ break;
+ }
}
}
return (bounce_info);
@@ -339,7 +341,7 @@ int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
"####################################################################");
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce,
- "Your message could not be delivered for %.1f hours.",
+ "Your message could not be delivered for %.1f hours.",
var_delay_warn_time / 3600.0);
post_mail_fprintf(bounce,
"It will be retried until it is %.1f days old.",
@@ -478,7 +480,7 @@ int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
#endif
if (bounce_info->flush == 0)
post_mail_fprintf(bounce, "Will-Retry-Until: %s",
- mail_date(bounce_info->arrival_time + var_max_queue_time));
+ mail_date(bounce_info->arrival_time + var_max_queue_time));
return (vstream_ferror(bounce));
}
@@ -547,12 +549,16 @@ int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
bounce_length += VSTRING_LEN(bounce_info->buf) + 2;
status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
- }
+ } else
+ break;
}
/*
* Final MIME headers. These require -- at the end of the boundary
* string.
+ *
+ * XXX This should be a separate bounce_terminate() entry so we can be
+ * assured that the terminator will always be sent.
*/
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary);
diff --git a/gnu/dist/postfix/src/bounce/bounce_notify_verp.c b/gnu/dist/postfix/src/bounce/bounce_notify_verp.c
new file mode 100644
index 00000000000..f1880bae00c
--- /dev/null
+++ b/gnu/dist/postfix/src/bounce/bounce_notify_verp.c
@@ -0,0 +1,210 @@
+/*++
+/* NAME
+/* bounce_notify_verp 3
+/* SUMMARY
+/* send non-delivery report to sender, server side
+/* SYNOPSIS
+/* #include "bounce_service.h"
+/*
+/* int bounce_notify_verp(service, queue_name, queue_id, sender,
+/* verp_delims, flush)
+/* char *queue_name;
+/* char *queue_id;
+/* char *sender;
+/* char *verp_delims;
+/* int flush;
+/* DESCRIPTION
+/* This module implements the server side of the bounce_notify()
+/* (send bounce message) request. If flush is zero, the logfile
+/* is not removed, and a warning is sent instead of a bounce.
+/* The bounce recipient address is encoded in VERP format.
+/* This routine must be used for single bounces only.
+/*
+/* When a message bounces, a full copy is sent to the originator,
+/* and an optional copy of the diagnostics with message headers is
+/* sent to the postmaster. The result is non-zero when the operation
+/* should be tried again.
+/*
+/* When a bounce is sent, the sender address is the empty
+/* address.
+/* DIAGNOSTICS
+/* Fatal error: error opening existing file. Warnings: corrupt
+/* message file. A corrupt message is saved to the "corrupt"
+/* queue for further inspection.
+/* SEE ALSO
+/* bounce(3) basic bounce service 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 <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_queue.h>
+#include <post_mail.h>
+#include <mail_addr.h>
+#include <mail_error.h>
+#include <verp_sender.h>
+
+/* Application-specific. */
+
+#include "bounce_service.h"
+
+#define STR vstring_str
+
+/* bounce_notify_verp - send a bounce */
+
+int bounce_notify_verp(char *service, char *queue_name,
+ char *queue_id, char *recipient,
+ char *verp_delims, int flush)
+{
+ char *myname = "bounce_notify_verp";
+ BOUNCE_INFO *bounce_info;
+ int bounce_status = 0;
+ int postmaster_status;
+ VSTREAM *bounce;
+ int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
+ var_notify_classes);
+ char *postmaster;
+ VSTRING *verp_buf = vstring_alloc(100);
+
+ /*
+ * Sanity checks. We must be called only for undeliverable non-bounce
+ * messages.
+ */
+ if (*recipient == 0)
+ msg_panic("%s: attempt to bounce a single bounce", myname);
+ if (strcasecmp(recipient, mail_addr_double_bounce()) == 0)
+ msg_panic("%s: attempt to bounce a double bounce", myname);
+
+ /*
+ * Initialize. Open queue file, bounce log, etc.
+ */
+ bounce_info = bounce_mail_init(service, queue_name, queue_id, flush);
+
+#define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
+#define NULL_CLEANUP_FLAGS 0
+#define BOUNCE_HEADERS 1
+#define BOUNCE_ALL 0
+
+ /*
+ * A non-bounce message was returned. Send a single bounce, one per
+ * recipient.
+ */
+ while (bounce_log_read(bounce_info->log_handle) != 0) {
+
+ /*
+ * Notify the originator.
+ */
+ verp_sender(verp_buf, verp_delims, recipient,
+ bounce_info->log_handle->recipient);
+ if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
+ NULL_CLEANUP_FLAGS)) != 0) {
+
+ /*
+ * Send the bounce message header, some boilerplate text that
+ * pretends that we are a polite mail system, the text with
+ * reason for the bounce, and a copy of the original message.
+ */
+ if (bounce_header(bounce, bounce_info, STR(verp_buf)) == 0
+ && bounce_boilerplate(bounce, bounce_info) == 0
+ && bounce_recipient_log(bounce, bounce_info) == 0
+ && bounce_header_dsn(bounce, bounce_info) == 0
+ && bounce_recipient_dsn(bounce, bounce_info) == 0)
+ bounce_original(bounce, bounce_info, flush ?
+ BOUNCE_ALL : BOUNCE_HEADERS);
+ bounce_status = post_mail_fclose(bounce);
+ } else
+ bounce_status = 1;
+
+ /*
+ * Stop at the first sign of trouble, instead of making the problem
+ * worse.
+ */
+ if (bounce_status != 0)
+ break;
+
+ /*
+ * Mark this recipient as done.
+ */
+ bounce_log_delrcpt(bounce_info->log_handle);
+
+ /*
+ * Optionally, send a postmaster notice.
+ *
+ * This postmaster notice is not critical, so if it fails don't
+ * retransmit the bounce that we just generated, just log a warning.
+ */
+#define WANT_IF_BOUNCE (flush == 1 && (notify_mask & MAIL_ERROR_BOUNCE))
+#define WANT_IF_DELAY (flush == 0 && (notify_mask & MAIL_ERROR_DELAY))
+
+ if (WANT_IF_BOUNCE || WANT_IF_DELAY) {
+
+ /*
+ * Send the text with reason for the bounce, and the headers of
+ * the original message. Don't bother sending the boiler-plate
+ * text. This postmaster notice is not critical, so if it fails
+ * don't retransmit the bounce that we just generated, just log a
+ * warning.
+ */
+ postmaster = flush ? var_bounce_rcpt : var_delay_rcpt;
+ if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
+ postmaster,
+ NULL_CLEANUP_FLAGS)) != 0) {
+ if (bounce_header(bounce, bounce_info, postmaster) == 0
+ && bounce_recipient_log(bounce, bounce_info) == 0
+ && bounce_header_dsn(bounce, bounce_info) == 0
+ && bounce_recipient_dsn(bounce, bounce_info) == 0)
+ bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
+ postmaster_status = post_mail_fclose(bounce);
+ } else
+ postmaster_status = 1;
+
+ if (postmaster_status)
+ msg_warn("postmaster notice failed while bouncing to %s",
+ recipient);
+ }
+ }
+
+ /*
+ * Examine the completion status. Delete the bounce log file only when
+ * the bounce was posted successfully, and only if we are bouncing for
+ * real, not just warning.
+ */
+ if (flush != 0 && bounce_status == 0 && mail_queue_remove(service, queue_id)
+ && errno != ENOENT)
+ msg_fatal("remove %s %s: %m", service, queue_id);
+
+ /*
+ * Cleanup.
+ */
+ bounce_mail_free(bounce_info);
+ vstring_free(verp_buf);
+
+ return (bounce_status);
+}
diff --git a/gnu/dist/postfix/src/bounce/bounce_service.h b/gnu/dist/postfix/src/bounce/bounce_service.h
index 369a3da0d08..858609b7970 100644
--- a/gnu/dist/postfix/src/bounce/bounce_service.h
+++ b/gnu/dist/postfix/src/bounce/bounce_service.h
@@ -29,6 +29,11 @@ extern int bounce_append_service(char *, char *, char *, char *);
extern int bounce_notify_service(char *, char *, char *, char *, int);
/*
+ * bounce_notify_verp.c
+ */
+extern int bounce_notify_verp(char *, char *, char *, char *, char *, int);
+
+ /*
* bounce_cleanup.c
*/
extern VSTRING *bounce_cleanup_path;
@@ -51,7 +56,7 @@ typedef struct {
VSTREAM *orig_fp; /* open queue file */
long orig_offs; /* start of content */
time_t arrival_time; /* time of arrival */
- BOUNCE_LOG *log_handle; /* open logfile */
+ BOUNCE_LOG *log_handle; /* open logfile */
} BOUNCE_INFO;
extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, int);
diff --git a/gnu/dist/postfix/src/cleanup/Makefile.in b/gnu/dist/postfix/src/cleanup/Makefile.in
index dd13667a6fc..cec834caa00 100644
--- a/gnu/dist/postfix/src/cleanup/Makefile.in
+++ b/gnu/dist/postfix/src/cleanup/Makefile.in
@@ -72,6 +72,7 @@ cleanup.o: ../../include/mail_conf.h
cleanup.o: ../../include/cleanup_user.h
cleanup.o: ../../include/mail_proto.h
cleanup.o: ../../include/iostuff.h
+cleanup.o: ../../include/attr.h
cleanup.o: ../../include/mail_params.h
cleanup.o: ../../include/record.h
cleanup.o: ../../include/rec_type.h
@@ -93,6 +94,7 @@ cleanup_api.o: ../../include/mail_queue.h
cleanup_api.o: ../../include/vstream.h
cleanup_api.o: ../../include/mail_proto.h
cleanup_api.o: ../../include/iostuff.h
+cleanup_api.o: ../../include/attr.h
cleanup_api.o: ../../include/bounce.h
cleanup_api.o: ../../include/mail_params.h
cleanup_api.o: ../../include/mail_stream.h
@@ -120,6 +122,7 @@ cleanup_envelope.o: ../../include/mail_params.h
cleanup_envelope.o: ../../include/ext_prop.h
cleanup_envelope.o: ../../include/mail_addr.h
cleanup_envelope.o: ../../include/canon_addr.h
+cleanup_envelope.o: ../../include/verp_sender.h
cleanup_envelope.o: cleanup.h
cleanup_envelope.o: ../../include/argv.h
cleanup_envelope.o: ../../include/maps.h
@@ -152,6 +155,7 @@ cleanup_init.o: cleanup_init.c
cleanup_init.o: ../../include/sys_defs.h
cleanup_init.o: ../../include/msg.h
cleanup_init.o: ../../include/iostuff.h
+cleanup_init.o: ../../include/name_mask.h
cleanup_init.o: ../../include/mail_addr.h
cleanup_init.o: ../../include/mail_params.h
cleanup_init.o: ../../include/ext_prop.h
diff --git a/gnu/dist/postfix/src/cleanup/cleanup.c b/gnu/dist/postfix/src/cleanup/cleanup.c
index 70476aa2ec2..74c80bcc635 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup.c
+++ b/gnu/dist/postfix/src/cleanup/cleanup.c
@@ -35,7 +35,7 @@
/* header addresses (i.e. strip host or domain information below
/* all domains listed in the \fBmasquerade_domains\fR parameter,
/* except for user names listed in \fBmasquerade_exceptions\fR).
-/* Address masquerading does not affect envelope recipients.
+/* By default, address masquerading does not affect envelope recipients.
/* .IP \(bu
/* Optionally, expand envelope recipients according to information
/* found in the \fBvirtual\fR(5) lookup tables.
@@ -94,6 +94,10 @@
/* .IP \fBsender_canonical_maps\fR
/* Address mapping lookup table for envelope and header sender
/* addresses.
+/* .IP \fBmasquerade_classes\fR
+/* List of address classes subject to masquerading: zero or
+/* more of \fBenvelope_sender\fR, \fBenvelope_recipient\fR,
+/* \fBheader_sender\fR, \fBheader_recipient\fR.
/* .IP \fBmasquerade_domains\fR
/* List of domains that hide their subdomain structure.
/* .IP \fBmasquerade_exceptions\fR
@@ -107,6 +111,9 @@
/* Limit the number of envelope recipients that are remembered.
/* .IP \fBheader_size_limit\fR
/* Limit the amount of memory in bytes used to process a message header.
+/* .IP \fBin_flow_delay\fR
+/* Amount of time to pause before accepting a message, when the
+/* message arrival rate exceeds the message delivery rate.
/* .IP \fBextract_recipient_limit\fR
/* Limit the amount of recipients extracted from message headers.
/* SEE ALSO
@@ -184,8 +191,12 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
* can't read the client processing options we can pretty much forget
* about the whole operation.
*/
- mail_print(src, "%s", state->queue_id);
- if (mail_scan(src, "%d", &flags) != 1) {
+ attr_print(src, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, state->queue_id,
+ ATTR_TYPE_END);
+ if (attr_scan(src, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
+ ATTR_TYPE_END) != 1) {
state->errs |= CLEANUP_STAT_BAD;
flags = 0;
}
@@ -223,7 +234,12 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
/*
* Finish this message, and report the result status to the client.
*/
- mail_print(src, "%d", cleanup_close(state));
+ attr_print(src, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, cleanup_flush(state),
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, state->reason ?
+ state->reason : "",
+ ATTR_TYPE_END);
+ cleanup_free(state);
/*
* Cleanup.
@@ -271,5 +287,6 @@ int main(int argc, char **argv)
MAIL_SERVER_PRE_INIT, cleanup_pre_jail,
MAIL_SERVER_POST_INIT, cleanup_post_jail,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ MAIL_SERVER_IN_FLOW_DELAY,
0);
}
diff --git a/gnu/dist/postfix/src/cleanup/cleanup.h b/gnu/dist/postfix/src/cleanup/cleanup.h
index a1e56848c73..09d700ed18a 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup.h
+++ b/gnu/dist/postfix/src/cleanup/cleanup.h
@@ -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 */
+ char *reason; /* failure reason */
} CLEANUP_STATE;
/*
@@ -71,6 +72,15 @@ extern MAPS *cleanup_header_checks;
extern MAPS *cleanup_body_checks;
extern MAPS *cleanup_virtual_maps;
extern ARGV *cleanup_masq_domains;
+extern int cleanup_masq_flags;
+
+ /*
+ * Address masquerading fine control.
+ */
+#define CLEANUP_MASQ_FLAG_ENV_FROM (1<<0) /* envelope sender */
+#define CLEANUP_MASQ_FLAG_ENV_RCPT (1<<1) /* envelope recipient */
+#define CLEANUP_MASQ_FLAG_HDR_FROM (1<<2) /* header sender */
+#define CLEANUP_MASQ_FLAG_HDR_RCPT (1<<3) /* header recipient */
/*
* Restrictions on extension propagation.
@@ -94,7 +104,8 @@ 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 int cleanup_flush(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 **);
diff --git a/gnu/dist/postfix/src/cleanup/cleanup_api.c b/gnu/dist/postfix/src/cleanup/cleanup_api.c
index 0773d2cf81f..2da603b9109 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup_api.c
+++ b/gnu/dist/postfix/src/cleanup/cleanup_api.c
@@ -18,7 +18,10 @@
/* char *buf;
/* int len;
/*
-/* int cleanup_close(state)
+/* int cleanup_flush(state)
+/* CLEANUP_STATE *state;
+/*
+/* int cleanup_free(state)
/* CLEANUP_STATE *state;
/* DESCRIPTION
/* This module implements a callable interface to the cleanup service
@@ -27,26 +30,39 @@
/*
/* 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()
-/* routines. The name of the queue file is in the queue_id result
-/* structure member.
+/* given to the cleanup_control(), cleanup_record(), cleanup_flush()
+/* and cleanup_free() routines. The name of the queue file is in the
+/* queue_id result structure member.
/*
/* cleanup_control() processes per-message flags specified by the caller.
/* These flags control the handling of data errors, and must be set
/* before processing the first message record.
-/*
+/* .IP CLEANUP_FLAG_BOUNCE
+/* The cleanup server is responsible for returning undeliverable
+/* mail (too many hops, message too large) to the sender.
+/* .IP CLEANUP_FLAG_FILTER
+/* Enable header/body filtering. This should be enabled only with mail
+/* that enters Postfix, not with locally forwarded mail or with bounce
+/* messages.
+/* .IP CLEANUP_FLAG_EXTRACT
+/* Extract recipients from message headers when no recipients are
+/* provided in the message envelope records.
+/* .PP
/* CLEANUP_RECORD() is a macro that processes one message record,
/* that copies the result to the queue file, and that maintains a
/* little state machine. The last record in a valid message has type
/* REC_TYPE_END. In order to find out if a message is corrupted,
/* the caller is encouraged to test the CLEANUP_OUT_OK(state) macro.
/* The result is false when further message processing is futile.
-/* In that case, it is safe to call cleanup_close() immediately.
+/* In that case, it is safe to call cleanup_flush() immediately.
/*
-/* cleanup_close() finishes a queue file. In case of any errors,
+/* cleanup_flush() 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.
+/* problems. In some cases a human-readable text can be found in
+/* the state->reason member. In all other cases, 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
@@ -109,7 +125,7 @@ CLEANUP_STATE *cleanup_open(void)
* that the runtime error handler can clean up in case of problems.
*/
state->handle = mail_stream_file(MAIL_QUEUE_INCOMING,
- MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE);
+ MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE, 0);
state->dst = state->handle->stream;
cleanup_path = mystrdup(VSTREAM_PATH(state->dst));
state->queue_id = mystrdup(state->handle->id);
@@ -148,25 +164,28 @@ void cleanup_control(CLEANUP_STATE *state, int flags)
* definition.
*/
if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
- state->err_mask =
- (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE);
+ state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE;
} else {
- state->err_mask = CLEANUP_STAT_LETHAL;
+ state->err_mask = ~CLEANUP_STAT_MASK_EXTRACT_RCPT;
}
}
-/* cleanup_close - finish queue file */
+/* cleanup_flush - finish queue file */
-int cleanup_close(CLEANUP_STATE *state)
+int cleanup_flush(CLEANUP_STATE *state)
{
char *junk;
int status;
/*
- * See if there are any errors. For example, the message is incomplete,
- * or it needs to be bounced for lack of recipients. We want to turn on
- * the execute bits on a file only when we really want the queue manager
- * to process it.
+ * Ignore recipient extraction alarms if (a) we did (not need to) extract
+ * recipients, or (b) we did not examine all queue file records.
+ */
+ if (state->recip != 0 || CLEANUP_OUT_OK(state) == 0)
+ state->errs &= ~CLEANUP_STAT_MASK_EXTRACT_RCPT;
+
+ /*
+ * Raise these errors only if we examined all queue file records.
*/
if (CLEANUP_OUT_OK(state)) {
if (state->recip == 0)
@@ -180,10 +199,11 @@ int cleanup_close(CLEANUP_STATE *state)
* because we are about to tell the sender that it can throw away its
* copy of the message.
*/
- if ((state->errs & CLEANUP_STAT_LETHAL) == 0)
- state->errs |= mail_stream_finish(state->handle);
- else
+ if (state->errs == 0) {
+ state->errs = mail_stream_finish(state->handle, (VSTRING *) 0);
+ } else {
mail_stream_cleanup(state->handle);
+ }
state->handle = 0;
state->dst = 0;
@@ -200,26 +220,19 @@ int cleanup_close(CLEANUP_STATE *state)
*
* Do not log the arrival of a message that will be bounced by the client.
*
- * XXX CLEANUP_STAT_LETHAL masks errors that are not directly fatal (e.g.,
- * header buffer overflow is normally allowed to happen), but that can
- * indirectly become a problem (e.g., no recipients were extracted from
- * message headers because we could not process all the message headers).
- * However, cleanup_strerror() prioritizes errors so that it can report
- * the cause (e.g., header buffer overflow), which is more useful.
- *
* XXX When bouncing, should log sender because qmgr won't be able to.
*/
#define CAN_BOUNCE() \
- ((state->errs & (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)) == 0 \
+ ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
&& state->sender != 0 \
&& (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
- if (state->errs & CLEANUP_STAT_LETHAL) {
+ if (state->errs != 0) {
if (CAN_BOUNCE()) {
if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
state->recip ? state->recip : "unknown",
"cleanup", state->time,
- "Message processing aborted: %s",
+ "%s", state->reason ? state->reason :
cleanup_strerror(state->errs)) == 0
&& bounce_flush(BOUNCE_FLAG_CLEAN, MAIL_QUEUE_INCOMING,
state->queue_id, state->sender) == 0) {
@@ -249,8 +262,14 @@ int cleanup_close(CLEANUP_STATE *state)
* initializations at the beginning of cleanup_open().
*/
if (msg_verbose)
- msg_info("cleanup_close: status %d", state->errs);
- status = state->errs & CLEANUP_STAT_LETHAL;
- cleanup_state_free(state);
+ msg_info("cleanup_flush: status %d", state->errs);
+ status = state->errs;
return (status);
}
+
+/* cleanup_free - pay the last respects */
+
+void cleanup_free(CLEANUP_STATE *state)
+{
+ cleanup_state_free(state);
+}
diff --git a/gnu/dist/postfix/src/cleanup/cleanup_envelope.c b/gnu/dist/postfix/src/cleanup/cleanup_envelope.c
index 86758c146ad..f4fd30e75d0 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup_envelope.c
+++ b/gnu/dist/postfix/src/cleanup/cleanup_envelope.c
@@ -67,6 +67,7 @@
#include <ext_prop.h>
#include <mail_addr.h>
#include <canon_addr.h>
+#include <verp_sender.h>
/* Application-specific. */
@@ -148,7 +149,8 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *buf,
if (cleanup_comm_canon_maps)
cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- if (cleanup_masq_domains)
+ if (cleanup_masq_domains
+ && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_FROM))
cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
CLEANUP_OUT_BUF(state, type, clean_addr);
if (state->sender == 0)
@@ -170,6 +172,9 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *buf,
if (cleanup_comm_canon_maps)
cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
+ if (cleanup_masq_domains
+ && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT))
+ cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
cleanup_out_recipient(state, STR(clean_addr));
if (state->recip == 0)
state->recip = mystrdup(STR(clean_addr));
@@ -179,6 +184,18 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *buf,
state->errs |= CLEANUP_STAT_BAD;
return;
}
+ } else if (type == REC_TYPE_VERP) {
+ if (state->sender == 0 || *state->sender == 0) {
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
+ if (verp_delims_verify(buf) == 0) {
+ cleanup_out(state, type, buf, len);
+ } else {
+ msg_warn("%s: bad VERP delimiters: \"%s\"", state->queue_id, buf);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
} else {
cleanup_out(state, type, buf, len);
}
diff --git a/gnu/dist/postfix/src/cleanup/cleanup_extracted.c b/gnu/dist/postfix/src/cleanup/cleanup_extracted.c
index 243404be1c4..b9721bd88e3 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup_extracted.c
+++ b/gnu/dist/postfix/src/cleanup/cleanup_extracted.c
@@ -123,6 +123,9 @@ static void cleanup_extracted_process(CLEANUP_STATE *state, int type, char *buf,
if (cleanup_comm_canon_maps)
cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
+ if (cleanup_masq_domains
+ && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT))
+ cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
cleanup_out_recipient(state, STR(clean_addr));
if (state->recip == 0)
state->recip = mystrdup(STR(clean_addr));
@@ -150,8 +153,8 @@ static void cleanup_extracted_process(CLEANUP_STATE *state, int type, char *buf,
if (rcpt->argc >= var_extra_rcpt_limit) {
state->errs |= CLEANUP_STAT_ROVFL;
} else {
+ clean_addr = vstring_alloc(100);
if (*var_always_bcc && rcpt->argv[0]) {
- clean_addr = vstring_alloc(100);
cleanup_rewrite_internal(clean_addr, var_always_bcc);
if (cleanup_rcpt_canon_maps)
cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps,
@@ -160,13 +163,26 @@ static void cleanup_extracted_process(CLEANUP_STATE *state, int type, char *buf,
cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
argv_add(rcpt, STR(clean_addr), (char *) 0);
- vstring_free(clean_addr);
}
argv_terminate(rcpt);
- for (cpp = rcpt->argv; CLEANUP_OUT_OK(state) && *cpp; cpp++)
- cleanup_out_recipient(state, *cpp);
+
+ /*
+ * Recipients extracted from message headers already have
+ * undergone recipient address rewriting (see cleanup_message.c),
+ * but still may need address masquerading.
+ */
+ for (cpp = rcpt->argv; CLEANUP_OUT_OK(state) && *cpp; cpp++) {
+ if (cleanup_masq_domains
+ && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT)) {
+ vstring_strcpy(clean_addr, *cpp);
+ cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
+ cleanup_out_recipient(state, STR(clean_addr));
+ } else
+ cleanup_out_recipient(state, *cpp);
+ }
if (rcpt->argv[0])
state->recip = mystrdup(rcpt->argv[0]);
+ vstring_free(clean_addr);
}
}
diff --git a/gnu/dist/postfix/src/cleanup/cleanup_init.c b/gnu/dist/postfix/src/cleanup/cleanup_init.c
index 0cb5812c5bc..b3e7a0157e4 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup_init.c
+++ b/gnu/dist/postfix/src/cleanup/cleanup_init.c
@@ -69,6 +69,7 @@
#include <msg.h>
#include <iostuff.h>
+#include <name_mask.h>
/* Global library. */
@@ -106,6 +107,7 @@ char *var_prop_extension; /* propagate unmatched extension */
char *var_always_bcc; /* big brother */
int var_extra_rcpt_limit; /* recipient extract limit */
char *var_rcpt_witheld; /* recipients not disclosed */
+char *var_masq_classes; /* what to masquerade */
CONFIG_INT_TABLE cleanup_int_table[] = {
VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
@@ -133,6 +135,7 @@ CONFIG_STR_TABLE cleanup_str_table[] = {
VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0,
VAR_RCPT_WITHELD, DEF_RCPT_WITHELD, &var_rcpt_witheld, 1, 0,
+ VAR_MASQ_CLASSES, DEF_MASQ_CLASSES, &var_masq_classes, 0, 0,
0,
};
@@ -146,6 +149,7 @@ MAPS *cleanup_header_checks;
MAPS *cleanup_body_checks;
MAPS *cleanup_virtual_maps;
ARGV *cleanup_masq_domains;
+int cleanup_masq_flags;
/*
* Address extension propagation restrictions.
@@ -164,9 +168,17 @@ void cleanup_all(void)
void cleanup_pre_jail(char *unused_name, char **unused_argv)
{
+ static NAME_MASK masq_class_table[] = {
+ MASQ_CLASS_ENV_FROM, CLEANUP_MASQ_FLAG_ENV_FROM,
+ MASQ_CLASS_ENV_RCPT, CLEANUP_MASQ_FLAG_ENV_RCPT,
+ MASQ_CLASS_HDR_FROM, CLEANUP_MASQ_FLAG_HDR_FROM,
+ MASQ_CLASS_HDR_RCPT, CLEANUP_MASQ_FLAG_HDR_RCPT,
+ 0,
+ };
+
if (*var_canonical_maps)
cleanup_comm_canon_maps =
- maps_create(VAR_CANONICAL_MAPS, var_canonical_maps, DICT_FLAG_LOCK);
+ maps_create(VAR_CANONICAL_MAPS, var_canonical_maps, DICT_FLAG_LOCK);
if (*var_send_canon_maps)
cleanup_send_canon_maps =
maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps,
@@ -186,6 +198,9 @@ void cleanup_pre_jail(char *unused_name, char **unused_argv)
if (*var_body_checks)
cleanup_body_checks =
maps_create(VAR_BODY_CHECKS, var_body_checks, DICT_FLAG_LOCK);
+ if (*var_masq_classes)
+ cleanup_masq_flags = name_mask(VAR_MASQ_CLASSES, masq_class_table,
+ var_masq_classes);
}
/* cleanup_post_jail - initialize after entering the chroot jail */
diff --git a/gnu/dist/postfix/src/cleanup/cleanup_message.c b/gnu/dist/postfix/src/cleanup/cleanup_message.c
index 99644203fb0..fca690451bc 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup_message.c
+++ b/gnu/dist/postfix/src/cleanup/cleanup_message.c
@@ -178,7 +178,8 @@ static void cleanup_rewrite_sender(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
if (cleanup_comm_canon_maps)
cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
- if (cleanup_masq_domains)
+ if (cleanup_masq_domains
+ && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_FROM))
cleanup_masquerade_tree(*tpp, cleanup_masq_domains);
if (hdr_opts->type == HDR_FROM && state->from == 0)
state->from = cleanup_extract_internal(state->header_buf, *tpp);
@@ -228,6 +229,11 @@ static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
if (cleanup_comm_canon_maps)
cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
+
+ /*
+ * Extract envelope recipients after recipient address rewriting but
+ * before address masquerading.
+ */
if (state->recip == 0 && (hdr_opts->flags & HDR_OPT_EXTRACT) != 0) {
rcpt = (hdr_opts->flags & HDR_OPT_RR) ?
state->resent_recip : state->recipients;
@@ -236,7 +242,8 @@ static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
argv_add(rcpt, vstring_str(state->temp1), (char *) 0);
}
}
- if (cleanup_masq_domains)
+ if (cleanup_masq_domains
+ && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_RCPT))
cleanup_masquerade_tree(*tpp, cleanup_masq_domains);
}
vstring_sprintf(state->header_buf, "%s: ", hdr_opts->name);
@@ -247,6 +254,33 @@ static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts)
cleanup_fold_header(state);
}
+/* cleanup_check_reject - parse and match header/body REJECT line */
+
+static int cleanup_check_reject(CLEANUP_STATE *state, const char *value)
+{
+ const char *reason = value + strcspn(value, " \t");
+
+ /*
+ * See if they spelled REJECT right.
+ *
+ * XXX The reason should be set only if we have a more severe error than
+ * anything that was found before. This calls for a cleanup_set_error()
+ * routine that takes an error code and an optional text.
+ */
+ if (strncasecmp(value, "REJECT", reason - value) == 0) {
+ if (state->reason == 0) {
+ while (*reason && ISSPACE(*reason))
+ reason++;
+ state->reason = mystrdup(*reason ? reason :
+ cleanup_strerror(CLEANUP_STAT_CONT));
+ }
+ state->errs |= CLEANUP_STAT_CONT;
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
/* cleanup_header - process one complete header line */
static void cleanup_header(CLEANUP_STATE *state)
@@ -262,13 +296,17 @@ static void cleanup_header(CLEANUP_STATE *state)
const char *value;
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 (cleanup_check_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->errs |= CLEANUP_STAT_CONT;
+ state->recip ? state->recip : "unknown",
+ state->reason);
} else if (strcasecmp(value, "IGNORE") == 0) {
return;
+ } else if (strcasecmp(value, "WARN") == 0) {
+ msg_info("%s: warning: header %.200s; from=<%s> to=<%s>",
+ state->queue_id, header, state->sender,
+ state->recip ? state->recip : "unknown");
}
}
}
@@ -287,6 +325,29 @@ static void cleanup_header(CLEANUP_STATE *state)
* we should do with this header: delete, count, rewrite. Note that we
* should examine headers even when they will be deleted from the output,
* because the addresses in those headers might be needed elsewhere.
+ *
+ * XXX 2821: Return-path breakage.
+ *
+ * RFC 821 specifies: When the receiver-SMTP makes the "final delivery" of a
+ * message it inserts at the beginning of the mail data a return path
+ * line. The return path line preserves the information in the
+ * <reverse-path> from the MAIL command. Here, final delivery means the
+ * message leaves the SMTP world. Normally, this would mean it has been
+ * delivered to the destination user, but in some cases it may be further
+ * processed and transmitted by another mail system.
+ *
+ * And that is what Postfix implements. Delivery agents prepend
+ * Return-Path:. In order to avoid cluttering up the message with
+ * possibly inconsistent Return-Path: information (the sender can change
+ * as the result of mail forwarding or mailing list delivery), Postfix
+ * removes any existing Return-Path: headers.
+ *
+ * RFC 2821 Section 4.4 specifies: A message-originating SMTP system
+ * SHOULD NOT send a message that already contains a Return-path header.
+ * SMTP servers performing a relay function MUST NOT inspect the message
+ * data, and especially not to the extent needed to determine if
+ * Return-path headers are present. SMTP servers making final delivery
+ * MAY remove Return-path headers before adding their own.
*/
else {
state->headers_seen |= (1 << hdr_opts->type);
@@ -324,6 +385,11 @@ static void cleanup_missing_headers(CLEANUP_STATE *state)
/*
* Add a missing (Resent-)Message-Id: header. The message ID gives the
* time in GMT units, plus the local queue ID.
+ *
+ * XXX Message-Id is not a required message header (RFC 822 and RFC 2822).
+ *
+ * XXX It is the queue ID non-inode bits that prevent messages from getting
+ * the same Message-Id within the same second.
*/
if ((state->headers_seen & (1 << (state->resent[0] ?
HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) {
@@ -366,6 +432,29 @@ static void cleanup_missing_headers(CLEANUP_STATE *state)
}
/*
+ * XXX 2821: Appendix B: The return address in the MAIL command SHOULD,
+ * if possible, be derived from the system's identity for the submitting
+ * (local) user, and the "From:" header field otherwise. If there is a
+ * system identity available, it SHOULD also be copied to the Sender
+ * header field if it is different from the address in the From header
+ * field. (Any Sender field that was already there SHOULD be removed.)
+ * Similar wording appears in RFC 2822 section 3.6.2.
+ *
+ * Postfix presently does not insert a Sender: header if envelope and From:
+ * address differ. Older Postfix versions assumed that the envelope
+ * sender address specifies the system identity and inserted Sender:
+ * whenever envelope and From: differed. This was wrong with relayed
+ * mail, and was often not even desirable with original submissions.
+ *
+ * XXX 2822 Section 3.6.2, as well as RFC 822 Section 4.1: FROM headers can
+ * contain multiple addresses. If this is the case, then a Sender: header
+ * must be provided with a single address.
+ *
+ * Postfix does not count the number of addresses in a From: header
+ * (although doing so is trivial, once the address is parsed).
+ */
+
+ /*
* Add a missing destination header.
*/
#define VISIBLE_RCPT ((1 << HDR_TO) | (1 << HDR_RESENT_TO) \
@@ -491,13 +580,17 @@ static void cleanup_message_body(CLEANUP_STATE *state, int type, char *buf, int
const char *value;
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 (cleanup_check_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->errs |= CLEANUP_STAT_CONT;
+ state->recip ? state->recip : "unknown",
+ state->reason);
} else if (strcasecmp(value, "IGNORE") == 0) {
return;
+ } else if (strcasecmp(value, "WARN") == 0) {
+ msg_info("%s: warning: body %.200s; from=<%s> to=<%s>",
+ state->queue_id, buf, state->sender,
+ state->recip ? state->recip : "unknown");
}
}
}
diff --git a/gnu/dist/postfix/src/cleanup/cleanup_state.c b/gnu/dist/postfix/src/cleanup/cleanup_state.c
index c7e5b74b6c2..2206d7a42ec 100644
--- a/gnu/dist/postfix/src/cleanup/cleanup_state.c
+++ b/gnu/dist/postfix/src/cleanup/cleanup_state.c
@@ -84,6 +84,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
state->xtra_offset = -1;
state->end_seen = 0;
state->rcpt_count = 0;
+ state->reason = 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->reason)
+ myfree(state->reason);
myfree((char *) state);
}
diff --git a/gnu/dist/postfix/src/dns/Makefile.in b/gnu/dist/postfix/src/dns/Makefile.in
index e132be3db58..812c3fbc8e8 100644
--- a/gnu/dist/postfix/src/dns/Makefile.in
+++ b/gnu/dist/postfix/src/dns/Makefile.in
@@ -81,6 +81,7 @@ dns_rr.o: dns_rr.c
dns_rr.o: ../../include/sys_defs.h
dns_rr.o: ../../include/msg.h
dns_rr.o: ../../include/mymalloc.h
+dns_rr.o: ../../include/myrand.h
dns_rr.o: dns.h
dns_rr.o: ../../include/vstring.h
dns_rr.o: ../../include/vbuf.h
diff --git a/gnu/dist/postfix/src/dns/dns.h b/gnu/dist/postfix/src/dns/dns.h
index 490a5ff3923..e08583d6e8d 100644
--- a/gnu/dist/postfix/src/dns/dns.h
+++ b/gnu/dist/postfix/src/dns/dns.h
@@ -105,6 +105,7 @@ extern void dns_rr_free(DNS_RR *);
extern DNS_RR *dns_rr_copy(DNS_RR *);
extern DNS_RR *dns_rr_append(DNS_RR *, DNS_RR *);
extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (DNS_RR *, DNS_RR *));
+extern DNS_RR *dns_rr_shuffle(DNS_RR *);
/*
* dns_lookup.c
diff --git a/gnu/dist/postfix/src/dns/dns_rr.c b/gnu/dist/postfix/src/dns/dns_rr.c
index 39cbf192061..2dc8edd1b0c 100644
--- a/gnu/dist/postfix/src/dns/dns_rr.c
+++ b/gnu/dist/postfix/src/dns/dns_rr.c
@@ -26,6 +26,9 @@
/* DNS_RR *dns_rr_sort(list, compar)
/* DNS_RR *list
/* int (*compar)(DNS_RR *, DNS_RR *);
+/*
+/* DNS_RR *dns_rr_shuffle(list)
+/* DNS_RR *list;
/* DESCRIPTION
/* The routines in this module maintain memory for DNS resource record
/* information, and maintain lists of DNS resource records.
@@ -49,6 +52,8 @@
/* dns_rr_sort() sorts a list of resource records into ascending
/* order according to a user-specified criterion. The result is the
/* sorted list.
+/*
+/* dns_rr_shuffle() randomly permutes a list of resource records.
/* LICENSE
/* .ad
/* .fi
@@ -70,6 +75,7 @@
#include <msg.h>
#include <mymalloc.h>
+#include <myrand.h>
/* DNS library. */
@@ -193,3 +199,47 @@ DNS_RR *dns_rr_sort(DNS_RR *list, int (*compar) (DNS_RR *, DNS_RR *))
dns_rr_sort_user = saved_user;
return (list);
}
+
+/* dns_rr_shuffle - shuffle resource record list */
+
+DNS_RR *dns_rr_shuffle(DNS_RR *list)
+{
+ DNS_RR **rr_array;
+ DNS_RR *rr;
+ int len;
+ int i;
+ int r;
+
+ /*
+ * Build linear array with pointers to each list element.
+ */
+ for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
+ /* void */ ;
+ rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
+ for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
+ rr_array[len] = rr;
+
+ /*
+ * Shuffle resource records.
+ */
+ for (i = 0; i < len; i++) {
+ r = myrand() % len;
+ rr = rr_array[i];
+ rr_array[i] = rr_array[r];
+ rr_array[r] = rr;
+ }
+
+ /*
+ * Fix the links.
+ */
+ for (i = 0; i < len - 1; i++)
+ rr_array[i]->next = rr_array[i + 1];
+ rr_array[i]->next = 0;
+ list = rr_array[0];
+
+ /*
+ * Cleanup.
+ */
+ myfree((char *) rr_array);
+ return (list);
+}
diff --git a/gnu/dist/postfix/src/flush/Makefile.in b/gnu/dist/postfix/src/flush/Makefile.in
index 19b51de2809..835aea3787e 100644
--- a/gnu/dist/postfix/src/flush/Makefile.in
+++ b/gnu/dist/postfix/src/flush/Makefile.in
@@ -71,10 +71,14 @@ flush.o: ../../include/mail_params.h
flush.o: ../../include/mail_queue.h
flush.o: ../../include/mail_proto.h
flush.o: ../../include/iostuff.h
+flush.o: ../../include/attr.h
flush.o: ../../include/mail_flush.h
flush.o: ../../include/flush_clnt.h
flush.o: ../../include/mail_conf.h
flush.o: ../../include/mail_scan_dir.h
flush.o: ../../include/maps.h
flush.o: ../../include/domain_list.h
+flush.o: ../../include/match_list.h
+flush.o: ../../include/match_ops.h
+flush.o: ../../include/match_parent_style.h
flush.o: ../../include/mail_server.h
diff --git a/gnu/dist/postfix/src/flush/flush.c b/gnu/dist/postfix/src/flush/flush.c
index c8c76c0d09a..828681ad24a 100644
--- a/gnu/dist/postfix/src/flush/flush.c
+++ b/gnu/dist/postfix/src/flush/flush.c
@@ -38,7 +38,7 @@
/* if mail is undeliverable it will be added back to the logfile.
/* .sp
/* If the destination is not eligible for a fast flush logfile,
-/* this request triggers delivery of all queued mail.
+/* this request is rejected (see below for status codes).
/* .IP \fBTRIGGER_REQ_WAKEUP\fR
/* This wakeup request from the master is an alternative way to
/* request \fBFLUSH_REQ_REFRESH\fR.
@@ -66,6 +66,10 @@
/* request parameter value).
/* .IP \fBFLUSH_STAT_FAIL\fR
/* The request failed.
+/* .IP \fBFLUSH_STAT_DENY\fR
+/* The request was denied because the destination domain is not
+/* eligible for fast flush service, or because the fast flush
+/* service is disabled.
/* SECURITY
/* .ad
/* .fi
@@ -106,6 +110,10 @@
/* .IP \fBfast_flush_purge_time\fR
/* Remove an empty "fast flush" logfile that was not updated in
/* this amount of time (default time unit: days).
+/* .IP \fBparent_domain_matches_subdomains\fR
+/* List of Postfix features that use \fIdomain.name\fR patterns
+/* to match \fIsub.domain.name\fR (as opposed to
+/* requiring \fI.domain.name\fR patterns).
/* SEE ALSO
/* smtpd(8) Postfix SMTP server
/* qmgr(8) Postfix queue manager
@@ -156,6 +164,7 @@
#include <mail_scan_dir.h>
#include <maps.h>
#include <domain_list.h>
+#include <match_parent_style.h>
/* Single server skeleton. */
@@ -243,10 +252,10 @@ static int flush_add_service(const char *site, const char *queue_id)
msg_info("%s: site %s queue_id %s", myname, site, queue_id);
/*
- * If this site is not eligible for logging, just ignore the request.
+ * If this site is not eligible for logging, deny the request.
*/
if (flush_policy_ok(site) == 0)
- return (FLUSH_STAT_OK);
+ return (FLUSH_STAT_DENY);
/*
* Map site to path and update log.
@@ -319,10 +328,10 @@ static int flush_send_service(const char *site)
msg_info("%s: site %s", myname, site);
/*
- * If this site is not eligible for logging, deliver all queued mail.
+ * If this site is not eligible for logging, deny the request.
*/
if (flush_policy_ok(site) == 0)
- return (mail_flush_deferred());
+ return (FLUSH_STAT_DENY);
/*
* Map site name to path name and flush the log.
@@ -507,6 +516,51 @@ static int flush_refresh_service(int max_age)
return (FLUSH_STAT_OK);
}
+/* flush_request_receive - receive request */
+
+static int flush_request_receive(VSTREAM *client_stream, VSTRING *request)
+{
+ int count;
+
+ /*
+ * Kluge: choose the protocol depending on the request size.
+ */
+ if (read_wait(vstream_fileno(client_stream), var_ipc_timeout) < 0) {
+ msg_warn("timeout while waiting for data from %s",
+ VSTREAM_PATH(client_stream));
+ return (-1);
+ }
+ if ((count = peekfd(vstream_fileno(client_stream))) < 0) {
+ msg_warn("cannot examine read buffer of %s: %m",
+ VSTREAM_PATH(client_stream));
+ return (-1);
+ }
+
+ /*
+ * Short request: master trigger. Use the string+null protocol.
+ */
+ if (count <= 2) {
+ if (vstring_get_null(request, client_stream) == VSTREAM_EOF) {
+ msg_warn("end-of-input while reading request from %s: %m",
+ VSTREAM_PATH(client_stream));
+ return (-1);
+ }
+ }
+
+ /*
+ * Long request: real flush client. Use the attribute list protocol.
+ */
+ else {
+ if (attr_scan(client_stream,
+ ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, request,
+ ATTR_TYPE_END) != 1) {
+ return (-1);
+ }
+ }
+ return (0);
+}
+
/* flush_service - perform service for client */
static void flush_service(VSTREAM *client_stream, char *unused_service,
@@ -536,31 +590,46 @@ static void flush_service(VSTREAM *client_stream, char *unused_service,
* All connection-management stuff is handled by the common code in
* single_server.c.
*/
- if (mail_scan(client_stream, "%s", request) == 1) {
+ if (flush_request_receive(client_stream, request) == 0) {
if (STREQ(STR(request), FLUSH_REQ_ADD)) {
site = vstring_alloc(10);
queue_id = vstring_alloc(10);
- if (mail_command_read(client_stream, "%s %s", site, queue_id) == 2
+ if (attr_scan(client_stream, ATTR_FLAG_STRICT,
+ 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));
- mail_print(client_stream, "%d", status);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
+ ATTR_TYPE_END);
} else if (STREQ(STR(request), FLUSH_REQ_SEND)) {
site = vstring_alloc(10);
- if (mail_command_read(client_stream, "%s", site) == 1)
+ if (attr_scan(client_stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
+ ATTR_TYPE_END) == 1)
status = flush_send_service(lowercase(STR(site)));
- mail_print(client_stream, "%d", status);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
+ ATTR_TYPE_END);
} else if (STREQ(STR(request), FLUSH_REQ_REFRESH)
|| STREQ(STR(request), wakeup)) {
- mail_print(client_stream, "%d", FLUSH_STAT_OK);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, FLUSH_STAT_OK,
+ ATTR_TYPE_END);
vstream_fflush(client_stream);
(void) flush_refresh_service(var_fflush_refresh);
} else if (STREQ(STR(request), FLUSH_REQ_PURGE)) {
- mail_print(client_stream, "%d", FLUSH_STAT_OK);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, FLUSH_STAT_OK,
+ ATTR_TYPE_END);
vstream_fflush(client_stream);
(void) flush_refresh_service(0);
}
} else
- mail_print(client_stream, "%d", status);
+ attr_print(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
+ ATTR_TYPE_END);
vstring_free(request);
if (site)
vstring_free(site);
@@ -572,7 +641,8 @@ static void flush_service(VSTREAM *client_stream, char *unused_service,
static void pre_jail_init(char *unused_name, char **unused_argv)
{
- flush_domains = domain_list_init(var_fflush_domains);
+ flush_domains = domain_list_init(match_parent_style(VAR_FFLUSH_DOMAINS),
+ var_fflush_domains);
}
/* main - pass control to the single-threaded skeleton */
diff --git a/gnu/dist/postfix/src/global/abounce.c b/gnu/dist/postfix/src/global/abounce.c
index bfda481d51d..5cacccf5e8c 100644
--- a/gnu/dist/postfix/src/global/abounce.c
+++ b/gnu/dist/postfix/src/global/abounce.c
@@ -14,6 +14,15 @@
/* void (*callback)(int status, char *context);
/* char *context;
/*
+/* void abounce_flush_verp(flags, queue, id, sender, verp, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *sender;
+/* const char *verp;
+/* void (*callback)(int status, char *context);
+/* char *context;
+/*
/* void adefer_flush(flags, queue, id, sender, callback, context)
/* int flags;
/* const char *queue;
@@ -22,6 +31,15 @@
/* void (*callback)(int status, char *context);
/* char *context;
/*
+/* void adefer_flush_verp(flags, queue, id, sender, verp, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *sender;
+/* const char *verp;
+/* void (*callback)(int status, char *context);
+/* char *context;
+/*
/* void adefer_warn(flags, queue, id, sender, callback, context)
/* int flags;
/* const char *queue;
@@ -38,10 +56,16 @@
/* the specified sender, including the bounce log that was
/* built with bounce_append().
/*
+/* abounce_flush_verp() is like abounce_flush() but sends
+/* one VERP style notification per undeliverable recipient.
+/*
/* adefer_flush() bounces the specified message to
/* the specified sender, including the defer log that was
/* built with defer_append().
/*
+/* adefer_flush_verp() is like adefer_flush() but sends
+/* one VERP style notification per undeliverable recipient.
+/*
/* adefer_warn() sends a "mail is delayed" notification to
/* the specified sender, including the defer log that was
/* built with defer_append().
@@ -64,6 +88,8 @@
/* file has the same name as the original message file.
/* .IP sender
/* The sender envelope address.
+/* .IP verp
+/* VERP delimiter characters.
/* .IP callback
/* Name of a routine that receives the notification status as
/* documented for bounce_flush() or defer_flush().
@@ -145,7 +171,69 @@ static void abounce_event(int unused_event, char *context)
int status;
event_disable_readwrite(vstream_fileno(ap->fp));
- abounce_done(ap, mail_scan(ap->fp, "%d", &status) == 1 ? status : -1);
+ abounce_done(ap, attr_scan(ap->fp, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) == 1 ? status : -1);
+}
+
+/* abounce_request_verp - suspend pseudo thread until server reply event */
+
+static void abounce_request_verp(const char *class, const char *service,
+ int command, int flags,
+ const char *queue, const char *id,
+ const char *sender, const char *verp,
+ ABOUNCE_FN callback,
+ char *context)
+{
+ ABOUNCE *ap;
+
+ /*
+ * Save pseudo thread state. Connect to the server. Send the request and
+ * suspend the pseudo thread until the server replies (or dies).
+ */
+ ap = (ABOUNCE *) mymalloc(sizeof(*ap));
+ ap->command = command;
+ ap->flags = flags;
+ ap->id = mystrdup(id);
+ ap->callback = callback;
+ ap->context = context;
+ ap->fp = mail_connect_wait(class, service);
+
+ if (attr_print(ap->fp, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_NREQ, command,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp,
+ ATTR_TYPE_END) == 0
+ && vstream_fflush(ap->fp) == 0) {
+ event_enable_read(vstream_fileno(ap->fp), abounce_event, (char *) ap);
+ } else {
+ abounce_done(ap, -1);
+ }
+}
+
+/* abounce_flush_verp - asynchronous bounce flush */
+
+void abounce_flush_verp(int flags, const char *queue, const char *id,
+ const char *sender, const char *verp,
+ ABOUNCE_FN callback, char *context)
+{
+ abounce_request_verp(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE,
+ BOUNCE_CMD_VERP, flags, queue, id, sender, verp,
+ callback, context);
+}
+
+/* adefer_flush_verp - asynchronous defer flush */
+
+void adefer_flush_verp(int flags, const char *queue, const char *id,
+ const char *sender, const char *verp,
+ ABOUNCE_FN callback, char *context)
+{
+ abounce_request_verp(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
+ BOUNCE_CMD_VERP, flags, queue, id, sender, verp,
+ callback, context);
}
/* abounce_request - suspend pseudo thread until server reply event */
@@ -171,8 +259,13 @@ static void abounce_request(const char *class, const char *service,
ap->context = context;
ap->fp = mail_connect_wait(class, service);
- if (mail_print(ap->fp, "%d %d %s %s %s %s", command,
- flags, queue, id, sender, MAIL_EOF) == 0
+ if (attr_print(ap->fp, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_NREQ, command,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_END) == 0
&& vstream_fflush(ap->fp) == 0) {
event_enable_read(vstream_fileno(ap->fp), abounce_event, (char *) ap);
} else {
diff --git a/gnu/dist/postfix/src/global/abounce.h b/gnu/dist/postfix/src/global/abounce.h
index 8932248751c..69542383a5b 100644
--- a/gnu/dist/postfix/src/global/abounce.h
+++ b/gnu/dist/postfix/src/global/abounce.h
@@ -25,6 +25,9 @@ extern void abounce_flush(int, const char *, const char *, const char *, ABOUNCE
extern void adefer_flush(int, const char *, const char *, const char *, ABOUNCE_FN, char *);
extern void adefer_warn(int, const char *, const char *, const char *, ABOUNCE_FN, char *);
+extern void abounce_flush_verp(int, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
+extern void adefer_flush_verp(int, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
+
/* LICENSE
/* .ad
/* .fi
diff --git a/gnu/dist/postfix/src/global/bounce.c b/gnu/dist/postfix/src/global/bounce.c
index 75b07298d87..805d4c78710 100644
--- a/gnu/dist/postfix/src/global/bounce.c
+++ b/gnu/dist/postfix/src/global/bounce.c
@@ -142,10 +142,14 @@ int vbounce_append(int flags, const char *id, const char *recipient,
why = vstring_alloc(100);
delay = time((time_t *) 0) - entry;
vstring_vsprintf(why, fmt, ap);
- if (mail_command_write(MAIL_CLASS_PRIVATE, var_soft_bounce ?
- MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE,
- "%d %d %s %s %s", BOUNCE_CMD_APPEND,
- flags, id, recipient, vstring_str(why)) == 0) {
+ if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
+ MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE,
+ 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,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
+ ATTR_TYPE_END) == 0) {
msg_info("%s: to=<%s>, relay=%s, delay=%d, status=%s (%s)",
id, recipient, relay, delay, var_soft_bounce ? "deferred" :
"bounced", vstring_str(why));
@@ -172,9 +176,13 @@ int bounce_flush(int flags, const char *queue, const char *id,
*/
if (var_soft_bounce)
return (-1);
- if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE,
- "%d %d %s %s %s", BOUNCE_CMD_FLUSH,
- flags, queue, id, sender) == 0) {
+ if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_BOUNCE,
+ 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,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_END) == 0) {
return (0);
} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
msg_info("%s: status=deferred (bounce failed)", id);
diff --git a/gnu/dist/postfix/src/global/bounce.h b/gnu/dist/postfix/src/global/bounce.h
index 43ebc6040bb..018b91a4fe6 100644
--- a/gnu/dist/postfix/src/global/bounce.h
+++ b/gnu/dist/postfix/src/global/bounce.h
@@ -40,8 +40,8 @@ extern int vbounce_recip(int, const char *, const char *, const char *,
*/
#define BOUNCE_CMD_APPEND 0 /* append log */
#define BOUNCE_CMD_FLUSH 1 /* send log */
-#define BOUNCE_CMD_WARN 2 /* send warning bounce, don't delete
- * log */
+#define BOUNCE_CMD_WARN 2 /* send warning, don't delete log */
+#define BOUNCE_CMD_VERP 3 /* send log, verp style */
/*
* Flags.
diff --git a/gnu/dist/postfix/src/global/bounce_log.c b/gnu/dist/postfix/src/global/bounce_log.c
index 566ad48783b..f0f84153207 100644
--- a/gnu/dist/postfix/src/global/bounce_log.c
+++ b/gnu/dist/postfix/src/global/bounce_log.c
@@ -24,6 +24,9 @@
/* BOUNCE_LOG *bounce_log_read(bp)
/* BOUNCE_LOG *bp;
/*
+/* BOUNCE_LOG *bounce_log_delrcpt(bp)
+/* BOUNCE_LOG *bp;
+/*
/* void bounce_log_rewind(bp)
/* BOUNCE_LOG *bp;
/*
@@ -31,8 +34,7 @@
/* BOUNCE_LOG *bp;
/* DESCRIPTION
/* This module implements a bounce/defer logfile API. Information
-/* is sanitized for control and non-ASCII characters. Currently,
-/* only the reading end is implemented.
+/* is sanitized for control and non-ASCII characters.
/*
/* bounce_log_open() opens the named bounce or defer logfile
/* and returns a handle that must be used for further access.
@@ -47,6 +49,9 @@
/* bounce_log_read() returns a null pointer when no recipient was read,
/* otherwise it returns its argument.
/*
+/* bounce_log_delrcpt() marks the last accessed recipient record as
+/* "deleted". This requires that the logfile is opened for update.
+/*
/* bounce_log_rewind() is a helper that seeks to the first recipient
/* in an open bounce or defer logfile (skipping over recipients that
/* are marked as done). The result is 0 in case of success, -1 in case
@@ -92,6 +97,7 @@
#include <sys_defs.h>
#include <string.h>
#include <ctype.h>
+#include <unistd.h>
/* Utility library. */
@@ -133,6 +139,7 @@ BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
bp->fp = fp;
bp->buf = vstring_alloc(100);
bp->status = STREQ(queue_name, MAIL_QUEUE_DEFER) ? "4.0.0" : "5.0.0";
+ bp->offset = 0;
return (bp);
}
}
@@ -145,7 +152,8 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
char *text;
char *cp;
- while (vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF) {
+ while ((bp->offset = vstream_ftell(bp->fp)),
+ (vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF)) {
if (STR(bp->buf)[0] == 0)
continue;
@@ -156,6 +164,12 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
cp = printable(STR(bp->buf), '?');
/*
+ * Skip over deleted recipients.
+ */
+ if (*cp == BOUNCE_LOG_STAT_DELETED)
+ continue;
+
+ /*
* Find the recipient address.
*/
if (*cp != '<') {
@@ -185,6 +199,21 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
return (0);
}
+/* bounce_log_delrcpt - mark recipient record as deleted */
+
+BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *bp)
+{
+ long current_offset;
+
+ current_offset = vstream_ftell(bp->fp);
+ if (vstream_fseek(bp->fp, bp->offset, SEEK_SET) < 0)
+ msg_fatal("bounce logfile %s seek error: %m", VSTREAM_PATH(bp->fp));
+ VSTREAM_PUTC(BOUNCE_LOG_STAT_DELETED, bp->fp);
+ if (vstream_fseek(bp->fp, current_offset, SEEK_SET) < 0)
+ msg_fatal("bounce logfile %s seek error: %m", VSTREAM_PATH(bp->fp));
+ return (bp);
+}
+
/* bounce_log_close - close bounce reader stream */
int bounce_log_close(BOUNCE_LOG *bp)
diff --git a/gnu/dist/postfix/src/global/bounce_log.h b/gnu/dist/postfix/src/global/bounce_log.h
index 7b949e95175..0f91c6f8d68 100644
--- a/gnu/dist/postfix/src/global/bounce_log.h
+++ b/gnu/dist/postfix/src/global/bounce_log.h
@@ -28,14 +28,18 @@ typedef struct {
const char *recipient; /* final recipient */
const char *status; /* recipient status */
const char *text; /* why undeliverable */
+ long offset; /* start of current record */
} BOUNCE_LOG;
extern BOUNCE_LOG *bounce_log_open(const char *, const char *, int, int);
extern BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *);
+extern BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *);
extern int bounce_log_close(BOUNCE_LOG *);
#define bounce_log_rewind(bp) vstream_fseek((bp)->fp, 0L, SEEK_SET)
+#define BOUNCE_LOG_STAT_DELETED 'D' /* deleted record */
+
/* LICENSE
/* .ad
/* .fi
diff --git a/gnu/dist/postfix/src/global/cleanup_user.h b/gnu/dist/postfix/src/global/cleanup_user.h
index 8dff9102277..c60565d6229 100644
--- a/gnu/dist/postfix/src/global/cleanup_user.h
+++ b/gnu/dist/postfix/src/global/cleanup_user.h
@@ -34,7 +34,23 @@
#define CLEANUP_STAT_HOVFL (1<<7) /* Header overflow */
#define CLEANUP_STAT_ROVFL (1<<8) /* Recipient overflow */
-#define CLEANUP_STAT_LETHAL ~(CLEANUP_STAT_HOVFL|CLEANUP_STAT_ROVFL)
+ /*
+ * These are set when we can't bounce even if we were asked to.
+ */
+#define CLEANUP_STAT_MASK_CANT_BOUNCE \
+ (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)
+
+ /*
+ * These are set when we can't examine every record of a message.
+ */
+#define CLEANUP_STAT_MASK_INCOMPLETE \
+ (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE)
+
+ /*
+ * These are relevant for extracting recipients from headers.
+ */
+#define CLEANUP_STAT_MASK_EXTRACT_RCPT \
+ (CLEANUP_STAT_HOVFL | CLEANUP_STAT_ROVFL | CLEANUP_STAT_RCPT)
extern const char *cleanup_strerror(unsigned);
diff --git a/gnu/dist/postfix/src/global/debug_peer.c b/gnu/dist/postfix/src/global/debug_peer.c
index 29170bad58f..c7096c97321 100644
--- a/gnu/dist/postfix/src/global/debug_peer.c
+++ b/gnu/dist/postfix/src/global/debug_peer.c
@@ -69,6 +69,7 @@
#include <mail_params.h>
#include <namadr_list.h>
#include <debug_peer.h>
+#include <match_parent_style.h>
/* Application-specific. */
@@ -97,7 +98,9 @@ void debug_peer_init(void)
* Finally.
*/
if (*var_debug_peer_list)
- debug_peer_list = namadr_list_init(var_debug_peer_list);
+ debug_peer_list =
+ namadr_list_init(match_parent_style(VAR_DEBUG_PEER_LIST),
+ var_debug_peer_list);
}
/* debug_peer_check - see if this peer needs verbose logging */
diff --git a/gnu/dist/postfix/src/global/defer.c b/gnu/dist/postfix/src/global/defer.c
index 59cde38fa41..40130cfe37d 100644
--- a/gnu/dist/postfix/src/global/defer.c
+++ b/gnu/dist/postfix/src/global/defer.c
@@ -145,9 +145,13 @@ int vdefer_append(int flags, const char *id, const char *recipient,
const char *rcpt_domain;
vstring_vsprintf(why, fmt, ap);
- if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
- "%d %d %s %s %s", BOUNCE_CMD_APPEND,
- flags, id, recipient, vstring_str(why)) != 0)
+ if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
+ 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,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
+ ATTR_TYPE_END) != 0)
msg_warn("%s: defer service failure", id);
msg_info("%s: to=<%s>, relay=%s, delay=%d, status=deferred (%s)",
id, recipient, relay, delay, vstring_str(why));
@@ -158,8 +162,14 @@ int vdefer_append(int flags, const char *id, const char *recipient,
* bounce/defer daemon? Well, doing it here is more robust.
*/
if ((rcpt_domain = strrchr(recipient, '@')) != 0 && *++rcpt_domain != 0)
- if (flush_add(rcpt_domain, id) != FLUSH_STAT_OK)
+ switch (flush_add(rcpt_domain, id)) {
+ case FLUSH_STAT_OK:
+ case FLUSH_STAT_DENY:
+ break;
+ default:
msg_warn("unable to talk to fast flush service");
+ break;
+ }
return (-1);
}
@@ -169,9 +179,13 @@ int vdefer_append(int flags, const char *id, const char *recipient,
int defer_flush(int flags, const char *queue, const char *id,
const char *sender)
{
- if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
- "%d %d %s %s %s", BOUNCE_CMD_FLUSH,
- flags, queue, id, sender) == 0) {
+ if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
+ 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,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_END) == 0) {
return (0);
} else {
return (-1);
@@ -184,9 +198,13 @@ int defer_flush(int flags, const char *queue, const char *id,
int defer_warn(int flags, const char *queue, const char *id,
const char *sender)
{
- if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
- "%d %d %s %s %s", BOUNCE_CMD_WARN,
- flags, queue, id, sender) == 0) {
+ if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER,
+ 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,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_END) == 0) {
return (0);
} else {
return (-1);
diff --git a/gnu/dist/postfix/src/global/deliver_pass.c b/gnu/dist/postfix/src/global/deliver_pass.c
index 2deec8c3c2a..f428d551ebf 100644
--- a/gnu/dist/postfix/src/global/deliver_pass.c
+++ b/gnu/dist/postfix/src/global/deliver_pass.c
@@ -77,7 +77,9 @@ static int deliver_pass_initial_reply(VSTREAM *stream)
{
int stat;
- if (mail_scan(stream, "%d", &stat) != 1) {
+ if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+ ATTR_TYPE_END) != 1) {
msg_warn("%s: malformed response", VSTREAM_PATH(stream));
stat = -1;
}
@@ -91,14 +93,21 @@ static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
{
int stat;
- mail_print(stream, "%d %s %s %ld %ld %s %s %s %s %ld %ld %s %s",
- request->flags,
- request->queue_name, request->queue_id,
- request->data_offset, request->data_size,
- nexthop, request->sender,
- request->errors_to, request->return_receipt,
- request->arrival_time,
- offs, addr, "0");
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, request->flags,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUE, request->queue_name,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, request->queue_id,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, request->data_offset,
+ ATTR_TYPE_LONG, MAIL_ATTR_SIZE, request->data_size,
+ ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, request->sender,
+ ATTR_TYPE_STR, MAIL_ATTR_ERRTO, request->errors_to,
+ ATTR_TYPE_STR, MAIL_ATTR_RRCPT, request->return_receipt,
+ ATTR_TYPE_LONG, MAIL_ATTR_TIME, request->arrival_time,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offs,
+ ATTR_TYPE_STR, MAIL_ATTR_RECIP, addr,
+ ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
+ ATTR_TYPE_END);
if (vstream_fflush(stream)) {
msg_warn("%s: bad write: %m", VSTREAM_PATH(stream));
@@ -115,7 +124,10 @@ static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *reason)
{
int stat;
- if (mail_scan(stream, "%s %d", reason, &stat) != 2) {
+ if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+ ATTR_TYPE_END) != 2) {
msg_warn("%s: malformed response", VSTREAM_PATH(stream));
stat = -1;
}
diff --git a/gnu/dist/postfix/src/global/deliver_request.c b/gnu/dist/postfix/src/global/deliver_request.c
index 63c20c4fee4..35c83e78061 100644
--- a/gnu/dist/postfix/src/global/deliver_request.c
+++ b/gnu/dist/postfix/src/global/deliver_request.c
@@ -67,7 +67,7 @@
/* Warnings: bad data sent by the client. Fatal errors: out of
/* memory, queue file open errors.
/* SEE ALSO
-/* mail_scan(3) low-level intra-mail input routines
+/* attr_scan(3) low-level intra-mail input routines
/* LICENSE
/* .ad
/* .fi
@@ -85,6 +85,7 @@
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
/* Utility library. */
@@ -117,7 +118,9 @@ static int deliver_request_initial(VSTREAM *stream)
*/
if (msg_verbose)
msg_info("deliver_request_initial: send initial status");
- mail_print(stream, "%d", 0);
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, 0,
+ ATTR_TYPE_END);
if ((err = vstream_fflush(stream)) != 0)
if (msg_verbose)
msg_warn("send initial status: %m");
@@ -137,7 +140,10 @@ static int deliver_request_final(VSTREAM *stream, char *reason, int status)
reason = "";
if (msg_verbose)
msg_info("deliver_request_final: send: \"%s\" %d", reason, status);
- mail_print(stream, "%s %d", reason, status);
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
+ ATTR_TYPE_END);
if ((err = vstream_fflush(stream)) != 0)
if (msg_verbose)
msg_warn("send final status: %m");
@@ -186,11 +192,18 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
* Extract the queue file name, data offset, and sender address. Abort
* the conversation when they send bad information.
*/
- if (mail_scan(stream, "%d %s %s %ld %ld %s %s %s %s %ld",
- &request->flags,
- queue_name, queue_id, &request->data_offset,
- &request->data_size, nexthop, address,
- errors_to, return_receipt, &request->arrival_time) != 10)
+ if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &request->flags,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &request->data_offset,
+ ATTR_TYPE_LONG, MAIL_ATTR_SIZE, &request->data_size,
+ ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, address,
+ ATTR_TYPE_STR, MAIL_ATTR_ERRTO, errors_to,
+ ATTR_TYPE_STR, MAIL_ATTR_RRCPT, return_receipt,
+ ATTR_TYPE_LONG, MAIL_ATTR_TIME, &request->arrival_time,
+ ATTR_TYPE_END) != 10)
return (-1);
if (mail_open_ok(vstring_str(queue_name),
vstring_str(queue_id), &st, &path) == 0)
@@ -204,14 +217,19 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
request->return_receipt = mystrdup(vstring_str(return_receipt));
/*
- * Extract the recipient offset and address list.
+ * Extract the recipient offset and address list. Skip over any
+ * attributes from the sender that we do not understand.
*/
for (;;) {
- if (mail_scan(stream, "%ld", &offset) != 1)
+ if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &offset,
+ ATTR_TYPE_END) != 1)
return (-1);
if (offset == 0)
break;
- if (mail_scan(stream, "%s", address) != 1)
+ if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_RECIP, address,
+ ATTR_TYPE_END) != 1)
return (-1);
recipient_list_add(&request->rcpt_list, offset, vstring_str(address));
}
@@ -230,8 +248,12 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
request->fp =
mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0);
- if (request->fp == 0)
- msg_fatal("open %s %s: %m", request->queue_name, request->queue_id);
+ if (request->fp == 0) {
+ if (errno != ENOENT)
+ msg_fatal("open %s %s: %m", request->queue_name, request->queue_id);
+ msg_warn("open %s %s: %m", request->queue_name, request->queue_id);
+ return (-1);
+ }
if (msg_verbose)
msg_info("%s: file %s", myname, VSTREAM_PATH(request->fp));
if (myflock(vstream_fileno(request->fp), INTERNAL_LOCK, DELIVER_LOCK_MODE) < 0)
@@ -309,9 +331,11 @@ DELIVER_REQUEST *deliver_request_read(VSTREAM *stream)
/*
* Allocate and read the queue manager's delivery request.
*/
+#define XXX_DEFER_STATUS -1
+
request = deliver_request_alloc();
if (deliver_request_get(stream, request) < 0) {
- deliver_request_free(request);
+ deliver_request_done(stream, request, XXX_DEFER_STATUS);
request = 0;
}
return (request);
diff --git a/gnu/dist/postfix/src/global/deliver_request.h b/gnu/dist/postfix/src/global/deliver_request.h
index 2406085ff1e..45c2055234c 100644
--- a/gnu/dist/postfix/src/global/deliver_request.h
+++ b/gnu/dist/postfix/src/global/deliver_request.h
@@ -45,6 +45,17 @@ typedef struct DELIVER_REQUEST {
#define DEL_REQ_FLAG_SUCCESS (1<<0) /* delete successful recipients */
#define DEL_REQ_FLAG_BOUNCE (1<<1) /* unimplemented */
+ /*
+ * Delivery status. Note that there are only FINAL and DEFER. This is
+ * because delivery status information can be lost when a delivery agent or
+ * queue manager process terminates prematurely. The only distinctions we
+ * can rely on are "final delivery completed" and "everything else". In the
+ * absence of a definitive statement the queue manager will always have to
+ * be prepared for all possibilities.
+ */
+#define DEL_STAT_FINAL 0 /* delivered or bounced */
+#define DEL_STAT_DEFER (-1) /* not delivered or bounced */
+
typedef struct VSTREAM _deliver_vstream_;
extern DELIVER_REQUEST *deliver_request_read(_deliver_vstream_ *);
extern int deliver_request_done(_deliver_vstream_ *, DELIVER_REQUEST *, int);
diff --git a/gnu/dist/postfix/src/global/domain_list.c b/gnu/dist/postfix/src/global/domain_list.c
index 1322642d0f9..d4ea536b29f 100644
--- a/gnu/dist/postfix/src/global/domain_list.c
+++ b/gnu/dist/postfix/src/global/domain_list.c
@@ -6,7 +6,8 @@
/* SYNOPSIS
/* #include <domain_list.h>
/*
-/* DOMAIN_LIST *domain_list_init(pattern_list)
+/* DOMAIN_LIST *domain_list_init(flags, pattern_list)
+/* int flags;
/* const char *pattern_list;
/*
/* int domain_list_match(list, name)
@@ -16,6 +17,8 @@
/* void domain_list_free(list)
/* DOMAIN_LIST *list;
/* DESCRIPTION
+/* This is a convenience wrapper around the match_list module.
+/*
/* This module implements tests for list membership of a host or
/* domain name.
/*
@@ -30,9 +33,17 @@
/* insensitive. In order to reverse the result, precede a non-file
/* name pattern with an exclamation point (!).
/*
-/* domain_list_init() performs initializations. The argument is a
-/* list of domain patterns, or the name of a file containing domain
-/* patterns.
+/* domain_list_init() performs initializations. The first argument
+/* is the bit-wise OR of zero or more of the following:
+/* .RS
+/* .IP MATCH_FLAG_PARENT
+/* The hostname pattern foo.com matches itself and any name below
+/* the domain foo.com. If this flag is cleared, foo.com matches itself
+/* only, and .foo.com matches any name below the domain foo.com.
+/* .RE
+/* Specify MATCH_FLAG_NONE to request none of the above.
+/* The second argument is a list of domain patterns, or the name of
+/* a file containing domain patterns.
/*
/* domain_list_match() matches the specified host or domain name
/* against the specified pattern list.
@@ -62,33 +73,11 @@
/* Utility library. */
#include <match_list.h>
-#include <match_ops.h>
/* Global library. */
#include "domain_list.h"
-/* domain_list_init - initialize domain list */
-
-DOMAIN_LIST *domain_list_init(const char *patterns)
-{
- return (match_list_init(patterns, 1, match_hostname));
-}
-
-/* domain_list_match - match host against domain list */
-
-int domain_list_match(DOMAIN_LIST *list, const char *name)
-{
- return (match_list_match(list, name));
-}
-
-/* domain_list_free - release storage */
-
-void domain_list_free(DOMAIN_LIST *list)
-{
- match_list_free(list);
-}
-
#ifdef TEST
#include <msg.h>
@@ -120,7 +109,7 @@ main(int argc, char **argv)
}
if (argc != optind + 2)
usage(argv[0]);
- list = domain_list_init(argv[optind]);
+ list = domain_list_init(MATCH_FLAG_PARENT, argv[optind]);
host = argv[optind + 1];
vstream_printf("%s: %s\n", host, domain_list_match(list, host) ?
"YES" : "NO");
diff --git a/gnu/dist/postfix/src/global/domain_list.h b/gnu/dist/postfix/src/global/domain_list.h
index b36f516b444..23f60c78950 100644
--- a/gnu/dist/postfix/src/global/domain_list.h
+++ b/gnu/dist/postfix/src/global/domain_list.h
@@ -12,13 +12,19 @@
/* .nf
/*
+ * Utility library.
+ */
+#include <match_list.h>
+#include <match_ops.h>
+
+ /*
* External interface.
*/
-typedef struct MATCH_LIST DOMAIN_LIST;
+#define DOMAIN_LIST MATCH_LIST
-extern DOMAIN_LIST *domain_list_init(const char *);
-extern int domain_list_match(DOMAIN_LIST *, const char *);
-extern void domain_list_free(DOMAIN_LIST *);
+#define domain_list_init(f, p) match_list_init((f), (p), 1, match_hostname)
+#define domain_list_match match_list_match
+#define domain_list_free match_list_free
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/global/file_id.c b/gnu/dist/postfix/src/global/file_id.c
index ad4ce57987f..e130087db24 100644
--- a/gnu/dist/postfix/src/global/file_id.c
+++ b/gnu/dist/postfix/src/global/file_id.c
@@ -65,7 +65,7 @@ const char *get_file_id(int fd)
result = vstring_alloc(1);
if (fstat(fd, &st) < 0)
msg_fatal("fstat: %m");
- vstring_sprintf(result, "%X", (int) st.st_ino);
+ vstring_sprintf(result, "%lX", (long) st.st_ino);
return (vstring_str(result));
}
diff --git a/gnu/dist/postfix/src/global/flush_clnt.c b/gnu/dist/postfix/src/global/flush_clnt.c
index 6de4b426de8..8f35b6c5bbf 100644
--- a/gnu/dist/postfix/src/global/flush_clnt.c
+++ b/gnu/dist/postfix/src/global/flush_clnt.c
@@ -46,6 +46,9 @@
/* .IP MAIL_FLUSH_BAD
/* The "fast flush" server rejected the request (invalid request
/* parameter).
+/* .IP MAIL_FLUSH_DENY
+/* The specified domain is not eligible for "fast flush" service,
+/* or the "fast flush" service is disabled.
/* SEE ALSO
/* flush(8) Postfix fast flush cache manager
/* LICENSE
@@ -95,10 +98,11 @@ int flush_purge(void)
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = FLUSH_STAT_OK;
+ status = FLUSH_STAT_DENY;
else
- status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
- "%s", FLUSH_REQ_PURGE);
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_PURGE,
+ ATTR_TYPE_END);
if (msg_verbose)
msg_info("%s: status %d", myname, status);
@@ -120,10 +124,11 @@ int flush_refresh(void)
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = FLUSH_STAT_OK;
+ status = FLUSH_STAT_DENY;
else
- status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
- "%s", FLUSH_REQ_REFRESH);
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_REFRESH,
+ ATTR_TYPE_END);
if (msg_verbose)
msg_info("%s: status %d", myname, status);
@@ -145,10 +150,12 @@ int flush_send(const char *site)
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = mail_flush_deferred();
+ status = FLUSH_STAT_DENY;
else
- status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
- "%s %s", FLUSH_REQ_SEND, site);
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_SEND,
+ ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
+ ATTR_TYPE_END);
if (msg_verbose)
msg_info("%s: site %s status %d", myname, site, status);
@@ -170,10 +177,13 @@ int flush_add(const char *site, const char *queue_id)
* Don't bother the server if the service is turned off.
*/
if (*var_fflush_domains == 0)
- status = FLUSH_STAT_OK;
+ status = FLUSH_STAT_DENY;
else
- status = mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
- "%s %s %s", FLUSH_REQ_ADD, site, queue_id);
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_ADD,
+ ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
+ ATTR_TYPE_END);
if (msg_verbose)
msg_info("%s: site %s id %s status %d", myname, site, queue_id,
diff --git a/gnu/dist/postfix/src/global/flush_clnt.h b/gnu/dist/postfix/src/global/flush_clnt.h
index 47a88558a53..ee82a984cb4 100644
--- a/gnu/dist/postfix/src/global/flush_clnt.h
+++ b/gnu/dist/postfix/src/global/flush_clnt.h
@@ -33,6 +33,7 @@ extern int flush_purge(void);
#define FLUSH_STAT_FAIL -1 /* request failed */
#define FLUSH_STAT_OK 0 /* request executed */
#define FLUSH_STAT_BAD 3 /* invalid parameter */
+#define FLUSH_STAT_DENY 4 /* request denied */
/* LICENSE
diff --git a/gnu/dist/postfix/src/global/is_header.c b/gnu/dist/postfix/src/global/is_header.c
index 0cd3e93c2d5..5ed88fadd6e 100644
--- a/gnu/dist/postfix/src/global/is_header.c
+++ b/gnu/dist/postfix/src/global/is_header.c
@@ -41,6 +41,13 @@ int is_header(const char *str)
const char *cp;
int c;
+ /*
+ * XXX RFC 2822 Section 4.5.2, Obsolete header fields: whitespace may
+ * appear between header label and ":" (see: RFC 822, Section 3.4.2.).
+ *
+ * The code below allows no such whitespace. This has never been a problem,
+ * and therefore we're not inclined to add code for it.
+ */
for (cp = str; (c = *(unsigned char *) cp) != 0; cp++) {
if (c == ':')
return (cp > str);
diff --git a/gnu/dist/postfix/src/global/mail_command_client.c b/gnu/dist/postfix/src/global/mail_command_client.c
new file mode 100644
index 00000000000..fc1c56c9efe
--- /dev/null
+++ b/gnu/dist/postfix/src/global/mail_command_client.c
@@ -0,0 +1,82 @@
+/*++
+/* NAME
+/* mail_command_client 3
+/* SUMMARY
+/* single-command client
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* int mail_command_client(class, name, type, attr, ...)
+/* const char *class;
+/* const char *name;
+/* int type;
+/* const char *attr;
+/* 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 "type, attr, ..."
+/* Attribute information as defined in attr_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
+/* attr_print(3), send attributes over byte stream
+/* mail_command_server(3), server interface
+/* mail_proto(3h), 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_client - single-command transaction with completion status */
+
+int mail_command_client(const char *class, const char *name,...)
+{
+ 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, name);
+ status = attr_vprint(stream, ATTR_FLAG_NONE, ap);
+ va_end(ap);
+ if (status != 0
+ || attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status, 0) != 1)
+ status = -1;
+ (void) vstream_fclose(stream);
+ return (status);
+}
diff --git a/gnu/dist/postfix/src/global/mail_command_server.c b/gnu/dist/postfix/src/global/mail_command_server.c
new file mode 100644
index 00000000000..9565b55fb4c
--- /dev/null
+++ b/gnu/dist/postfix/src/global/mail_command_server.c
@@ -0,0 +1,67 @@
+/*++
+/* NAME
+/* mail_command_server 3
+/* SUMMARY
+/* single-command server
+/* SYNOPSIS
+/* #include <mail_proto.h>
+/*
+/* int mail_command_server(stream, type, name, ...)
+/* VSTREAM *stream;
+/* int type;
+/* const char *name;
+/* 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 "type, name, ..."
+/* Attribute list as defined in attr_scan(3).
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/* SEE ALSO
+/* attr_scan(3)
+/* mail_command_client(3) client interface
+/* mail_proto(3h), client-server protocol
+#include <mail_proto.h>
+/* 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 <vstream.h>
+
+/* Global library. */
+
+#include "mail_proto.h"
+
+/* mail_command_server - read single-command request */
+
+int mail_command_server(VSTREAM *stream,...)
+{
+ va_list ap;
+ int count;
+
+ va_start(ap, stream);
+ count = attr_vscan(stream, ATTR_FLAG_MISSING, ap);
+ va_end(ap);
+ return (count);
+}
diff --git a/gnu/dist/postfix/src/global/mail_conf.c b/gnu/dist/postfix/src/global/mail_conf.c
index 2af13f29c55..df25bed6974 100644
--- a/gnu/dist/postfix/src/global/mail_conf.c
+++ b/gnu/dist/postfix/src/global/mail_conf.c
@@ -73,7 +73,9 @@
/* System library. */
#include <sys_defs.h>
+#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
/* Utility library. */
@@ -84,12 +86,55 @@
#include <dict.h>
#include <safe.h>
#include <stringops.h>
+#include <readlline.h>
/* Global library. */
#include "mail_params.h"
#include "mail_conf.h"
+/* mail_conf_checkdir - authorize non-default directory */
+
+static void mail_conf_checkdir(const char *config_dir)
+{
+ VSTRING *buf;
+ VSTREAM *fp;
+ char *path;
+ char *name;
+ char *value;
+ char *cp;
+ int found = 0;
+
+ /*
+ * If running set-[ug]id, require that a non-default configuration
+ * directory name is blessed as a bona fide configuration directory in
+ * the default main.cf file.
+ */
+ path = concatenate(DEF_CONFIG_DIR, "/", "main.cf", (char *) 0);
+ if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
+ msg_fatal("open file %s: %m", path);
+
+ buf = vstring_alloc(1);
+ while (found == 0 && readlline(buf, fp, (int *) 0)) {
+ if (split_nameval(vstring_str(buf), &name, &value) == 0
+ && strcmp(name, VAR_CONFIG_DIRS) == 0) {
+ while (found == 0 && (cp = mystrtok(&value, ", \t\r\n")) != 0)
+ if (strcmp(cp, config_dir) == 0)
+ found = 1;
+ }
+ }
+ if (vstream_fclose(fp))
+ msg_fatal("read file %s: %m", path);
+ vstring_free(buf);
+
+ if (found == 0) {
+ msg_error("untrusted configuration directory name: %s", config_dir);
+ msg_fatal("specify \"%s = %s\" in %s",
+ VAR_CONFIG_DIRS, config_dir, path);
+ }
+ myfree(path);
+}
+
/* mail_conf_read - read global configuration file */
void mail_conf_read(void)
@@ -114,9 +159,19 @@ void mail_conf_suck(void)
dict_unknown_allowed = 1;
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 */
+ if ((config_dir = getenv(CONF_ENV_PATH)) == 0)
+ config_dir = DEF_CONFIG_DIR;
+ var_config_dir = mystrdup(config_dir);
set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
+
+ /*
+ * If the configuration directory name comes from a different trust
+ * domain, require that it is listed in the default main.cf file.
+ */
+ if (strcmp(var_config_dir, DEF_CONFIG_DIR) != 0 /* non-default */
+ && safe_getenv(CONF_ENV_PATH) == 0 /* non-default */
+ && geteuid() != 0) /* untrusted */
+ mail_conf_checkdir(var_config_dir);
path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
dict_load_file(CONFIG_DICT, path);
myfree(path);
diff --git a/gnu/dist/postfix/src/global/mail_conf.h b/gnu/dist/postfix/src/global/mail_conf.h
index ffa1341d7ca..25406e8da14 100644
--- a/gnu/dist/postfix/src/global/mail_conf.h
+++ b/gnu/dist/postfix/src/global/mail_conf.h
@@ -21,7 +21,8 @@
*/
#define CONF_ENV_PATH "MAIL_CONFIG" /* config database */
#define CONF_ENV_VERB "MAIL_VERBOSE" /* verbose mode on */
-#define CONF_ENV_DEBUG "MAIL_DEBUG" /* verbose mode on */
+#define CONF_ENV_DEBUG "MAIL_DEBUG" /* live debugging */
+#define CONF_ENV_LOGTAG "MAIL_LOGTAG" /* instance name */
/*
* External representation for booleans.
diff --git a/gnu/dist/postfix/src/global/mail_connect.c b/gnu/dist/postfix/src/global/mail_connect.c
index cd486f9bc40..a8ef96d1364 100644
--- a/gnu/dist/postfix/src/global/mail_connect.c
+++ b/gnu/dist/postfix/src/global/mail_connect.c
@@ -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(path, " socket", (char *) 0);
vstream_control(stream,
- VSTREAM_CTL_PATH, path,
+ VSTREAM_CTL_PATH, sock_name,
VSTREAM_CTL_END);
+ myfree(sock_name);
}
myfree(path);
return (stream);
diff --git a/gnu/dist/postfix/src/global/mail_copy.c b/gnu/dist/postfix/src/global/mail_copy.c
index e08d301f56e..c2d328981ca 100644
--- a/gnu/dist/postfix/src/global/mail_copy.c
+++ b/gnu/dist/postfix/src/global/mail_copy.c
@@ -62,6 +62,14 @@
/* DIAGNOSTICS
/* A non-zero result means the operation failed. Warnings: corrupt
/* message file. A corrupt message is marked as corrupt.
+/*
+/* The result is the bit-wise OR of zero or more of the following:
+/* .IP MAIL_COPY_STAT_CORRUPT
+/* The queue file is marked as corrupt.
+/* .IP MAIL_COPY_STAT_READ
+/* A read error was detected; errno specifies the nature of the problem.
+/* .IP MAIL_COPY_STAT_WRITE
+/* A write error was detected; errno specifies the nature of the problem.
/* SEE ALSO
/* mark_corrupt(3), mark queue file as corrupted.
/* LICENSE
@@ -81,6 +89,7 @@
#include <string.h>
#include <unistd.h>
#include <time.h>
+#include <errno.h>
/* Utility library. */
@@ -98,8 +107,9 @@
#include "rec_type.h"
#include "mail_queue.h"
#include "mail_addr.h"
-#include "mail_copy.h"
#include "mark_corrupt.h"
+#include "mail_params.h"
+#include "mail_copy.h"
/* mail_copy - copy message with extreme prejudice */
@@ -183,6 +193,8 @@ int mail_copy(const char *sender, const char *delivered,
prev_type = type;
}
if (vstream_ferror(dst) == 0) {
+ if (var_fault_inj_code == 1)
+ type = 0;
if (type != REC_TYPE_XTRA)
corrupt_error = mark_corrupt(src);
if (prev_type != REC_TYPE_NORM)
@@ -208,9 +220,17 @@ int mail_copy(const char *sender, const char *delivered,
if ((flags & MAIL_COPY_TOFILE) != 0)
write_error |= fsync(vstream_fileno(dst));
#endif
+ if (var_fault_inj_code == 2) {
+ read_error = 1;
+ errno = ENOENT;
+ }
+ if (var_fault_inj_code == 3) {
+ write_error = 1;
+ errno = ENOENT;
+ }
#ifndef NO_TRUNCATE
if ((flags & MAIL_COPY_TOFILE) != 0)
- if (read_error || write_error)
+ if (corrupt_error || read_error || write_error)
ftruncate(vstream_fileno(dst), (off_t) orig_length);
#endif
write_error |= vstream_fclose(dst);
@@ -218,5 +238,7 @@ int mail_copy(const char *sender, const char *delivered,
vstring_sprintf(why, "error reading message: %m");
if (why && write_error)
vstring_sprintf(why, "error writing message: %m");
- return (corrupt_error || read_error || write_error);
+ return ((corrupt_error ? MAIL_COPY_STAT_CORRUPT : 0)
+ | (read_error ? MAIL_COPY_STAT_READ : 0)
+ | (write_error ? MAIL_COPY_STAT_WRITE : 0));
}
diff --git a/gnu/dist/postfix/src/global/mail_copy.h b/gnu/dist/postfix/src/global/mail_copy.h
index 2d8c6ae39f1..efa1957c5c0 100644
--- a/gnu/dist/postfix/src/global/mail_copy.h
+++ b/gnu/dist/postfix/src/global/mail_copy.h
@@ -35,6 +35,11 @@ extern int mail_copy(const char *, const char *, VSTREAM *, VSTREAM *,
MAIL_COPY_RETURN_PATH | MAIL_COPY_BLANK)
#define MAIL_COPY_NONE 0 /* all turned off */
+#define MAIL_COPY_STAT_OK 0
+#define MAIL_COPY_STAT_CORRUPT (1<<0)
+#define MAIL_COPY_STAT_READ (1<<1)
+#define MAIL_COPY_STAT_WRITE (1<<2)
+
/* LICENSE
/* .ad
/* .fi
diff --git a/gnu/dist/postfix/src/global/mail_params.c b/gnu/dist/postfix/src/global/mail_params.c
index ed04630aad6..bf134b11a0e 100644
--- a/gnu/dist/postfix/src/global/mail_params.c
+++ b/gnu/dist/postfix/src/global/mail_params.c
@@ -14,9 +14,12 @@
/* char *var_transit_origin;
/* char *var_transit_dest;
/* char *var_mail_name;
+/* char *var_syslog_name;
/* char *var_mail_owner;
/* uid_t var_owner_uid;
/* gid_t var_owner_gid;
+/* char *var_sgid_group;
+/* gid_t var_sgid_gid;
/* char *var_default_privs;
/* uid_t var_default_uid;
/* gid_t var_default_gid;
@@ -39,6 +42,7 @@
/* int var_line_limit;
/* char *var_alias_db_map;
/* int var_message_limit;
+/* char *var_mail_release;
/* char *var_mail_version;
/* int var_ipc_idle_limit;
/* char *var_db_type;
@@ -61,9 +65,17 @@
/* char *var_fflush_domains;
/* char *var_def_transport;
/* char *var_mynetworks_style;
+/* char *var_verp_delims;
+/* char *var_verp_filter;
+/* char *var_par_dom_match;
+/* char *var_config_dirs;
/*
/* char *var_import_environ;
/* char *var_export_environ;
+/* char *var_debug_peer_list;
+/* int var_debug_peer_level;
+/* int var_in_flow_delay;
+/* int var_fault_inj_code;
/*
/* void mail_params_init()
/* DESCRIPTION
@@ -92,8 +104,10 @@
#include <sys_defs.h>
#include <unistd.h>
+#include <stdlib.h>
#include <string.h>
#include <pwd.h>
+#include <grp.h>
#include <time.h>
#ifdef STRCASECMP_IN_STRINGS_H
@@ -107,6 +121,7 @@
#include <get_hostname.h>
#include <valid_hostname.h>
#include <stringops.h>
+#include <safe.h>
/* Global library. */
@@ -114,6 +129,7 @@
#include "mail_conf.h"
#include "mail_version.h"
#include "mail_proto.h"
+#include "verp_sender.h"
#include "mail_params.h"
/*
@@ -127,9 +143,12 @@ char *var_relayhost;
char *var_transit_origin;
char *var_transit_dest;
char *var_mail_name;
+char *var_syslog_name;
char *var_mail_owner;
uid_t var_owner_uid;
gid_t var_owner_gid;
+char *var_sgid_group;
+gid_t var_sgid_gid;
char *var_default_privs;
uid_t var_default_uid;
gid_t var_default_gid;
@@ -152,6 +171,7 @@ char *var_double_bounce_sender;
int var_line_limit;
char *var_alias_db_map;
int var_message_limit;
+char *var_mail_release;
char *var_mail_version;
int var_ipc_idle_limit;
char *var_db_type;
@@ -174,9 +194,19 @@ char *var_relay_domains;
char *var_fflush_domains;
char *var_def_transport;
char *var_mynetworks_style;
+char *var_verp_delims;
+char *var_verp_filter;
+int var_in_flow_delay;
+char *var_par_dom_match;
+char *var_config_dirs;
char *var_import_environ;
char *var_export_environ;
+char *var_debug_peer_list;
+int var_debug_peer_level;
+int var_fault_inj_code;
+
+#define MAIN_CONF_FILE "main.cf"
/* check_myhostname - lookup hostname and validate */
@@ -199,9 +229,11 @@ static const char *check_myhostname(void)
name = get_hostname();
if ((dot = strchr(name, '.')) == 0) {
if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0)
- msg_fatal("My hostname %s is not a fully qualified name - set %s or %s in %s/main.cf",
- name, VAR_MYHOSTNAME, VAR_MYDOMAIN, var_config_dir);
- name = concatenate(name, ".", domain, (char *) 0);
+ msg_warn("My hostname %s is not a fully qualified name - set %s or %s in %s/%s",
+ name, VAR_MYHOSTNAME, VAR_MYDOMAIN,
+ var_config_dir, MAIN_CONF_FILE);
+ else
+ name = concatenate(name, ".", domain, (char *) 0);
}
return (name);
}
@@ -228,13 +260,16 @@ static void check_default_privs(void)
struct passwd *pwd;
if ((pwd = getpwnam(var_default_privs)) == 0)
- msg_fatal("unknown %s configuration parameter value: %s",
+ msg_fatal("file %s/%s: parameter %s: unknown user name value: %s",
+ var_config_dir, MAIN_CONF_FILE,
VAR_DEFAULT_PRIVS, var_default_privs);
if ((var_default_uid = pwd->pw_uid) == 0)
- msg_fatal("%s: %s: privileged user is not allowed",
+ msg_fatal("file %s/%s: parameter %s: user %s has privileged user ID",
+ var_config_dir, MAIN_CONF_FILE,
VAR_DEFAULT_PRIVS, var_default_privs);
if ((var_default_gid = pwd->pw_gid) == 0)
- msg_fatal("%s: %s: privileged group is not allowed",
+ msg_fatal("file %s/%s: parameter %s: user %s has privileged group ID",
+ var_config_dir, MAIN_CONF_FILE,
VAR_DEFAULT_PRIVS, var_default_privs);
}
@@ -245,14 +280,91 @@ static void check_mail_owner(void)
struct passwd *pwd;
if ((pwd = getpwnam(var_mail_owner)) == 0)
- msg_fatal("unknown %s configuration parameter value: %s",
+ msg_fatal("file %s/%s: parameter %s: unknown user name value: %s",
+ var_config_dir, MAIN_CONF_FILE,
VAR_MAIL_OWNER, var_mail_owner);
if ((var_owner_uid = pwd->pw_uid) == 0)
- msg_fatal("%s: %s: privileged user is not allowed",
+ msg_fatal("file %s/%s: parameter %s: user %s has privileged user ID",
+ var_config_dir, MAIN_CONF_FILE,
VAR_MAIL_OWNER, var_mail_owner);
if ((var_owner_gid = pwd->pw_gid) == 0)
- msg_fatal("%s: %s: privileged group is not allowed",
- VAR_DEFAULT_PRIVS, var_mail_owner);
+ msg_fatal("file %s/%s: parameter %s: user %s has privileged group ID",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_MAIL_OWNER, var_mail_owner);
+
+ /*
+ * This detects only some forms of sharing. Enumerating the entire
+ * password file name space could be expensive. The purpose of this code
+ * is to discourage user ID sharing by developers and package
+ * maintainers.
+ */
+ if ((pwd = getpwuid(var_owner_uid)) != 0
+ && strcmp(pwd->pw_name, var_mail_owner) != 0)
+ msg_fatal("file %s/%s: parameter %s: user %s has same user ID as %s",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_MAIL_OWNER, var_mail_owner, pwd->pw_name);
+}
+
+/* check_sgid_group - lookup setgid group attributes and validate */
+
+static void check_sgid_group(void)
+{
+ struct group *grp;
+
+ if ((grp = getgrnam(var_sgid_group)) == 0)
+ msg_fatal("file %s/%s: parameter %s: unknown group name: %s",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_SGID_GROUP, var_sgid_group);
+ if ((var_sgid_gid = grp->gr_gid) == 0)
+ msg_fatal("file %s/%s: parameter %s: group %s has privileged group ID",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_SGID_GROUP, var_sgid_group);
+
+ /*
+ * This detects only some forms of sharing. Enumerating the entire group
+ * file name space could be expensive. The purpose of this code is to
+ * discourage group ID sharing by developers and package maintainers.
+ */
+ if ((grp = getgrgid(var_sgid_gid)) != 0
+ && strcmp(grp->gr_name, var_sgid_group) != 0)
+ msg_fatal("file %s/%s: parameter %s: group %s has same group ID as %s",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_SGID_GROUP, var_sgid_group, grp->gr_name);
+}
+
+/* check_overlap - disallow UID or GID sharing */
+
+static void check_overlap(void)
+{
+ if (strcmp(var_default_privs, var_mail_owner) == 0)
+ msg_fatal("file %s/%s: parameters %s and %s specify the same user %s",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_DEFAULT_PRIVS, VAR_MAIL_OWNER,
+ var_default_privs);
+ if (var_default_uid == var_owner_uid)
+ msg_fatal("file %s/%s: parameters %s and %s: users %s and %s have the same user ID: %ld",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_DEFAULT_PRIVS, VAR_MAIL_OWNER,
+ var_default_privs, var_mail_owner,
+ (long) var_owner_uid);
+ if (var_default_gid == var_owner_gid)
+ msg_fatal("file %s/%s: parameters %s and %s: users %s and %s have the same group ID: %ld",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_DEFAULT_PRIVS, VAR_MAIL_OWNER,
+ var_default_privs, var_mail_owner,
+ (long) var_owner_gid);
+ if (var_default_gid == var_sgid_gid)
+ msg_fatal("file %s/%s: parameters %s and %s: user %s and group %s have the same group ID: %ld",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_DEFAULT_PRIVS, VAR_SGID_GROUP,
+ var_default_privs, var_sgid_group,
+ (long) var_sgid_gid);
+ if (var_owner_gid == var_sgid_gid)
+ msg_fatal("file %s/%s: parameters %s and %s: user %s and group %s have the same group ID: %ld",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_MAIL_OWNER, VAR_SGID_GROUP,
+ var_mail_owner, var_sgid_group,
+ (long) var_sgid_gid);
}
/* mail_params_init - configure built-in parameters */
@@ -270,7 +382,9 @@ void mail_params_init()
};
static CONFIG_STR_TABLE other_str_defaults[] = {
VAR_MAIL_NAME, DEF_MAIL_NAME, &var_mail_name, 1, 0,
+ VAR_SYSLOG_NAME, DEF_SYSLOG_NAME, &var_syslog_name, 1, 0,
VAR_MAIL_OWNER, DEF_MAIL_OWNER, &var_mail_owner, 1, 0,
+ VAR_SGID_GROUP, DEF_SGID_GROUP, &var_sgid_group, 1, 0,
VAR_MYDEST, DEF_MYDEST, &var_mydest, 0, 0,
VAR_MYORIGIN, DEF_MYORIGIN, &var_myorigin, 1, 0,
VAR_RELAYHOST, DEF_RELAYHOST, &var_relayhost, 0, 0,
@@ -283,6 +397,7 @@ void mail_params_init()
VAR_DOUBLE_BOUNCE, DEF_DOUBLE_BOUNCE, &var_double_bounce_sender, 1, 0,
VAR_DEFAULT_PRIVS, DEF_DEFAULT_PRIVS, &var_default_privs, 1, 0,
VAR_ALIAS_DB_MAP, DEF_ALIAS_DB_MAP, &var_alias_db_map, 0, 0,
+ VAR_MAIL_RELEASE, DEF_MAIL_RELEASE, &var_mail_release, 1, 0,
VAR_MAIL_VERSION, DEF_MAIL_VERSION, &var_mail_version, 1, 0,
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,
@@ -293,6 +408,11 @@ void mail_params_init()
VAR_IMPORT_ENVIRON, DEF_IMPORT_ENVIRON, &var_import_environ, 0, 0,
VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 0, 0,
VAR_MYNETWORKS_STYLE, DEF_MYNETWORKS_STYLE, &var_mynetworks_style, 1, 0,
+ VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0,
+ VAR_VERP_DELIMS, DEF_VERP_DELIMS, &var_verp_delims, 2, 2,
+ VAR_VERP_FILTER, DEF_VERP_FILTER, &var_verp_filter, 1, 0,
+ VAR_PAR_DOM_MATCH, DEF_PAR_DOM_MATCH, &var_par_dom_match, 0, 0,
+ VAR_CONFIG_DIRS, DEF_CONFIG_DIRS, &var_config_dirs, 0, 0,
0,
};
static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
@@ -307,6 +427,8 @@ void mail_params_init()
VAR_HASH_QUEUE_DEPTH, DEF_HASH_QUEUE_DEPTH, &var_hash_queue_depth, 1, 0,
VAR_FORK_TRIES, DEF_FORK_TRIES, &var_fork_tries, 1, 0,
VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 1, 0,
+ VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
+ VAR_FAULT_INJ_CODE, DEF_FAULT_INJ_CODE, &var_fault_inj_code, 0, 0,
0,
};
static CONFIG_TIME_TABLE time_defaults[] = {
@@ -318,6 +440,7 @@ void mail_params_init()
VAR_FLOCK_DELAY, DEF_FLOCK_DELAY, &var_flock_delay, 1, 0,
VAR_FLOCK_STALE, DEF_FLOCK_STALE, &var_flock_stale, 1, 0,
VAR_DAEMON_TIMEOUT, DEF_DAEMON_TIMEOUT, &var_daemon_timeout, 1, 0,
+ VAR_IN_FLOW_DELAY, DEF_IN_FLOW_DELAY, &var_in_flow_delay, 0, 10,
0,
};
static CONFIG_BOOL_TABLE bool_defaults[] = {
@@ -326,6 +449,7 @@ void mail_params_init()
VAR_OWNREQ_SPECIAL, DEF_OWNREQ_SPECIAL, &var_ownreq_special,
0,
};
+ const char *cp;
/*
* Extract syslog_facility early, so that from here on all errors are
@@ -334,7 +458,8 @@ void mail_params_init()
get_mail_conf_str_table(first_str_defaults);
if (!msg_syslog_facility(var_syslog_facility))
- msg_fatal("unknown %s configuration parameter value: %s",
+ msg_fatal("file %s/%s: parameter %s: unrecognized value: %s",
+ var_config_dir, MAIN_CONF_FILE,
VAR_SYSLOG_FACILITY, var_syslog_facility);
/*
@@ -343,10 +468,14 @@ void mail_params_init()
* the domain.
*/
get_mail_conf_str_fn_table(function_str_defaults);
- if (!valid_hostname(var_myhostname, DO_GRIPE)
- || !valid_hostname(var_mydomain, DO_GRIPE))
- msg_fatal("main.cf configuration error: bad %s or %s parameter value",
- VAR_MYHOSTNAME, VAR_MYDOMAIN);
+ if (!valid_hostname(var_myhostname, DO_GRIPE))
+ msg_fatal("file %s/%s: parameter %s: bad parameter value: %s",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_MYHOSTNAME, var_myhostname);
+ if (!valid_hostname(var_mydomain, DO_GRIPE))
+ msg_fatal("file %s/%s: parameter %s: bad parameter value: %s",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_MYDOMAIN, var_mydomain);
/*
* Variables that are needed by almost every program.
@@ -357,6 +486,8 @@ void mail_params_init()
get_mail_conf_time_table(time_defaults);
check_default_privs();
check_mail_owner();
+ check_sgid_group();
+ check_overlap();
/*
* Variables whose defaults are determined at runtime, after other
@@ -377,8 +508,25 @@ void mail_params_init()
time(&var_starttime);
/*
+ * Export the syslog name so children can inherit and use it before they
+ * have initialized.
+ */
+ if ((cp = safe_getenv(CONF_ENV_LOGTAG)) == 0
+ || strcmp(cp, var_syslog_name) != 0)
+ if (setenv(CONF_ENV_LOGTAG, var_syslog_name, 1) < 0)
+ msg_fatal("setenv %s %s: %m", CONF_ENV_LOGTAG, var_syslog_name);
+
+ /*
* I have seen this happen just too often.
*/
if (strcasecmp(var_myhostname, var_relayhost) == 0)
msg_fatal("myhostname == relayhost");
+
+ /*
+ * One more sanity check.
+ */
+ if ((cp = verp_delims_verify(var_verp_delims)) != 0)
+ msg_fatal("file %s/%s: parameters %s and %s: %s",
+ var_config_dir, MAIN_CONF_FILE,
+ VAR_VERP_DELIMS, VAR_VERP_FILTER, cp);
}
diff --git a/gnu/dist/postfix/src/global/mail_params.h b/gnu/dist/postfix/src/global/mail_params.h
index 294fd1fc5fd..4009dbdc2b5 100644
--- a/gnu/dist/postfix/src/global/mail_params.h
+++ b/gnu/dist/postfix/src/global/mail_params.h
@@ -49,6 +49,11 @@ extern char *var_mail_owner;
extern uid_t var_owner_uid;
extern gid_t var_owner_gid;
+#define VAR_SGID_GROUP "setgid_group"
+#define DEF_SGID_GROUP "postdrop"
+extern char *var_sgid_group;
+extern gid_t var_sgid_gid;
+
#define VAR_DEFAULT_PRIVS "default_privs"
#define DEF_DEFAULT_PRIVS "nobody"
extern char *var_default_privs;
@@ -124,6 +129,17 @@ extern char *var_masq_domains;
#define DEF_MASQ_EXCEPTIONS ""
extern char *var_masq_exceptions;
+#define MASQ_CLASS_ENV_FROM "envelope_sender"
+#define MASQ_CLASS_ENV_RCPT "envelope_recipient"
+#define MASQ_CLASS_HDR_FROM "header_sender"
+#define MASQ_CLASS_HDR_RCPT "header_recipient"
+
+#define VAR_MASQ_CLASSES "masquerade_classes"
+#define DEF_MASQ_CLASSES MASQ_CLASS_ENV_FROM ", " \
+ MASQ_CLASS_HDR_FROM ", " \
+ MASQ_CLASS_HDR_RCPT
+extern char *var_masq_classes;
+
/*
* Intranet versus internet.
*/
@@ -158,13 +174,13 @@ extern char *var_queue_dir;
#define VAR_DAEMON_DIR "daemon_directory"
#ifndef DEF_DAEMON_DIR
-#define DEF_DAEMON_DIR "$program_directory"
+#define DEF_DAEMON_DIR "/usr/libexec/postfix"
#endif
extern char *var_daemon_dir;
#define VAR_COMMAND_DIR "command_directory"
#ifndef DEF_COMMAND_DIR
-#define DEF_COMMAND_DIR "$program_directory"
+#define DEF_COMMAND_DIR "/usr/sbin"
#endif
extern char *var_command_dir;
@@ -191,6 +207,10 @@ extern time_t var_starttime;
#endif
extern char *var_config_dir;
+#define VAR_CONFIG_DIRS "alternate_config_directories"
+#define DEF_CONFIG_DIRS ""
+extern char *var_config_dirs;
+
/*
* Preferred type of indexed files. The DEF_DB_TYPE macro value is system
* dependent. It is defined in <sys_defs.h>.
@@ -224,6 +244,10 @@ extern char *var_always_bcc;
/*
* What to put in the To: header when no recipients were disclosed.
+ *
+ * XXX 2822: When no recipient headers remain, a system should insert a Bcc:
+ * header without additional information. That is not so great given that
+ * MTAs routinely strip Bcc: headers from message headers.
*/
#define VAR_RCPT_WITHELD "undisclosed_recipients_header"
#define DEF_RCPT_WITHELD "To: undisclosed-recipients:;"
@@ -358,6 +382,10 @@ extern char *var_home_mailbox;
#define DEF_MAILBOX_COMMAND ""
extern char *var_mailbox_command;
+#define VAR_MAILBOX_CMD_MAPS "mailbox_command_maps"
+#define DEF_MAILBOX_CMD_MAPS ""
+extern char *var_mailbox_cmd_maps;
+
#define VAR_MAILBOX_TRANSP "mailbox_transport"
#define DEF_MAILBOX_TRANSP ""
extern char *var_mailbox_transport;
@@ -634,6 +662,9 @@ extern int var_hash_queue_depth;
* determines how many recipient addresses the SMTP client sends along with
* each message. Unfortunately, some mailers misbehave and disconnect (smap)
* when given more recipients than they are willing to handle.
+ *
+ * XXX 2821: A mail system is supposed to use EHLO instead of HELO, and to fall
+ * back to HELO if EHLO is not supported.
*/
#define VAR_BESTMX_TRANSP "best_mx_transport"
#define DEF_BESTMX_TRANSP ""
@@ -688,7 +719,11 @@ extern bool var_ign_mx_lookup_err;
extern bool var_skip_quit_resp;
#define VAR_SMTP_ALWAYS_EHLO "smtp_always_send_ehlo"
+#ifdef RFC821_SYNTAX
#define DEF_SMTP_ALWAYS_EHLO 0
+#else
+#define DEF_SMTP_ALWAYS_EHLO 1
+#endif
extern bool var_smtp_always_ehlo;
#define VAR_SMTP_NEVER_EHLO "smtp_never_send_ehlo"
@@ -699,6 +734,22 @@ extern bool var_smtp_never_ehlo;
#define DEF_SMTP_BIND_ADDR ""
extern char *var_smtp_bind_addr;
+#define VAR_SMTP_RAND_ADDR "smtp_randomize_addresses"
+#define DEF_SMTP_RAND_ADDR 1
+extern bool var_smtp_rand_addr;
+
+#define VAR_SMTP_BREAK_LINES "smtp_break_lines"
+#define DEF_SMTP_BREAK_LINES 1
+extern bool var_smtp_break_lines;
+
+#define VAR_SMTP_PIX_THRESH "smtp_pix_workaround_threshold_time"
+#define DEF_SMTP_PIX_THRESH "500s"
+extern int var_smtp_pix_thresh;
+
+#define VAR_SMTP_PIX_DELAY "smtp_pix_workaround_delay_time"
+#define DEF_SMTP_PIX_DELAY "10s"
+extern int var_smtp_pix_delay;
+
/*
* SMTP server. The soft error limit determines how many errors an SMTP
* client may make before we start to slow down; the hard error limit
@@ -729,9 +780,17 @@ extern int var_smtpd_hard_erlim;
extern int var_smtpd_err_sleep;
#define VAR_SMTPD_JUNK_CMD "smtpd_junk_command_limit"
-#define DEF_SMTPD_JUNK_CMD 1000
+#define DEF_SMTPD_JUNK_CMD 100
extern int var_smtpd_junk_cmd_limit;
+#define VAR_SMTPD_HIST_THRSH "smtpd_history_flush_threshold"
+#define DEF_SMTPD_HIST_THRSH 100
+extern int var_smtpd_hist_thrsh;
+
+#define VAR_SMTPD_NOOP_CMDS "smtpd_noop_commands"
+#define DEF_SMTPD_NOOP_CMDS ""
+extern char *var_smtpd_noop_cmds;
+
/*
* SASL authentication support, SMTP server side.
*/
@@ -747,6 +806,12 @@ extern char *var_smtpd_sasl_opts;
#define DEF_SMTPD_SASL_REALM "$myhostname"
extern char *var_smtpd_sasl_realm;
+#define VAR_SMTPD_SND_AUTH_MAPS "smtpd_sender_login_maps"
+#define DEF_SMTPD_SND_AUTH_MAPS ""
+extern char *var_smtpd_snd_auth_maps;
+
+#define REJECT_SENDER_LOGIN_MISMATCH "reject_sender_login_mismatch"
+
/*
* SASL authentication support, SMTP client side.
*/
@@ -1092,6 +1157,10 @@ extern int var_relay_code;
#define PERMIT_MX_BACKUP "permit_mx_backup"
+#define VAR_PERM_MX_NETWORKS "permit_mx_backup_networks"
+#define DEF_PERM_MX_NETWORKS ""
+extern char *var_perm_mx_networks;
+
#define VAR_ACCESS_MAP_CODE "access_map_reject_code"
#define DEF_ACCESS_MAP_CODE 554
extern int var_access_map_code;
@@ -1102,13 +1171,15 @@ extern int var_access_map_code;
#define CHECK_RECIP_ACL "check_recipient_access"
#define CHECK_ETRN_ACL "check_etrn_access"
+#define WARN_IF_REJECT "warn_if_reject"
+
#define REJECT_MAPS_RBL "reject_maps_rbl"
#define VAR_MAPS_RBL_CODE "maps_rbl_reject_code"
#define DEF_MAPS_RBL_CODE 554
extern int var_maps_rbl_code;
#define VAR_MAPS_RBL_DOMAINS "maps_rbl_domains"
-#define DEF_MAPS_RBL_DOMAINS "blackholes.mail-abuse.org"
+#define DEF_MAPS_RBL_DOMAINS ""
extern char *var_maps_rbl_domains;
#define VAR_SMTPD_DELAY_REJECT "smtpd_delay_reject"
@@ -1117,6 +1188,10 @@ extern int var_smtpd_delay_reject;
#define REJECT_UNAUTH_PIPE "reject_unauth_pipelining"
+#define VAR_SMTPD_NULL_KEY "smtpd_null_access_lookup_key"
+#define DEF_SMTPD_NULL_KEY ""
+extern char *var_smtpd_null_key;
+
/*
* Heuristic to reject most unknown recipients at the SMTP port.
*/
@@ -1192,7 +1267,7 @@ extern int var_fflush_refresh;
* and what Postfix exports to the external world.
*/
#define VAR_IMPORT_ENVIRON "import_environment"
-#define DEF_IMPORT_ENVIRON "MAIL_CONFIG MAIL_DEBUG TZ XAUTHORITY DISPLAY"
+#define DEF_IMPORT_ENVIRON "MAIL_CONFIG MAIL_DEBUG MAIL_LOGTAG TZ XAUTHORITY DISPLAY"
extern char *var_import_environ;
#define VAR_EXPORT_ENVIRON "export_environment"
@@ -1230,6 +1305,112 @@ extern int var_virt_mailbox_limit;
#define DEF_VIRT_MAILBOX_LOCK "fcntl"
extern char *var_virt_mailbox_lock;
+ /*
+ * Distinct logging tag for multiple Postfix instances.
+ */
+#define VAR_SYSLOG_NAME "syslog_name"
+#define DEF_SYSLOG_NAME "postfix"
+extern char *var_syslog_name;
+
+ /*
+ * QMQPD
+ */
+#define VAR_QMQPD_CLIENTS "qmqpd_authorized_clients"
+#define DEF_QMQPD_CLIENTS ""
+extern char *var_qmqpd_clients;
+
+#define VAR_QMTPD_TMOUT "qmqpd_timeout"
+#define DEF_QMTPD_TMOUT "300s"
+extern int var_qmqpd_timeout;
+
+#define VAR_QMTPD_ERR_SLEEP "qmqpd_error_delay"
+#define DEF_QMTPD_ERR_SLEEP "5s"
+extern int var_qmqpd_err_sleep;
+
+ /*
+ * VERP, more DJB intellectual cross-pollination. However, we prefer + as
+ * the default recipient delimiter.
+ */
+#define VAR_VERP_DELIMS "default_verp_delimiters"
+#define DEF_VERP_DELIMS "+="
+extern char *var_verp_delims;
+
+#define VAR_VERP_FILTER "verp_delimiter_filter"
+#define DEF_VERP_FILTER "-=+"
+extern char *var_verp_filter;
+
+#define VAR_VERP_BOUNCE_OFF "disable_verp_bounces"
+#define DEF_VERP_BOUNCE_OFF 0
+extern bool var_verp_bounce_off;
+
+ /*
+ * Inbound mail flow control. This allows for a stiffer coupling between
+ * receiving mail and sending mail. A sending process produces one token for
+ * each message that it takes from the incoming queue; a receiving process
+ * consumes one token for each message that it adds to the incoming queue.
+ * When no token is available (Postfix receives more mail than it is able to
+ * deliver) a receiving process pauses for $in_flow_delay seconds so that
+ * the sending processes get a chance to access the disk.
+ */
+#define VAR_IN_FLOW_DELAY "in_flow_delay"
+#define DEF_IN_FLOW_DELAY "1s"
+extern int var_in_flow_delay;
+
+ /*
+ * Backwards compatibility: foo.com matches itself and names below foo.com.
+ */
+#define VAR_PAR_DOM_MATCH "parent_domain_matches_subdomains"
+#define DEF_PAR_DOM_MATCH VAR_DEBUG_PEER_LIST "," \
+ VAR_FFLUSH_DOMAINS "," \
+ VAR_MYNETWORKS "," \
+ VAR_PERM_MX_NETWORKS "," \
+ VAR_QMQPD_CLIENTS "," \
+ VAR_RELAY_DOMAINS "," \
+ SMTPD_ACCESS_MAPS
+extern char *var_par_dom_match;
+
+#define SMTPD_ACCESS_MAPS "smtpd_access_maps"
+
+ /*
+ * Run-time fault injection.
+ */
+#define VAR_FAULT_INJ_CODE "fault_injection_code"
+#define DEF_FAULT_INJ_CODE 0
+extern int var_fault_inj_code;
+
+ /*
+ * Install/upgrade information.
+ */
+#define VAR_SENDMAIL_PATH "sendmail_path"
+#ifndef DEF_SENDMAIL_PATH
+#define DEF_SENDMAIL_PATH "/usr/sbin/sendmail"
+#endif
+
+#define VAR_MAILQ_PATH "mailq_path"
+#ifndef DEF_MAILQ_PATH
+#define DEF_MAILQ_PATH "/usr/bin/mailq"
+#endif
+
+#define VAR_NEWALIAS_PATH "newaliases_path"
+#ifndef DEF_NEWALIAS_PATH
+#define DEF_NEWALIAS_PATH "/usr/bin/newaliases"
+#endif
+
+#define VAR_MANPAGE_DIR "manpage_directory"
+#ifndef DEF_MANPAGE_DIR
+#define DEF_MANPAGE_DIR "/usr/local/man"
+#endif
+
+#define VAR_SAMPLE_DIR "sample_directory"
+#ifndef DEF_SAMPLE_DIR
+#define DEF_SAMPLE_DIR DEF_CONFIG_DIR
+#endif
+
+#define VAR_README_DIR "readme_directory"
+#ifndef DEF_README_DIR
+#define DEF_README_DIR "no"
+#endif
+
/* LICENSE
/* .ad
/* .fi
diff --git a/gnu/dist/postfix/src/global/mail_proto.h b/gnu/dist/postfix/src/global/mail_proto.h
index 78b76c864ec..adadcda60c9 100644
--- a/gnu/dist/postfix/src/global/mail_proto.h
+++ b/gnu/dist/postfix/src/global/mail_proto.h
@@ -21,6 +21,7 @@
*/
#include <vstream.h>
#include <iostuff.h>
+#include <attr.h>
/*
* Names of services: these are the names if INET ports, UNIX-domain sockets
@@ -50,12 +51,6 @@
#define MAIL_CLASS_PRIVATE "private"
/*
- * When sending across a list of objects, this is how we signal the list
- * end.
- */
-#define MAIL_EOF "@"
-
- /*
* Generic triggers.
*/
#define TRIGGER_REQ_WAKEUP 'W' /* wakeup */
@@ -71,28 +66,37 @@
/*
* Functional interface.
*/
-#define MAIL_SCAN_MORE 0
-#define MAIL_SCAN_DONE 1
-#define MAIL_SCAN_ERROR -1
-
-typedef int (*MAIL_SCAN_FN) (const char *, char *);
-typedef void (*MAIL_PRINT_FN) (VSTREAM *, const char *);
extern VSTREAM *mail_connect(const char *, const char *, int);
extern VSTREAM *mail_connect_wait(const char *, const char *);
-extern int mail_scan(VSTREAM *, const char *,...);
-extern void mail_scan_register(int, const char *, MAIL_SCAN_FN);
-extern void mail_print_register(int, const char *, MAIL_PRINT_FN);
-extern int PRINTFLIKE(2, 3) mail_print(VSTREAM *, const char *,...);
-extern int PRINTFLIKE(3, 4) mail_command_write(const char *, const char *, const char *,...);
-extern int mail_command_read(VSTREAM *, char *,...);
+extern int mail_command_client(const char *, const char *,...);
+extern int mail_command_server(VSTREAM *,...);
extern int mail_trigger(const char *, const char *, const char *, int);
extern char *mail_pathname(const char *, const char *);
/*
- * Stuff that needs <stdarg.h>
+ * Attribute names.
*/
-extern int mail_vprint(VSTREAM *, const char *, va_list);
-extern int mail_vscan(VSTREAM *, const char *, va_list);
+#define MAIL_ATTR_REQ "request"
+#define MAIL_ATTR_NREQ "nrequest"
+#define MAIL_ATTR_STATUS "status"
+
+#define MAIL_ATTR_FLAGS "flags"
+#define MAIL_ATTR_QUEUE "queue_name"
+#define MAIL_ATTR_QUEUEID "queue_id"
+#define MAIL_ATTR_SENDER "sender"
+#define MAIL_ATTR_RECIP "recipient"
+#define MAIL_ATTR_WHY "reason"
+#define MAIL_ATTR_VERPDL "verp_delimiters"
+#define MAIL_ATTR_SITE "site"
+#define MAIL_ATTR_OFFSET "offset"
+#define MAIL_ATTR_SIZE "size"
+#define MAIL_ATTR_ERRTO "errors-to"
+#define MAIL_ATTR_RRCPT "return-receipt"
+#define MAIL_ATTR_TIME "time"
+#define MAIL_ATTR_RULE "rule"
+#define MAIL_ATTR_ADDR "address"
+#define MAIL_ATTR_TRANSPORT "transport"
+#define MAIL_ATTR_NEXTHOP "nexthop"
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/global/mail_queue.c b/gnu/dist/postfix/src/global/mail_queue.c
index 5fc18640ec3..54916c8b59f 100644
--- a/gnu/dist/postfix/src/global/mail_queue.c
+++ b/gnu/dist/postfix/src/global/mail_queue.c
@@ -76,7 +76,7 @@
/* mail_queue_rename() renames a queue file. A non-zero result
/* means the operation failed.
/*
-/* mail_queue_remove() renames the named queue file. A non-zero result
+/* mail_queue_remove() removes the named queue file. A non-zero result
/* means the operation failed.
/*
/* mail_queue_name_ok() validates a mail queue name and returns
@@ -378,9 +378,23 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode)
file_id = get_file_id(fd);
GETTIMEOFDAY(&tv);
+ /*
+ * XXX Some systems seem to have clocks that correlate with process
+ * scheduling or something. Unfortunately, we cannot add random
+ * quantities to the time, because the non-inode part of a queue ID must
+ * not repeat within the same second. The queue ID is the sole thing that
+ * prevents multiple messages from getting the same Message-ID value.
+ */
for (count = 0;; count++) {
vstring_sprintf(id_buf, "%05X%s", (int) tv.tv_usec, file_id);
mail_queue_path(path_buf, queue_name, STR(id_buf));
+#if 0
+ if (access(STR(path_buf), X_OK) == 0) { /* collision. */
+ if ((int) ++tv.tv_usec < 0)
+ tv.tv_usec = 0;
+ continue;
+ }
+#endif
if (sane_rename(STR(temp_path), STR(path_buf)) == 0) /* success */
break;
if (errno == EPERM || errno == EISDIR) {/* collision. weird. */
diff --git a/gnu/dist/postfix/src/global/mail_stream.c b/gnu/dist/postfix/src/global/mail_stream.c
index e30537dfff6..74a8ada8c0a 100644
--- a/gnu/dist/postfix/src/global/mail_stream.c
+++ b/gnu/dist/postfix/src/global/mail_stream.c
@@ -14,10 +14,11 @@
/* .in -4
/* } MAIL_STREAM;
/*
-/* MAIL_STREAM *mail_stream_file(queue, class, service)
+/* MAIL_STREAM *mail_stream_file(queue, class, service, mode)
/* const char *queue;
/* const char *class;
/* const char *service;
+/* int mode;
/*
/* MAIL_STREAM *mail_stream_service(class, service)
/* const char *class;
@@ -29,8 +30,9 @@
/* void mail_stream_cleanup(info)
/* MAIL_STREAM *info;
/*
-/* int mail_stream_finish(info)
+/* int mail_stream_finish(info, why)
/* MAIL_STREAM *info;
+/* VSTRING *why;
/* DESCRIPTION
/* This module provides a generic interface to Postfix queue file
/* format messages to file, to Postfix server, or to external command.
@@ -41,7 +43,8 @@
/*
/* mail_stream_file() opens a mail stream to a newly-created file and
/* arranges for trigger delivery at finish time. This call never fails.
-/* But it may take forever.
+/* But it may take forever. The mode argument specifies additional
+/* file permissions that will be OR-ed in.
/*
/* mail_stream_command() opens a mail stream to external command,
/* and receives queue ID information from the command. The result
@@ -62,6 +65,7 @@
/* any of the mail_stream_xxx() routines, and destroys the argument.
/* The result is any of the status codes defined in <cleanup_user.h>.
/* It is up to the caller to remove incomplete file objects.
+/* The why argument can be a null pointer.
/* LICENSE
/* .ad
/* .fi
@@ -78,6 +82,7 @@
#include <sys_defs.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <errno.h>
/* Utility library. */
@@ -116,22 +121,30 @@ 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};
/*
* Make sure the message makes it to file. Set the execute bit when no
- * write error was detected.
+ * write error was detected. Some people believe that this code has a
+ * problem if the system crashes before fsync() returns; fchmod() could
+ * take effect before all the data blocks are written. Wietse claims that
+ * this is not a problem. Postfix rejects incomplete queue files, even
+ * when the +x attribute is set. Every Postfix queue file record has a
+ * type code and a length field. Files with truncated records are
+ * rejected, as are files with unknown type codes. Every Postfix queue
+ * file must end with an explicit END record. Postfix queue files without
+ * END record are discarded.
*/
if (vstream_fflush(info->stream)
- || fchmod(vstream_fileno(info->stream), 0700)
+ || fchmod(vstream_fileno(info->stream), 0700 | info->mode)
#ifdef HAS_FSYNC
|| fsync(vstream_fileno(info->stream))
#endif
)
- status = CLEANUP_STAT_WRITE;
+ status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
/*
* Close the queue file and mark it as closed. Be prepared for
@@ -142,7 +155,7 @@ static int mail_stream_finish_file(MAIL_STREAM * info)
* reasons.
*/
if (info->close(info->stream))
- status = CLEANUP_STAT_WRITE;
+ status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
info->stream = 0;
/*
@@ -161,14 +174,20 @@ 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;
/*
* Receive the peer's completion status.
*/
- if (mail_scan(info->stream, "%d", &status) != 1)
+ if ((why && attr_scan(info->stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
+ ATTR_TYPE_END) != 2)
+ || (!why && attr_scan(info->stream, ATTR_FLAG_MISSING,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) != 1))
status = CLEANUP_STAT_WRITE;
/*
@@ -180,20 +199,20 @@ 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 */
MAIL_STREAM *mail_stream_file(const char *queue, const char *class,
- const char *service)
+ const char *service, int mode)
{
MAIL_STREAM *info;
VSTREAM *stream;
- stream = mail_queue_enter(queue, 0600);
+ stream = mail_queue_enter(queue, 0600 | mode);
if (msg_verbose)
msg_info("open %s", VSTREAM_PATH(stream));
@@ -204,6 +223,7 @@ MAIL_STREAM *mail_stream_file(const char *queue, const char *class,
info->id = mystrdup(basename(VSTREAM_PATH(stream)));
info->class = mystrdup(class);
info->service = mystrdup(service);
+ info->mode = mode;
return (info);
}
@@ -218,7 +238,8 @@ MAIL_STREAM *mail_stream_service(const char *class, const char *name)
id_buf = vstring_alloc(10);
stream = mail_connect_wait(class, name);
- if (mail_scan(stream, "%s", id_buf) != 1) {
+ if (attr_scan(stream, ATTR_FLAG_MISSING,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id_buf, 0) != 1) {
vstream_fclose(stream);
return (0);
} else {
@@ -263,8 +284,12 @@ MAIL_STREAM *mail_stream_command(const char *command)
sleep(10);
}
argv_free(export_env);
+ vstream_control(stream,
+ VSTREAM_CTL_PATH, command,
+ VSTREAM_CTL_END);
- if (mail_scan(stream, "%s", id_buf) != 1) {
+ if (attr_scan(stream, ATTR_FLAG_MISSING,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id_buf, 0) != 1) {
vstream_pclose(stream);
return (0);
} else {
diff --git a/gnu/dist/postfix/src/global/mail_stream.h b/gnu/dist/postfix/src/global/mail_stream.h
index 3ae6c624ac8..c9fbfd97a78 100644
--- a/gnu/dist/postfix/src/global/mail_stream.h
+++ b/gnu/dist/postfix/src/global/mail_stream.h
@@ -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 {
@@ -32,13 +32,14 @@ struct MAIL_STREAM {
MAIL_STREAM_CLOSE_FN close; /* close stream */
char *class; /* trigger class */
char *service; /* trigger service */
+ int mode; /* additional permissions */
};
-extern MAIL_STREAM *mail_stream_file(const char *, const char *, const char *);
+extern MAIL_STREAM *mail_stream_file(const char *, const char *, const char *, int);
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
diff --git a/gnu/dist/postfix/src/global/mail_task.c b/gnu/dist/postfix/src/global/mail_task.c
index 0500754565c..2c06f9edaa3 100644
--- a/gnu/dist/postfix/src/global/mail_task.c
+++ b/gnu/dist/postfix/src/global/mail_task.c
@@ -12,7 +12,8 @@
/* mail_task() enforces consistent naming of mailer processes.
/* It strips pathname information from the process name, and
/* prepends the name of the mail system so that logfile entries
-/* are easier to recognize.
+/* are easier to recognize. The mail system name is specified
+/* with the "syslog_name" configuration parameter.
/*
/* The result is volatile. Make a copy of the result if it is
/* to be used for any appreciable amount of time.
@@ -35,25 +36,28 @@
/* Utility library. */
#include <vstring.h>
+#include <safe.h>
/* Global library. */
#include "mail_params.h"
+#include "mail_conf.h"
#include "mail_task.h"
-#define MAIL_TASK_FORMAT "postfix/%s"
-
/* mail_task - clean up and decorate the process name */
const char *mail_task(const char *argv0)
{
static VSTRING *canon_name;
const char *slash;
+ const char *tag;
if (canon_name == 0)
canon_name = vstring_alloc(10);
if ((slash = strrchr(argv0, '/')) != 0)
argv0 = slash + 1;
- vstring_sprintf(canon_name, MAIL_TASK_FORMAT, argv0);
+ if ((tag = safe_getenv(CONF_ENV_LOGTAG)) == 0)
+ tag = DEF_SYSLOG_NAME;
+ vstring_sprintf(canon_name, "%s/%s", tag, argv0);
return (vstring_str(canon_name));
}
diff --git a/gnu/dist/postfix/src/global/mail_trigger.c b/gnu/dist/postfix/src/global/mail_trigger.c
index 4d4d61c3f8b..457f19d6b8f 100644
--- a/gnu/dist/postfix/src/global/mail_trigger.c
+++ b/gnu/dist/postfix/src/global/mail_trigger.c
@@ -79,7 +79,7 @@ int mail_trigger(const char *class, const char *service,
*/
path = mail_pathname(class, service);
if ((status = stat(path, &st)) < 0) {
- /* void */ ;
+ msg_warn("unable to look up %s: %m", path);
} else if (S_ISFIFO(st.st_mode)) {
status = fifo_trigger(path, req_buf, req_len, var_trigger_timeout);
if (status < 0 && S_ISSOCK(st.st_mode))
diff --git a/gnu/dist/postfix/src/global/mail_version.h b/gnu/dist/postfix/src/global/mail_version.h
index fe062b14530..c5c930804ee 100644
--- a/gnu/dist/postfix/src/global/mail_version.h
+++ b/gnu/dist/postfix/src/global/mail_version.h
@@ -12,12 +12,29 @@
/* .nf
/*
- * Version of this program.
+ * Version of this program. Official versions are called a.b.c, and
+ * snapshots are called a.b.c-yyyymmdd, where a=major release number,
+ * b=minor release number, c=patchlevel, and yyyymmdd is the release date:
+ * yyyy=year, mm=month, dd=day.
+ *
+ * Patches change the patchlevel and the release date. Snapshots change the
+ * release date only, unless they include the same bugfix as a patch release.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Postfix-20010228-pl08"
+#ifdef SNAPSHOT
+#define DEF_MAIL_VERSION "1.1.2-$mail_release_date"
+#else
+#define DEF_MAIL_VERSION "1.1.2"
+#endif
extern char *var_mail_version;
+ /*
+ * Release date.
+ */
+#define VAR_MAIL_RELEASE "mail_release_date"
+#define DEF_MAIL_RELEASE "20020125"
+extern char *var_mail_release;
+
/* LICENSE
/* .ad
/* .fi
diff --git a/gnu/dist/postfix/src/global/mark_corrupt.c b/gnu/dist/postfix/src/global/mark_corrupt.c
index 994492ef1c3..bcffef8dd5c 100644
--- a/gnu/dist/postfix/src/global/mark_corrupt.c
+++ b/gnu/dist/postfix/src/global/mark_corrupt.c
@@ -27,15 +27,19 @@
#include <sys_defs.h>
#include <sys/stat.h>
+#include <unistd.h>
/* Utility library. */
#include <msg.h>
#include <vstream.h>
+#include <set_eugid.h>
/* Global library. */
#include <mail_queue.h>
+#include <mail_params.h>
+#include <deliver_request.h>
#include <mark_corrupt.h>
/* mark_corrupt - mark queue file as corrupt */
@@ -43,6 +47,16 @@
int mark_corrupt(VSTREAM *src)
{
char *myname = "mark_corrupt";
+ uid_t saved_uid;
+ gid_t saved_gid;
+
+ /*
+ * If not running as the mail system, change privileges first.
+ */
+ if ((saved_uid = geteuid()) != var_owner_uid) {
+ saved_gid = getegid();
+ set_eugid(var_owner_uid, var_owner_gid);
+ }
/*
* For now, the result value is -1; this may become a bit mask, or
@@ -52,5 +66,12 @@ int mark_corrupt(VSTREAM *src)
msg_warn("corrupted queue file: %s", VSTREAM_PATH(src));
if (fchmod(vstream_fileno(src), MAIL_QUEUE_STAT_CORRUPT))
msg_fatal("%s: fchmod %s: %m", myname, VSTREAM_PATH(src));
- return (-1);
+
+ /*
+ * Restore privileges.
+ */
+ if (saved_uid != var_owner_uid)
+ set_eugid(saved_uid, saved_gid);
+
+ return (DEL_STAT_DEFER);
}
diff --git a/gnu/dist/postfix/src/global/match_parent_style.c b/gnu/dist/postfix/src/global/match_parent_style.c
new file mode 100644
index 00000000000..a914f616fbe
--- /dev/null
+++ b/gnu/dist/postfix/src/global/match_parent_style.c
@@ -0,0 +1,72 @@
+/*++
+/* NAME
+/* match_parent_style 3
+/* SUMMARY
+/* parent domain matching control
+/* SYNOPSIS
+/* #include <match_parent_style.h>
+/*
+/* int match_parent_style(name)
+/* const char *name;
+/* DESCRIPTION
+/* This module queries configuration parameters for the policy that
+/* controls how wild-card parent domain names are used by various
+/* postfix lookup mechanisms.
+/*
+/* match_parent_style() looks up "name" in the
+/* parent_domain_matches_subdomain configuration parameter
+/* and returns either MATCH_FLAG_PARENT or MATCH_PARENT_NONE.
+/* DIAGNOSTICS
+/* Fatal error: out of memory, name listed under both parent wild card
+/* matching policies.
+/* SEE ALSO
+/* string_list(3) plain string matching
+/* domain_list(3) match host name patterns
+/* namadr_list(3) match host name/address patterns
+/* 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>
+
+/* Utility library. */
+
+/* Global library. */
+
+#include <string_list.h>
+#include <mail_params.h>
+#include <match_parent_style.h>
+
+/* Application-specific. */
+
+static STRING_LIST *match_par_dom_list;
+
+int match_parent_style(const char *name)
+{
+ int result;
+
+ /*
+ * Initialize on the fly.
+ */
+ if (match_par_dom_list == 0)
+ match_par_dom_list =
+ string_list_init(MATCH_FLAG_NONE, var_par_dom_match);
+
+ /*
+ * Look up the parent domain matching policy.
+ */
+ if (string_list_match(match_par_dom_list, name))
+ result = MATCH_FLAG_PARENT;
+ else
+ result = 0;
+ return (result);
+}
diff --git a/gnu/dist/postfix/src/global/match_parent_style.h b/gnu/dist/postfix/src/global/match_parent_style.h
new file mode 100644
index 00000000000..7b51eddf70a
--- /dev/null
+++ b/gnu/dist/postfix/src/global/match_parent_style.h
@@ -0,0 +1,35 @@
+#ifndef _MATCH_PARENT_STYLE_H_INCLUDED_
+#define _MATCH_PARENT_STYLE_H_INCLUDED_
+
+/*++
+/* NAME
+/* match_parent_style 3h
+/* SUMMARY
+/* parent domain matching control
+/* SYNOPSIS
+/* #include <match_parent_style.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <match_ops.h>
+
+ /*
+ * External interface.
+ */
+extern int match_parent_style(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
diff --git a/gnu/dist/postfix/src/global/namadr_list.c b/gnu/dist/postfix/src/global/namadr_list.c
index 0e6b1bba730..c5169d87e59 100644
--- a/gnu/dist/postfix/src/global/namadr_list.c
+++ b/gnu/dist/postfix/src/global/namadr_list.c
@@ -6,7 +6,8 @@
/* SYNOPSIS
/* #include <namadr_list.h>
/*
-/* NAMADR_LIST *namadr_list_init(pattern_list)
+/* NAMADR_LIST *namadr_list_init(flags, pattern_list)
+/* int flags;
/* const char *pattern_list;
/*
/* int namadr_list_match(list, name, addr)
@@ -17,6 +18,8 @@
/* void namadr_list_free(list)
/* NAMADR_LIST *list;
/* DESCRIPTION
+/* This is a convenience wrapper around the match_list module.
+/*
/* This module implements tests for list membership of a
/* hostname or network address.
/*
@@ -35,9 +38,18 @@
/* a pattern, or when any of its parent domains matches a
/* pattern. The matching process is case insensitive.
/*
-/* namadr_list_init() performs initializations. The argument
-/* is a list of patterns, or the absolute pathname of a file
-/* with patterns.
+/* namadr_list_init() performs initializations. The first
+/* argument is the bit-wise OR of zero or more of the
+/* following:
+/* .RS
+/* .IP MATCH_FLAG_PARENT
+/* The hostname pattern foo.com matches itself and any name below
+/* the domain foo.com. If this flag is cleared, foo.com matches itself
+/* only, and .foo.com matches any name below the domain foo.com.
+/* .RE
+/* Specify MATCH_FLAG_NONE to request none of the above.
+/* The second argument is a list of patterns, or the absolute
+/* pathname of a file with patterns.
/*
/* namadr_list_match() matches the specified host name and
/* address against the specified list of patterns.
@@ -67,33 +79,11 @@
/* Utility library. */
#include <match_list.h>
-#include <match_ops.h>
/* Global library. */
#include "namadr_list.h"
-/* namadr_list_init - initialize domain list */
-
-NAMADR_LIST *namadr_list_init(const char *patterns)
-{
- return (match_list_init(patterns, 2, match_hostaddr, match_hostname));
-}
-
-/* namadr_list_match - match host against set of namadr_list patterns */
-
-int namadr_list_match(NAMADR_LIST *list, const char *name, const char *addr)
-{
- return (match_list_match(list, addr, name));
-}
-
-/* namadr_list_free - release storage */
-
-void namadr_list_free(NAMADR_LIST *list)
-{
- match_list_free(list);
-}
-
#ifdef TEST
#include <msg.h>
@@ -126,7 +116,7 @@ main(int argc, char **argv)
}
if (argc != optind + 3)
usage(argv[0]);
- list = namadr_list_init(argv[optind]);
+ list = namadr_list_init(MATCH_FLAG_PARENT, argv[optind]);
host = argv[optind + 1];
addr = argv[optind + 2];
vstream_printf("%s/%s: %s\n", host, addr,
diff --git a/gnu/dist/postfix/src/global/namadr_list.h b/gnu/dist/postfix/src/global/namadr_list.h
index 186cd66c1b7..9bf90d7684d 100644
--- a/gnu/dist/postfix/src/global/namadr_list.h
+++ b/gnu/dist/postfix/src/global/namadr_list.h
@@ -7,18 +7,25 @@
/* SUMMARY
/* name/address membership
/* SYNOPSIS
-/* #include <namadr_list_list.h>
+/* #include <namadr_list.h>
/* DESCRIPTION
/* .nf
/*
+ * Utility library.
+ */
+#include <match_list.h>
+#include <match_ops.h>
+
+ /*
* External interface.
*/
-typedef struct MATCH_LIST NAMADR_LIST;
+#define NAMADR_LIST MATCH_LIST
-extern NAMADR_LIST *namadr_list_init(const char *);
-extern int namadr_list_match(NAMADR_LIST *, const char *, const char *);
-extern void namadr_list_free(NAMADR_LIST *);
+#define namadr_list_init(f, p) \
+ match_list_init((f), (p), 2, match_hostname, match_hostaddr)
+#define namadr_list_match match_list_match
+#define namadr_list_free match_list_free
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/global/off_cvt.c b/gnu/dist/postfix/src/global/off_cvt.c
index 95a689f14e1..756b63c8c60 100644
--- a/gnu/dist/postfix/src/global/off_cvt.c
+++ b/gnu/dist/postfix/src/global/off_cvt.c
@@ -27,10 +27,6 @@
/* Buffer for storage of the result of conversion to string.
/* .IP offset
/* Non-negative off_t value to be converted to string.
-/* BUGS
-/* The string to number conversion routine has no reliable way to
-/* detect an overflow error, so the result may be much smaller
-/* than the number specified in the input.
/* DIAGNOSTICS
/* Panic: negative offset
/* LICENSE
@@ -70,19 +66,28 @@ off_t off_cvt_string(const char *str)
{
int ch;
off_t result;
+ off_t res2;
+ off_t res4;
+ off_t res8;
+ off_t res10;
/*
- * We're not doing this often, so simplicity has precedence over
- * performance. XXX Need a portable way to correctly detect overflow.
- * Bear in mind that an off_t is not necessarily a long integer, so using
- * raw bit patterns is not going to be a portable solution.
+ * Multiplication by numbers > 2 can overflow without producing a smaller
+ * result mod 2^N (where N is the number of bits in the result type).
+ * (Victor Duchovni, Morgan Stanley).
*/
for (result = 0; (ch = *(unsigned char *) str) != 0; str++) {
if (!ISDIGIT(ch))
return (-1);
- result *= 10;
- result += ch - '0';
- if (result < 0)
+ if ((res2 = result + result) < result)
+ return (-1);
+ if ((res4 = res2 + res2) < res2)
+ return (-1);
+ if ((res8 = res4 + res4) < res4)
+ return (-1);
+ if ((res10 = res8 + res2) < res8)
+ return (-1);
+ if ((result = res10 + ch - '0') < res10)
return (-1);
}
return (result);
@@ -101,7 +106,7 @@ VSTRING *off_cvt_number(VSTRING *buf, off_t offset)
* Sanity checks
*/
if (offset < 0)
- msg_panic("off_cvt_number: negative offset %s",
+ msg_panic("off_cvt_number: negative offset -%s",
STR(off_cvt_number(buf, -offset)));
/*
diff --git a/gnu/dist/postfix/src/global/pipe_command.c b/gnu/dist/postfix/src/global/pipe_command.c
index 8c9615a6fd4..11f68b32544 100644
--- a/gnu/dist/postfix/src/global/pipe_command.c
+++ b/gnu/dist/postfix/src/global/pipe_command.c
@@ -33,11 +33,11 @@
/* or when it appears to be a shell built-in command, otherwise
/* the command is executed without invoking a shell.
/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
+/* See also the PIPE_CMD_SHELL attribute below.
/* .IP "PIPE_CMD_ARGV (char **)"
/* The command is specified as an argument vector. This vector is
/* passed without further inspection to the \fIexecvp\fR() routine.
/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
-/* See also the PIPE_CMD_SHELL attribute below.
/* .IP "PIPE_CMD_ENV (char **)"
/* Additional environment information, in the form of a null-terminated
/* list of name, value, name, value, ... elements. By default only the
@@ -88,8 +88,11 @@
/* The command indicated that the message was not acceptable,
/* or the command did not finish within the time limit.
/* The reason is given via the \fIwhy\fR argument.
+/* .IP PIPE_STAT_CORRUPT
+/* The queue file is corrupted.
/* SEE ALSO
/* mail_copy(3) deliver to any.
+/* mark_corrupt(3) mark queue file as corrupt.
/* sys_exits(3) sendmail-compatible exit status codes.
/* LICENSE
/* .ad
@@ -509,6 +512,8 @@ int pipe_command(VSTREAM *src, VSTRING *why,...)
log_len ? ". Command output: " : "", log_buf);
return (PIPE_STAT_BOUNCE);
}
+ } else if (write_status & MAIL_COPY_STAT_CORRUPT) {
+ return (PIPE_STAT_CORRUPT);
} else if (write_status && errno != EPIPE) {
vstring_sprintf(why, "Command failed: %m: \"%s\"", args.command);
return (PIPE_STAT_DEFER);
diff --git a/gnu/dist/postfix/src/global/pipe_command.h b/gnu/dist/postfix/src/global/pipe_command.h
index cf8d5989220..1750ccc7bd3 100644
--- a/gnu/dist/postfix/src/global/pipe_command.h
+++ b/gnu/dist/postfix/src/global/pipe_command.h
@@ -45,6 +45,7 @@
#define PIPE_STAT_OK 0 /* success */
#define PIPE_STAT_DEFER 1 /* try again */
#define PIPE_STAT_BOUNCE 2 /* failed */
+#define PIPE_STAT_CORRUPT 3 /* corrupted file */
extern int pipe_command(VSTREAM *, VSTRING *,...);
diff --git a/gnu/dist/postfix/src/global/post_mail.c b/gnu/dist/postfix/src/global/post_mail.c
index c3673243f5c..7c2fb0def1e 100644
--- a/gnu/dist/postfix/src/global/post_mail.c
+++ b/gnu/dist/postfix/src/global/post_mail.c
@@ -6,17 +6,15 @@
/* SYNOPSIS
/* #include <post_mail.h>
/*
-/* VSTREAM *post_mail_fopen(sender, recipient, flags, via)
+/* VSTREAM *post_mail_fopen(sender, recipient, flags)
/* const char *sender;
/* const char *recipient;
/* int flags;
-/* const char *via;
/*
-/* VSTREAM *post_mail_fopen_nowait(sender, recipient, flags, via)
+/* VSTREAM *post_mail_fopen_nowait(sender, recipient, flags)
/* const char *sender;
/* const char *recipient;
/* int flags;
-/* const char *via;
/*
/* int post_mail_fprintf(stream, format, ...)
/* VSTREAM *stream;
@@ -132,7 +130,7 @@
/* post_mail_init - initial negotiations */
static void post_mail_init(VSTREAM *stream, const char *sender,
- const char *recipient, int flags, const char *via)
+ const char *recipient, int flags)
{
VSTRING *id = vstring_alloc(100);
long now = time((time_t *) 0);
@@ -141,8 +139,12 @@ static void post_mail_init(VSTREAM *stream, const char *sender,
/*
* Negotiate with the cleanup service. Give up if we can't agree.
*/
- if (mail_scan(stream, "%s", id) != 1
- || mail_print(stream, "%d", flags) != 0)
+ if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
+ ATTR_TYPE_END) != 1
+ || attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
+ ATTR_TYPE_END) != 0)
msg_fatal("unable to contact the %s service", MAIL_SERVICE_CLEANUP);
/*
@@ -158,8 +160,8 @@ static void post_mail_init(VSTREAM *stream, const char *sender,
* Do the Received: and Date: header lines. This allows us to shave a few
* cycles by using the expensive date conversion result for both.
*/
- post_mail_fprintf(stream, "Received: by %s (%s) via %s",
- var_myhostname, var_mail_name, via);
+ post_mail_fprintf(stream, "Received: by %s (%s)",
+ var_myhostname, var_mail_name);
post_mail_fprintf(stream, "\tid %s; %s", vstring_str(id), date);
post_mail_fprintf(stream, "Date: %s", date);
vstring_free(id);
@@ -167,26 +169,25 @@ static void post_mail_init(VSTREAM *stream, const char *sender,
/* post_mail_fopen - prepare for posting a message */
-VSTREAM *post_mail_fopen(const char *sender, const char *recipient,
- int flags, const char *via)
+VSTREAM *post_mail_fopen(const char *sender, const char *recipient, int flags)
{
VSTREAM *stream;
- stream = mail_connect_wait(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP);
- post_mail_init(stream, sender, recipient, flags, via);
+ stream = mail_connect_wait(MAIL_CLASS_PUBLIC, MAIL_SERVICE_CLEANUP);
+ post_mail_init(stream, sender, recipient, flags);
return (stream);
}
/* post_mail_fopen_nowait - prepare for posting a message */
VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient,
- int flags, const char *via)
+ int flags)
{
VSTREAM *stream;
- if ((stream = mail_connect(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP,
+ if ((stream = mail_connect(MAIL_CLASS_PUBLIC, MAIL_SERVICE_CLEANUP,
BLOCKING)) != 0)
- post_mail_init(stream, sender, recipient, flags, via);
+ post_mail_init(stream, sender, recipient, flags);
return (stream);
}
@@ -235,7 +236,10 @@ int post_mail_fclose(VSTREAM *cleanup)
} else {
rec_fputs(cleanup, REC_TYPE_XTRA, "");
rec_fputs(cleanup, REC_TYPE_END, "");
- if (vstream_fflush(cleanup) || mail_scan(cleanup, "%d", &status) != 1)
+ if (vstream_fflush(cleanup)
+ || attr_scan(cleanup, ATTR_FLAG_MISSING,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) != 1)
status = CLEANUP_STAT_WRITE;
}
(void) vstream_fclose(cleanup);
diff --git a/gnu/dist/postfix/src/global/post_mail.h b/gnu/dist/postfix/src/global/post_mail.h
index 8fa5a80bf3b..264afc77bff 100644
--- a/gnu/dist/postfix/src/global/post_mail.h
+++ b/gnu/dist/postfix/src/global/post_mail.h
@@ -24,9 +24,8 @@
/*
* External interface.
*/
-extern VSTREAM *post_mail_fopen(const char *, const char *, int, const char *);
-extern VSTREAM *post_mail_fopen_nowait(const char *, const char *,
- int, const char *);
+extern VSTREAM *post_mail_fopen(const char *, const char *, int);
+extern VSTREAM *post_mail_fopen_nowait(const char *, const char *, int);
extern int PRINTFLIKE(2, 3) post_mail_fprintf(VSTREAM *, const char *,...);
extern int post_mail_fputs(VSTREAM *, const char *);
extern int post_mail_buffer(VSTREAM *, const char *, int);
diff --git a/gnu/dist/postfix/src/global/qmqp_proto.h b/gnu/dist/postfix/src/global/qmqp_proto.h
new file mode 100644
index 00000000000..8ad9e2aadaf
--- /dev/null
+++ b/gnu/dist/postfix/src/global/qmqp_proto.h
@@ -0,0 +1,27 @@
+/*++
+/* NAME
+/* qmqpd 3h
+/* SUMMARY
+/* QMQP protocol
+/* SYNOPSIS
+/* include <qmqpd_proto.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * QMQP protocol status codes.
+ */
+#define QMQP_STAT_OK 'K' /* success */
+#define QMQP_STAT_RETRY 'Z' /* recoverable error */
+#define QMQP_STAT_HARD 'D' /* unrecoverable error */
+
+/* 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
+/*--*/
diff --git a/gnu/dist/postfix/src/global/rec_type.c b/gnu/dist/postfix/src/global/rec_type.c
index c86a461aa14..d1e630b3709 100644
--- a/gnu/dist/postfix/src/global/rec_type.c
+++ b/gnu/dist/postfix/src/global/rec_type.c
@@ -48,7 +48,9 @@ REC_TYPE_NAME rec_type_names[] = {
REC_TYPE_FROM, "sender",
REC_TYPE_DONE, "done",
REC_TYPE_RCPT, "recipient",
+ REC_TYPE_ORCP, "original recipient",
REC_TYPE_WARN, "warning_message_time",
+ REC_TYPE_ATTR, "named attribute",
REC_TYPE_MESG, "message_content",
REC_TYPE_CONT, "unterminated",
REC_TYPE_NORM, "normal_data",
@@ -56,6 +58,7 @@ REC_TYPE_NAME rec_type_names[] = {
REC_TYPE_RRTO, "return_receipt",
REC_TYPE_ERTO, "errors_to",
REC_TYPE_PRIO, "priority",
+ REC_TYPE_VERP, "verp_delimiters",
REC_TYPE_END, "message_end",
0, 0,
};
diff --git a/gnu/dist/postfix/src/global/rec_type.h b/gnu/dist/postfix/src/global/rec_type.h
index 4412ee37da4..f1caaa466fe 100644
--- a/gnu/dist/postfix/src/global/rec_type.h
+++ b/gnu/dist/postfix/src/global/rec_type.h
@@ -33,7 +33,9 @@
#define REC_TYPE_FROM 'S' /* sender, required */
#define REC_TYPE_DONE 'D' /* delivered recipient, optional */
#define REC_TYPE_RCPT 'R' /* todo recipient, optional */
+#define REC_TYPE_ORCP 'O' /* original recipient, optional */
#define REC_TYPE_WARN 'W' /* warning message time */
+#define REC_TYPE_ATTR 'A' /* named attribute for extensions */
#define REC_TYPE_MESG 'M' /* start message records */
@@ -45,6 +47,7 @@
#define REC_TYPE_RRTO 'r' /* return-receipt, from headers */
#define REC_TYPE_ERTO 'e' /* errors-to, from headers */
#define REC_TYPE_PRIO 'P' /* priority */
+#define REC_TYPE_VERP 'V' /* VERP delimiters */
#define REC_TYPE_END 'E' /* terminator, required */
@@ -53,9 +56,9 @@
* record groups. The first member in each set is the record type that
* indicates the end of that record group.
*/
-#define REC_TYPE_ENVELOPE "MCTFILSDRW"
+#define REC_TYPE_ENVELOPE "MCTFILSDROWVA"
#define REC_TYPE_CONTENT "XLN"
-#define REC_TYPE_EXTRACT "EDRPre"
+#define REC_TYPE_EXTRACT "EDROPre" /* NOT A */
#define REC_TYPE_NOEXTRACT "E"
/*
diff --git a/gnu/dist/postfix/src/global/recdump.c b/gnu/dist/postfix/src/global/recdump.c
index a02d1755e5d..74ff7ea02e0 100644
--- a/gnu/dist/postfix/src/global/recdump.c
+++ b/gnu/dist/postfix/src/global/recdump.c
@@ -44,7 +44,8 @@ int main(int unused_argc, char **argv)
msg_vstream_init(argv[0], VSTREAM_OUT);
while (offset = vstream_ftell(VSTREAM_IN),
- (type = rec_get(VSTREAM_IN, buf, 0)) > 0) {
+ ((type = rec_get(VSTREAM_IN, buf, 0)) != REC_TYPE_EOF
+ && type != REC_TYPE_ERROR)) {
vstream_fprintf(VSTREAM_OUT, "%15s|%4ld|%3d|%s\n",
rec_type_name(type), offset,
VSTRING_LEN(buf), vstring_str(buf));
diff --git a/gnu/dist/postfix/src/global/resolve_clnt.c b/gnu/dist/postfix/src/global/resolve_clnt.c
index ff27504a627..56382876541 100644
--- a/gnu/dist/postfix/src/global/resolve_clnt.c
+++ b/gnu/dist/postfix/src/global/resolve_clnt.c
@@ -156,13 +156,20 @@ void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply)
for (;;) {
stream = clnt_stream_access(rewrite_clnt_stream);
- if (mail_print(stream, "%s %s", RESOLVE_ADDR, addr)
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, RESOLVE_ADDR,
+ ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
+ ATTR_TYPE_END)
|| vstream_fflush(stream)) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: bad write: %m", myname);
- } else if (mail_scan(stream, "%s %s %s %d",
- reply->transport, reply->nexthop,
- reply->recipient, &reply->flags) != 4) {
+ } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_TRANSPORT, reply->transport,
+ ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, reply->nexthop,
+
+ ATTR_TYPE_STR, MAIL_ATTR_RECIP, reply->recipient,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &reply->flags,
+ ATTR_TYPE_END) != 4) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: bad read: %m", myname);
} else {
diff --git a/gnu/dist/postfix/src/global/rewrite_clnt.c b/gnu/dist/postfix/src/global/rewrite_clnt.c
index ba9049ad3b6..886dc4922eb 100644
--- a/gnu/dist/postfix/src/global/rewrite_clnt.c
+++ b/gnu/dist/postfix/src/global/rewrite_clnt.c
@@ -123,11 +123,17 @@ VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result)
for (;;) {
stream = clnt_stream_access(rewrite_clnt_stream);
- if (mail_print(stream, "%s %s %s", REWRITE_ADDR, rule, addr),
+ if (attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, REWRITE_ADDR,
+ ATTR_TYPE_STR, MAIL_ATTR_RULE, rule,
+ ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
+ ATTR_TYPE_END),
vstream_fflush(stream)) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: bad write: %m", myname);
- } else if (mail_scan(stream, "%s", result) != 1) {
+ } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_ADDR, result,
+ ATTR_TYPE_END) != 1) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: bad read: %m", myname);
} else {
diff --git a/gnu/dist/postfix/src/global/smtp_stream.c b/gnu/dist/postfix/src/global/smtp_stream.c
index 606ffb65fc0..751e92aa6ae 100644
--- a/gnu/dist/postfix/src/global/smtp_stream.c
+++ b/gnu/dist/postfix/src/global/smtp_stream.c
@@ -200,6 +200,9 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, int bound)
* Allow for partial long lines (we will read the remainder later) and
* allow for lines ending in bare LF. The idea is to be liberal in what
* we accept, strict in what we send.
+ *
+ * XXX 2821: Section 4.1.1.4 says that an SMTP server must not recognize
+ * bare LF as record terminator.
*/
smtp_timeout_reset(stream);
last_char = (bound == 0 ? vstring_get(vp, stream) :
@@ -224,11 +227,13 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, int bound)
/*
* Strip off the record terminator: either CRLF or just bare LF.
+ *
+ * XXX RFC 2821 disallows sending bare CR everywhere. We remove bare CR
+ * if received before CRLF, and leave it alone otherwise.
*/
case '\n':
- if (VSTRING_LEN(vp) > 1 && vstring_end(vp)[-2] == '\r')
- vstring_truncate(vp, VSTRING_LEN(vp) - 2);
- else
+ vstring_truncate(vp, VSTRING_LEN(vp) - 1);
+ while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
vstring_truncate(vp, VSTRING_LEN(vp) - 1);
VSTRING_TERMINATE(vp);
diff --git a/gnu/dist/postfix/src/global/string_list.c b/gnu/dist/postfix/src/global/string_list.c
index bcb457e77a7..522ff26a1df 100644
--- a/gnu/dist/postfix/src/global/string_list.c
+++ b/gnu/dist/postfix/src/global/string_list.c
@@ -6,7 +6,8 @@
/* SYNOPSIS
/* #include <string_list.h>
/*
-/* STRING_LIST *string_list_init(pattern_list)
+/* STRING_LIST *string_list_init(flags, pattern_list)
+/* int flags;
/* const char *pattern_list;
/*
/* int string_list_match(list, name)
@@ -16,6 +17,8 @@
/* void string_list_free(list)
/* STRING_LIST *list;
/* DESCRIPTION
+/* This is a convenience wrapper around the match_list module.
+/*
/* This module implements tests for list membership of a string.
/*
/* Patterns are separated by whitespace and/or commas. A pattern
@@ -28,8 +31,8 @@
/* In order to reverse the result, precede a non-file name pattern
/* with an exclamation point (!).
/*
-/* string_list_init() performs initializations. The argument is a
-/* list of string patterns.
+/* string_list_init() performs initializations. The flags argument
+/* is ignored; pattern_list specifies a list of string patterns.
/*
/* string_list_match() matches the specified string against the
/* compiled pattern list.
@@ -58,33 +61,11 @@
/* Utility library. */
#include <match_list.h>
-#include <match_ops.h>
/* Global library. */
#include "string_list.h"
-/* string_list_init - initialize string list */
-
-STRING_LIST *string_list_init(const char *patterns)
-{
- return (match_list_init(patterns, 1, match_string));
-}
-
-/* string_list_match - match string against list */
-
-int string_list_match(STRING_LIST * list, const char *string)
-{
- return (match_list_match(list, string));
-}
-
-/* string_list_free - release storage */
-
-void string_list_free(STRING_LIST * list)
-{
- match_list_free(list);
-}
-
#ifdef TEST
#include <msg.h>
@@ -117,7 +98,7 @@ main(int argc, char **argv)
}
if (argc != optind + 2)
usage(argv[0]);
- list = string_list_init(argv[optind]);
+ list = string_list_init(MATCH_FLAG_NONE, argv[optind]);
string = argv[optind + 1];
vstream_printf("%s: %s\n", string, string_list_match(list, string) ?
"YES" : "NO");
diff --git a/gnu/dist/postfix/src/global/string_list.h b/gnu/dist/postfix/src/global/string_list.h
index 946ecf1d296..daa9541d2f5 100644
--- a/gnu/dist/postfix/src/global/string_list.h
+++ b/gnu/dist/postfix/src/global/string_list.h
@@ -12,13 +12,19 @@
/* .nf
/*
+ * Utility library.
+ */
+#include <match_list.h>
+#include <match_ops.h>
+
+ /*
* External interface.
*/
-typedef struct MATCH_LIST STRING_LIST;
+#define STRING_LIST MATCH_LIST
-extern STRING_LIST *string_list_init(const char *);
-extern int string_list_match(STRING_LIST *, const char *);
-extern void string_list_free(STRING_LIST *);
+#define string_list_init(f, p) match_list_init((f), (p), 1, match_string)
+#define string_list_match match_list_match
+#define string_list_free match_list_free
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/global/tok822.h b/gnu/dist/postfix/src/global/tok822.h
index c0dd10f5c58..316dfb19821 100644
--- a/gnu/dist/postfix/src/global/tok822.h
+++ b/gnu/dist/postfix/src/global/tok822.h
@@ -45,8 +45,7 @@ typedef struct TOK822 {
#define TOK822_DOMLIT 259 /* stuff between [] not nesting */
#define TOK822_ADDR 260 /* actually a token group */
#define TOK822_STARTGRP 261 /* start of named group */
-#define TOK822_COMMENT_TEXT 262 /* comment text */
-#define TOK822_MAXTOK 262
+#define TOK822_MAXTOK 261
/*
* tok822_node.c
diff --git a/gnu/dist/postfix/src/global/tok822_node.c b/gnu/dist/postfix/src/global/tok822_node.c
index 797deb3424e..d39bf15b94f 100644
--- a/gnu/dist/postfix/src/global/tok822_node.c
+++ b/gnu/dist/postfix/src/global/tok822_node.c
@@ -57,7 +57,7 @@ TOK822 *tok822_alloc(int type, const char *strval)
TOK822 *tp;
#define CONTAINER_TOKEN(x) \
- ((x) == TOK822_ADDR || (x) == TOK822_COMMENT || (x) == TOK822_STARTGRP)
+ ((x) == TOK822_ADDR || (x) == TOK822_STARTGRP)
tp = (TOK822 *) mymalloc(sizeof(*tp));
tp->type = type;
diff --git a/gnu/dist/postfix/src/global/tok822_parse.ref b/gnu/dist/postfix/src/global/tok822_parse.ref
index dc35bff57b7..169957ac40e 100644
--- a/gnu/dist/postfix/src/global/tok822_parse.ref
+++ b/gnu/dist/postfix/src/global/tok822_parse.ref
@@ -86,8 +86,7 @@ Parse tree:
OP ","
address
atom "venema"
- comment
- text ""wietse "
+ comment "("wietse )"
OP ","
address
quoted string ")"
@@ -135,11 +134,7 @@ Parse tree:
OP "."
atom "org"
OP ")"
- comment
- text " "
- comment
- text ""wietse "
- text " venema""
+ comment "( ("wietse ) venema")"
Internalized:
wietse venema@porcupine.org) ( ("wietse ) venema")
@@ -272,9 +267,8 @@ Parse tree:
atom "wietse"
OP "@"
atom "foo"
- comment
- text "wietse
- venema"
+ comment "(wietse
+ venema)"
Internalized:
wietse@foo (wietse
diff --git a/gnu/dist/postfix/src/global/verp_sender.c b/gnu/dist/postfix/src/global/verp_sender.c
new file mode 100644
index 00000000000..b7c0f806e06
--- /dev/null
+++ b/gnu/dist/postfix/src/global/verp_sender.c
@@ -0,0 +1,106 @@
+/*++
+/* NAME
+/* verp_sender 3
+/* SUMMARY
+/* quote local part of mailbox
+/* SYNOPSIS
+/* #include <verp_sender.h>
+/*
+/* VSTRING *verp_sender(dst, delims, sender, recipient)
+/* VSTRING *dst;
+/* const char *delims;
+/* const char *sender;
+/* const char *recipient;
+/*
+/* const char *verp_delims_verify(delims)
+/* const char *delims;
+/* DESCRIPTION
+/* verp_sender() encodes the recipient address in the sender
+/* address, using the specified delimiters. For example,
+/* with delims +=, sender \fIprefix@origin\fR, and
+/* recipient \fIuser@domain\fR the result is
+/* \fIprefix+user=domain@origin\fR.
+/*
+/* verp_delims_verify() determines if the specified VERP delimiters
+/* have reasonable values. What is reasonable is configured with
+/* the verp_delimiter_filter configuration parameter. The result
+/* is null in case of success, a description of the problem in
+/* case of error.
+/*
+/* Arguments:
+/* .IP dst
+/* The result. The buffer is null terminated.
+/* .IP delims
+/* VERP formatting characters.
+/* .IP sender
+/* Sender envelope address.
+/* .IP recipient
+/* Recipient envelope address.
+/* 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 <vstring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <verp_sender.h>
+
+/* verp_sender - encode recipient into envelope sender address */
+
+VSTRING *verp_sender(VSTRING *buf, const char *delimiters,
+ const char *sender, const char *recipient)
+{
+ int send_local_len;
+ int rcpt_local_len;
+ const char *cp;
+
+ /*
+ * Change prefix@origin into prefix+user=domain@origin.
+ */
+ send_local_len = ((cp = strrchr(sender, '@')) ?
+ cp - sender : strlen(sender));
+ rcpt_local_len = ((cp = strrchr(recipient, '@')) ?
+ cp - recipient : strlen(recipient));
+ vstring_strncpy(buf, sender, send_local_len);
+ VSTRING_ADDCH(buf, delimiters[0] & 0xff);
+ vstring_strncat(buf, recipient, rcpt_local_len);
+ if (recipient[rcpt_local_len] && recipient[rcpt_local_len + 1]) {
+ VSTRING_ADDCH(buf, delimiters[1] & 0xff);
+ vstring_strcat(buf, recipient + rcpt_local_len + 1);
+ }
+ if (sender[send_local_len] && sender[send_local_len + 1]) {
+ VSTRING_ADDCH(buf, '@');
+ vstring_strcat(buf, sender + send_local_len + 1);
+ }
+ VSTRING_TERMINATE(buf);
+ return (buf);
+}
+
+/* verp_delims_verify - sanitize VERP delimiters */
+
+const char *verp_delims_verify(const char *delims)
+{
+ if (strlen(delims) != 2)
+ return ("bad VERP delimiter character count");
+ if (strchr(var_verp_filter, delims[0]) == 0)
+ return ("bad first VERP delimiter character");
+ if (strchr(var_verp_filter, delims[1]) == 0)
+ return ("bad second VERP delimiter character");
+ return (0);
+}
diff --git a/gnu/dist/postfix/src/global/verp_sender.h b/gnu/dist/postfix/src/global/verp_sender.h
new file mode 100644
index 00000000000..61641ac8a14
--- /dev/null
+++ b/gnu/dist/postfix/src/global/verp_sender.h
@@ -0,0 +1,36 @@
+#ifndef _VERP_SENDER_H_INCLUDED_
+#define _VERP_SENDER_H_INCLUDED_
+
+/*++
+/* NAME
+/* verp_sender 3h
+/* SUMMARY
+/* encode recipient into sender, VERP style
+/* SYNOPSIS
+/* #include "verp_sender.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * External interface.
+ */
+extern VSTRING *verp_sender(VSTRING *, const char *, const char *, const char *);
+extern const char *verp_delims_verify(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
diff --git a/gnu/dist/postfix/src/lmtp/Makefile.in b/gnu/dist/postfix/src/lmtp/Makefile.in
index 9821cf6af48..cb8f5637fd9 100644
--- a/gnu/dist/postfix/src/lmtp/Makefile.in
+++ b/gnu/dist/postfix/src/lmtp/Makefile.in
@@ -132,6 +132,7 @@ lmtp_connect.o: ../../include/timed_connect.h
lmtp_connect.o: ../../include/stringops.h
lmtp_connect.o: ../../include/mail_params.h
lmtp_connect.o: ../../include/mail_proto.h
+lmtp_connect.o: ../../include/attr.h
lmtp_connect.o: ../../include/dns.h
lmtp_connect.o: lmtp.h
lmtp_connect.o: ../../include/argv.h
@@ -175,6 +176,8 @@ lmtp_sasl_glue.o: ../../include/split_at.h
lmtp_sasl_glue.o: ../../include/name_mask.h
lmtp_sasl_glue.o: ../../include/mail_params.h
lmtp_sasl_glue.o: ../../include/string_list.h
+lmtp_sasl_glue.o: ../../include/match_list.h
+lmtp_sasl_glue.o: ../../include/match_ops.h
lmtp_sasl_glue.o: ../../include/maps.h
lmtp_sasl_glue.o: ../../include/dict.h
lmtp_sasl_glue.o: ../../include/vstream.h
diff --git a/gnu/dist/postfix/src/lmtp/lmtp.c b/gnu/dist/postfix/src/lmtp/lmtp.c
index 8a1560c5350..17a5e9c598d 100644
--- a/gnu/dist/postfix/src/lmtp/lmtp.c
+++ b/gnu/dist/postfix/src/lmtp/lmtp.c
@@ -50,6 +50,7 @@
/* RFC 2033 (LMTP protocol)
/* RFC 2197 (Pipelining)
/* RFC 2554 (AUTH command)
+/* RFC 2821 (SMTP protocol)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* Corrupted message files are marked so that the queue manager can
@@ -87,7 +88,7 @@
/* The TCP port to be used when connecting to a LMTP server. Used as
/* backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
/* .SH "Authentication controls"
-/* .IP \fBlmtp_enable_sasl_auth\fR
+/* .IP \fBlmtp_sasl_auth_enable\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
/* By default, Postfix is built without SASL support.
/* .IP \fBlmtp_sasl_password_maps\fR
@@ -226,8 +227,6 @@
#include <string.h>
#include <fcntl.h>
#include <dict.h>
-#include <pwd.h>
-#include <grp.h>
/* Utility library. */
@@ -269,8 +268,6 @@ int var_lmtp_data0_tmout;
int var_lmtp_data1_tmout;
int var_lmtp_data2_tmout;
int var_lmtp_quit_tmout;
-char *var_debug_peer_list;
-int var_debug_peer_level;
int var_lmtp_cache_conn;
int var_lmtp_skip_quit_resp;
char *var_notify_classes;
@@ -512,7 +509,6 @@ static void pre_accept(char *unused_name, char **unused_argv)
int main(int argc, char **argv)
{
static CONFIG_STR_TABLE str_table[] = {
- VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0,
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
VAR_LMTP_SASL_PASSWD, DEF_LMTP_SASL_PASSWD, &var_lmtp_sasl_passwd, 0, 0,
@@ -521,7 +517,6 @@ int main(int argc, char **argv)
};
static CONFIG_INT_TABLE int_table[] = {
VAR_LMTP_TCP_PORT, DEF_LMTP_TCP_PORT, &var_lmtp_tcp_port, 0, 0,
- VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
0,
};
static CONFIG_TIME_TABLE time_table[] = {
diff --git a/gnu/dist/postfix/src/lmtp/lmtp.h b/gnu/dist/postfix/src/lmtp/lmtp.h
index 9574b648ac0..419cb38d5b0 100644
--- a/gnu/dist/postfix/src/lmtp/lmtp.h
+++ b/gnu/dist/postfix/src/lmtp/lmtp.h
@@ -61,7 +61,7 @@ typedef struct LMTP_STATE {
#define LMTP_FEATURE_8BITMIME (1<<1)
#define LMTP_FEATURE_PIPELINING (1<<2)
#define LMTP_FEATURE_SIZE (1<<3)
-#define SMTP_FEATURE_AUTH (1<<5)
+#define LMTP_FEATURE_AUTH (1<<5)
/*
* lmtp.c
diff --git a/gnu/dist/postfix/src/lmtp/lmtp_chat.c b/gnu/dist/postfix/src/lmtp/lmtp_chat.c
index 19b37cc03c4..40f5947165a 100644
--- a/gnu/dist/postfix/src/lmtp/lmtp_chat.c
+++ b/gnu/dist/postfix/src/lmtp/lmtp_chat.c
@@ -268,7 +268,7 @@ void lmtp_chat_notify(LMTP_STATE *state)
notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
var_error_rcpt,
- NULL_CLEANUP_FLAGS, "NOTICE");
+ NULL_CLEANUP_FLAGS);
if (notice == 0) {
msg_warn("postmaster notify: %m");
return;
diff --git a/gnu/dist/postfix/src/lmtp/lmtp_proto.c b/gnu/dist/postfix/src/lmtp/lmtp_proto.c
index 1c970dad728..7cf98f77cc6 100644
--- a/gnu/dist/postfix/src/lmtp/lmtp_proto.c
+++ b/gnu/dist/postfix/src/lmtp/lmtp_proto.c
@@ -218,7 +218,7 @@ int lmtp_lhlo(LMTP_STATE *state)
lines = resp->str;
(void) mystrtok(&lines, "\n");
while ((words = mystrtok(&lines, "\n")) != 0) {
- if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
+ if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t=")) != 0) {
if (strcasecmp(word, "8BITMIME") == 0)
state->features |= LMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
@@ -235,7 +235,7 @@ int lmtp_lhlo(LMTP_STATE *state)
msg_info("server features: 0x%x", state->features);
#ifdef USE_SASL_AUTH
- if (var_lmtp_sasl_enable && (state->features & SMTP_FEATURE_AUTH))
+ if (var_lmtp_sasl_enable && (state->features & LMTP_FEATURE_AUTH))
return (lmtp_sasl_helo_login(state));
#endif
@@ -489,9 +489,19 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state)
* rejected, ignore RCPT TO responses: all recipients are
* dead already. When all recipients are rejected the
* receiver may apply a course correction.
+ *
+ * XXX 2821: Section 4.5.3.1 says that a 552 RCPT TO reply
+ * must be treated as if the server replied with 452.
+ * However, this causes "too much mail data" to be
+ * treated as a recoverable error, which is wrong. I'll
+ * stick with RFC 821.
*/
case LMTP_STATE_RCPT:
if (!mail_from_rejected) {
+#ifdef notdef
+ if (resp->code == 552)
+ resp->code = 452;
+#endif
rcpt = request->rcpt_list.info + recv_rcpt;
if (resp->code / 100 == 2) {
if (survivors == 0)
diff --git a/gnu/dist/postfix/src/lmtp/lmtp_sasl_proto.c b/gnu/dist/postfix/src/lmtp/lmtp_sasl_proto.c
index 8eaeafd1489..b8c280c019c 100644
--- a/gnu/dist/postfix/src/lmtp/lmtp_sasl_proto.c
+++ b/gnu/dist/postfix/src/lmtp/lmtp_sasl_proto.c
@@ -14,14 +14,14 @@
/* LMTP_STATE *state;
/* DESCRIPTION
/* This module contains random chunks of code that implement
-/* the SMTP protocol interface for SASL negotiation. The goal
+/* the LMTP protocol interface for SASL negotiation. The goal
/* is to reduce clutter in the main LMTP client source code.
/*
/* lmtp_sasl_helo_auth() processes the AUTH option in the
-/* SMTP server's EHLO response.
+/* LMTP server's LHLO response.
/*
/* lmtp_sasl_helo_login() authenticates the LMTP client to the
-/* SMTP server, using the authentication mechanism information
+/* LMTP server, using the authentication mechanism information
/* given by the server. The result is a Postfix delivery status
/* code in case of trouble.
/*
@@ -52,6 +52,10 @@
/* System library. */
#include <sys_defs.h>
+#include <string.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
/* Utility library. */
@@ -79,15 +83,17 @@ void lmtp_sasl_helo_auth(LMTP_STATE *state, const char *words)
* then pretend that the server doesn't support SASL authentication.
*/
if (state->sasl_mechanism_list) {
+ if (strcasecmp(state->sasl_mechanism_list, words) == 0)
+ return;
myfree(state->sasl_mechanism_list);
msg_warn("%s offered AUTH option multiple times",
state->session->namaddr);
state->sasl_mechanism_list = 0;
- state->features &= ~SMTP_FEATURE_AUTH;
+ state->features &= ~LMTP_FEATURE_AUTH;
}
if (strlen(words) > 0) {
state->sasl_mechanism_list = mystrdup(words);
- state->features |= SMTP_FEATURE_AUTH;
+ state->features |= LMTP_FEATURE_AUTH;
} else {
msg_warn("%s offered null AUTH mechanism list",
state->session->namaddr);
diff --git a/gnu/dist/postfix/src/local/Makefile.in b/gnu/dist/postfix/src/local/Makefile.in
index ff36cf9bf65..fc6a4ae2d76 100644
--- a/gnu/dist/postfix/src/local/Makefile.in
+++ b/gnu/dist/postfix/src/local/Makefile.in
@@ -213,6 +213,7 @@ forward.o: ../../include/vstring_vstream.h
forward.o: ../../include/iostuff.h
forward.o: ../../include/stringops.h
forward.o: ../../include/mail_proto.h
+forward.o: ../../include/attr.h
forward.o: ../../include/cleanup_user.h
forward.o: ../../include/sent.h
forward.o: ../../include/record.h
@@ -334,13 +335,16 @@ mailbox.o: ../../include/deliver_request.h
mailbox.o: ../../include/recipient_list.h
mailbox.o: ../../include/mail_proto.h
mailbox.o: ../../include/iostuff.h
+mailbox.o: ../../include/attr.h
mailbox.o: ../../include/mbox_open.h
mailbox.o: ../../include/safe_open.h
+mailbox.o: ../../include/maps.h
+mailbox.o: ../../include/dict.h
+mailbox.o: ../../include/argv.h
mailbox.o: local.h
mailbox.o: ../../include/tok822.h
mailbox.o: ../../include/resolve_clnt.h
mailbox.o: ../../include/mbox_conf.h
-mailbox.o: ../../include/argv.h
mailbox.o: biff_notify.h
maildir.o: maildir.c
maildir.o: ../../include/sys_defs.h
@@ -403,6 +407,7 @@ resolve.o: ../../include/htable.h
resolve.o: ../../include/mail_proto.h
resolve.o: ../../include/vstream.h
resolve.o: ../../include/iostuff.h
+resolve.o: ../../include/attr.h
resolve.o: ../../include/resolve_clnt.h
resolve.o: ../../include/rewrite_clnt.h
resolve.o: ../../include/tok822.h
@@ -446,6 +451,7 @@ unknown.o: ../../include/mail_params.h
unknown.o: ../../include/mail_proto.h
unknown.o: ../../include/vstream.h
unknown.o: ../../include/iostuff.h
+unknown.o: ../../include/attr.h
unknown.o: ../../include/bounce.h
unknown.o: local.h
unknown.o: ../../include/htable.h
diff --git a/gnu/dist/postfix/src/local/alias.c b/gnu/dist/postfix/src/local/alias.c
index 69e0b4e7991..fe1eb46fd7d 100644
--- a/gnu/dist/postfix/src/local/alias.c
+++ b/gnu/dist/postfix/src/local/alias.c
@@ -135,7 +135,8 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
struct mypasswd *alias_pwd;
VSTRING *canon_owner;
DICT *dict;
- const char *owner_rhs; /* owner alias, RHS */
+ const char *owner_rhs; /* owner alias, RHS */
+ int alias_count;
/*
* Make verbose logging easier to understand.
@@ -171,7 +172,7 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
if (state.level > 100) {
msg_warn("possible alias database loop for %s", name);
*statusp = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
- "possible alias database loop for %s", name);
+ "possible alias database loop for %s", name);
return (YES);
}
state.msg_attr.exp_from = name;
@@ -271,11 +272,19 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
/*
* Deliver.
*/
+ alias_count = 0;
*statusp =
(dict_errno ?
defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
"alias database unavailable") :
- deliver_token_string(state, usr_attr, expansion, (int *) 0));
+ deliver_token_string(state, usr_attr, expansion, &alias_count));
+#if 0
+ if (var_ownreq_special
+ && strncmp("owner-", state.msg_attr.sender, 6) != 0
+ && alias_count > 10)
+ msg_warn("mailing list \"%s\" needs an \"owner-%s\" alias",
+ name, name);
+#endif
myfree(expansion);
if (owner)
myfree(owner);
diff --git a/gnu/dist/postfix/src/local/command.c b/gnu/dist/postfix/src/local/command.c
index 23fffdf2ff3..8a86ac63a05 100644
--- a/gnu/dist/postfix/src/local/command.c
+++ b/gnu/dist/postfix/src/local/command.c
@@ -9,7 +9,7 @@
/* int deliver_command(state, usr_attr, command)
/* LOCAL_STATE state;
/* USER_ATTR exp_attr;
-/* char *command;
+/* const char *command;
/* DESCRIPTION
/* deliver_command() runs a command with a message as standard
/* input. A limited amount of standard output and standard error
@@ -79,7 +79,7 @@
/* deliver_command - deliver to shell command */
-int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command)
+int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command)
{
char *myname = "deliver_command";
VSTRING *why;
@@ -107,16 +107,6 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command)
return (0);
/*
- * DELIVERY POLICY
- *
- * Do we permit mail to shell commands? Allow delivery via mailbox_command.
- */
- if (command != var_mailbox_command
- && (local_cmd_deliver_mask & state.msg_attr.exp_type) == 0)
- return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
- "mail to command is restricted"));
-
- /*
* DELIVERY RIGHTS
*
* Choose a default uid and gid when none have been selected (i.e. values
@@ -205,6 +195,9 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command)
BOUNCE_ATTR(state.msg_attr),
"%s", vstring_str(why));
break;
+ case PIPE_STAT_CORRUPT:
+ deliver_status = DEL_STAT_DEFER;
+ break;
default:
msg_panic("%s: bad status %d", myname, cmd_status);
/* NOTREACHED */
diff --git a/gnu/dist/postfix/src/local/file.c b/gnu/dist/postfix/src/local/file.c
index e3a530ba8a1..96663d3c41e 100644
--- a/gnu/dist/postfix/src/local/file.c
+++ b/gnu/dist/postfix/src/local/file.c
@@ -86,7 +86,8 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
struct stat st;
MBOX *mp;
VSTRING *why;
- int status = -1;
+ int mail_copy_status = MAIL_COPY_STAT_WRITE;
+ int deliver_status;
int copy_flags;
/*
@@ -157,10 +158,10 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
vstring_sprintf(why, "destination file is executable");
errno = 0;
} else {
- status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
- S_ISREG(st.st_mode) ? copy_flags :
- (copy_flags & ~MAIL_COPY_TOFILE),
- "\n", why);
+ mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
+ S_ISREG(st.st_mode) ? copy_flags :
+ (copy_flags & ~MAIL_COPY_TOFILE),
+ "\n", why);
}
mbox_release(mp);
}
@@ -169,19 +170,21 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
/*
* As the mail system, bounce, defer delivery, or report success.
*/
- if (status != 0) {
- status = (errno == EAGAIN || errno == ENOSPC ?
- defer_append : bounce_append)
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ deliver_status = (errno == EAGAIN || errno == ENOSPC || errno == ESTALE ?
+ defer_append : bounce_append)
(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
"cannot append message to destination file %s: %s",
path, STR(why));
} else {
- sent(SENT_ATTR(state.msg_attr), "%s", path);
+ deliver_status = sent(SENT_ATTR(state.msg_attr), "%s", path);
}
/*
* Clean up.
*/
vstring_free(why);
- return (status);
+ return (deliver_status);
}
diff --git a/gnu/dist/postfix/src/local/forward.c b/gnu/dist/postfix/src/local/forward.c
index 91f239348d6..5efbf181341 100644
--- a/gnu/dist/postfix/src/local/forward.c
+++ b/gnu/dist/postfix/src/local/forward.c
@@ -120,12 +120,18 @@ static FORWARD_INFO *forward_open(char *sender)
* Contact the cleanup service and save the new mail queue id. Request
* that the cleanup service bounces bad messages to the sender so that we
* can avoid the trouble of bounce management.
+ *
+ * In case you wonder what kind of bounces, examples are "too many hops",
+ * "message too large", perhaps some others. The reason not to bounce
+ * ourselves is that we don't really know who the recipients are.
*/
- cleanup = mail_connect(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP, BLOCKING);
+ cleanup = mail_connect(MAIL_CLASS_PUBLIC, MAIL_SERVICE_CLEANUP, BLOCKING);
if (cleanup == 0)
return (0);
close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC);
- if (mail_scan(cleanup, "%s", buffer) != 1) {
+ if (attr_scan(cleanup, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, buffer,
+ ATTR_TYPE_END) != 1) {
vstream_fclose(cleanup);
return (0);
}
@@ -133,7 +139,9 @@ static FORWARD_INFO *forward_open(char *sender)
info->cleanup = cleanup;
info->queue_id = mystrdup(vstring_str(buffer));
info->posting_time = time((time_t *) 0);
- mail_print(cleanup, "%d", CLEANUP_FLAG_BOUNCE);
+ attr_print(cleanup, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, CLEANUP_FLAG_BOUNCE,
+ ATTR_TYPE_END);
/*
* Send initial message envelope information. For bounces, set the
@@ -233,7 +241,9 @@ static int forward_send(FORWARD_INFO *info, DELIVER_ATTR attr, char *delivered)
*/
if (status == 0)
if (vstream_fflush(info->cleanup)
- || mail_scan(info->cleanup, "%d", &status) != 1)
+ || attr_scan(info->cleanup, ATTR_FLAG_MISSING,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) != 1)
status = 1;
/*
diff --git a/gnu/dist/postfix/src/local/local.c b/gnu/dist/postfix/src/local/local.c
index 931b2c98f3d..dc13ca16677 100644
--- a/gnu/dist/postfix/src/local/local.c
+++ b/gnu/dist/postfix/src/local/local.c
@@ -84,7 +84,8 @@
/* The default per-user mailbox is a file in the UNIX mail spool
/* directory (\fB/var/mail/\fIuser\fR or \fB/var/spool/mail/\fIuser\fR);
/* the location can be specified with the \fBmail_spool_directory\fR
-/* configuration parameter.
+/* configuration parameter. Specify a name ending in \fB/\fR for
+/* \fBqmail\fR-compatible \fBmaildir\fR delivery.
/*
/* Alternatively, the per-user mailbox can be a file in the user's home
/* directory with a name specified via the \fBhome_mailbox\fR
@@ -177,13 +178,16 @@
/* EXTERNAL FILE DELIVERY
/* .ad
/* .fi
+/* The delivery format depends on the destination filename syntax.
+/* The default is to use UNIX-style mailbox format. Specify a name
+/* ending in \fB/\fR for \fBqmail\fR-compatible \fBmaildir\fR delivery.
+/*
/* The \fBallow_mail_to_files\fR configuration parameter restricts
/* delivery to external files. The default setting (\fBalias,
/* forward\fR) forbids file destinations in \fB:include:\fR files.
-/* Specify a pathname ending in \fB/\fR for \fBqmail\fR-compatible
-/* \fBmaildir\fR delivery.
/*
-/* The \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR"
+/* In the case of UNIX-style mailbox delivery,
+/* the \fBlocal\fR daemon prepends a "\fBFrom \fIsender time_stamp\fR"
/* envelope header to each message, prepends an
/* optional \fBDelivered-To:\fR
/* header with the recipient envelope address, prepends a \fB>\fR
@@ -303,10 +307,14 @@
/* .IP \fBmail_spool_directory\fR
/* Directory with UNIX-style mailboxes. The default pathname is system
/* dependent.
+/* Specify a path ending in \fB/\fR for maildir-style delivery.
/* .IP \fBmailbox_command\fR
/* External command to use for mailbox delivery. The command executes
/* with the recipient privileges (exception: root). The string is subject
/* to $name expansions.
+/* .IP \fBmailbox_command_maps\fR
+/* Lookup tables with per-recipient external commands to use for mailbox
+/* delivery. Behavior is as with \fBmailbox_command\fR.
/* .IP \fBmailbox_transport\fR
/* Message transport to use for mailbox delivery to all local
/* recipients, whether or not they are found in the UNIX passwd database.
@@ -446,6 +454,7 @@ int var_dup_filter_limit;
int var_command_maxtime;
char *var_home_mailbox;
char *var_mailbox_command;
+char *var_mailbox_cmd_maps;
char *var_rcpt_fdelim;
char *var_local_cmd_shell;
char *var_luser_relay;
@@ -695,6 +704,7 @@ int main(int argc, char **argv)
static CONFIG_RAW_TABLE raw_table[] = {
VAR_FORWARD_PATH, DEF_FORWARD_PATH, &var_forward_path, 0, 0,
VAR_MAILBOX_COMMAND, DEF_MAILBOX_COMMAND, &var_mailbox_command, 0, 0,
+ VAR_MAILBOX_CMD_MAPS, DEF_MAILBOX_CMD_MAPS, &var_mailbox_cmd_maps, 0, 0,
VAR_LUSER_RELAY, DEF_LUSER_RELAY, &var_luser_relay, 0, 0,
0,
};
diff --git a/gnu/dist/postfix/src/local/local.h b/gnu/dist/postfix/src/local/local.h
index 4843179e0d5..302ea541973 100644
--- a/gnu/dist/postfix/src/local/local.h
+++ b/gnu/dist/postfix/src/local/local.h
@@ -152,7 +152,7 @@ extern int deliver_resolve_addr(LOCAL_STATE, USER_ATTR, char *);
* "leaf" nodes of the delivery graph.
*/
extern int deliver_mailbox(LOCAL_STATE, USER_ATTR, int *);
-extern int deliver_command(LOCAL_STATE, USER_ATTR, char *);
+extern int deliver_command(LOCAL_STATE, USER_ATTR, const char *);
extern int deliver_file(LOCAL_STATE, USER_ATTR, char *);
extern int deliver_indirect(LOCAL_STATE);
extern int deliver_maildir(LOCAL_STATE, USER_ATTR, char *);
diff --git a/gnu/dist/postfix/src/local/mailbox.c b/gnu/dist/postfix/src/local/mailbox.c
index 854590c111a..45d24d8d234 100644
--- a/gnu/dist/postfix/src/local/mailbox.c
+++ b/gnu/dist/postfix/src/local/mailbox.c
@@ -72,6 +72,7 @@
#include <mail_params.h>
#include <deliver_pass.h>
#include <mbox_open.h>
+#include <maps.h>
#ifndef EDQUOT
#define EDQUOT EFBIG
@@ -94,7 +95,8 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
char *mailbox;
VSTRING *why;
MBOX *mp;
- int status;
+ int mail_copy_status;
+ int deliver_status;
int copy_flags;
VSTRING *biff;
long end;
@@ -118,7 +120,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
state.msg_attr.delivered = state.msg_attr.recipient;
- status = -1;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
why = vstring_alloc(100);
if (*var_home_mailbox) {
spool_dir = 0;
@@ -189,8 +191,8 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
errno = 0;
} else {
end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
- status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
- copy_flags, "\n", why);
+ mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
+ copy_flags, "\n", why);
}
if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid)
set_eugid(spool_uid, spool_gid);
@@ -201,14 +203,16 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
/*
* As the mail system, bounce, defer delivery, or report success.
*/
- if (status != 0) {
- status = (errno == EAGAIN || errno == ENOSPC ?
- defer_append : bounce_append)
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ deliver_status = (errno == EAGAIN || errno == ENOSPC || errno == ESTALE ?
+ defer_append : bounce_append)
(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
"cannot access mailbox %s for user %s. %s",
mailbox, state.msg_attr.user, vstring_str(why));
} else {
- sent(SENT_ATTR(state.msg_attr), "mailbox");
+ deliver_status = sent(SENT_ATTR(state.msg_attr), "mailbox");
if (var_biff) {
biff = vstring_alloc(100);
vstring_sprintf(biff, "%s@%ld", usr_attr.logname, (long) end);
@@ -222,7 +226,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
*/
myfree(mailbox);
vstring_free(why);
- return (status);
+ return (deliver_status);
}
/* deliver_mailbox - deliver to recipient mailbox */
@@ -233,6 +237,8 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
int status;
struct mypasswd *mbox_pwd;
char *path;
+ static MAPS *cmd_maps;
+ const char *map_command;
/*
* Make verbose logging easier to understand.
@@ -276,16 +282,29 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
SET_USER_ATTR(usr_attr, mbox_pwd, state.level);
/*
- * Deliver to mailbox or to external command.
+ * Deliver to mailbox, maildir or to external command.
*/
#define LAST_CHAR(s) (s[strlen(s) - 1])
- if (*var_mailbox_command)
+ if (*var_mailbox_cmd_maps && cmd_maps == 0)
+ cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps,
+ DICT_FLAG_LOCK);
+
+ if (*var_mailbox_cmd_maps
+ && (map_command = maps_find(cmd_maps, state.msg_attr.user,
+ DICT_FLAG_FIXED)) != 0) {
+ status = deliver_command(state, usr_attr, map_command);
+ } else if (*var_mailbox_command) {
status = deliver_command(state, usr_attr, var_mailbox_command);
- else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') {
+ } else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') {
path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0);
status = deliver_maildir(state, usr_attr, path);
myfree(path);
+ } else if (*var_mail_spool_dir && LAST_CHAR(var_mail_spool_dir) == '/') {
+ path = concatenate(var_mail_spool_dir, state.msg_attr.user,
+ "/", (char *) 0);
+ status = deliver_maildir(state, usr_attr, path);
+ myfree(path);
} else
status = deliver_mailbox_file(state, usr_attr);
diff --git a/gnu/dist/postfix/src/local/maildir.c b/gnu/dist/postfix/src/local/maildir.c
index e9bd0de85c7..b2ccef3cea5 100644
--- a/gnu/dist/postfix/src/local/maildir.c
+++ b/gnu/dist/postfix/src/local/maildir.c
@@ -80,7 +80,8 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
VSTRING *why;
VSTRING *buf;
VSTREAM *dst;
- int status;
+ int mail_copy_status;
+ int deliver_status;
int copy_flags;
static int count;
@@ -98,7 +99,7 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
state.msg_attr.delivered = state.msg_attr.recipient;
- status = -1;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
buf = vstring_alloc(100);
why = vstring_alloc(100);
@@ -137,14 +138,14 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
|| (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
vstring_sprintf(why, "create %s: %m", tmpfile);
} else {
- if (mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, "\n", why) == 0) {
+ if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
+ dst, copy_flags, "\n", why)) == 0) {
if (sane_link(tmpfile, newfile) < 0
&& (errno != ENOENT
|| (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
|| sane_link(tmpfile, newfile) < 0)) {
vstring_sprintf(why, "link to %s: %m", newfile);
- } else {
- status = 0;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
}
}
if (unlink(tmpfile) < 0)
@@ -152,12 +153,19 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
}
set_eugid(var_owner_uid, var_owner_gid);
- if (status)
- status = (errno == ENOSPC ? defer_append : bounce_append)
+ /*
+ * As the mail system, bounce or defer delivery.
+ */
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ deliver_status = (errno == ENOSPC || errno == ESTALE ?
+ defer_append : bounce_append)
(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
"maildir delivery failed: %s", vstring_str(why));
- else
- status = sent(SENT_ATTR(state.msg_attr), "maildir");
+ } else {
+ deliver_status = sent(SENT_ATTR(state.msg_attr), "maildir");
+ }
vstring_free(buf);
vstring_free(why);
myfree(newdir);
@@ -165,5 +173,5 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
myfree(curdir);
myfree(tmpfile);
myfree(newfile);
- return (status);
+ return (deliver_status);
}
diff --git a/gnu/dist/postfix/src/local/token.c b/gnu/dist/postfix/src/local/token.c
index 28803fe4c6c..c427eb7042c 100644
--- a/gnu/dist/postfix/src/local/token.c
+++ b/gnu/dist/postfix/src/local/token.c
@@ -148,7 +148,12 @@ int deliver_token(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr)
} else if (*STR(addr_buf) == '~') {
status = deliver_token_home(state, usr_attr, STR(addr_buf));
} else if (*STR(addr_buf) == '|') {
- status = deliver_command(state, usr_attr, STR(addr_buf) + 1);
+ if ((local_cmd_deliver_mask & state.msg_attr.exp_type) == 0)
+ status = bounce_append(BOUNCE_FLAG_KEEP,
+ BOUNCE_ATTR(state.msg_attr),
+ "mail to command is restricted");
+ else
+ status = deliver_command(state, usr_attr, STR(addr_buf) + 1);
} else if (strncasecmp(STR(addr_buf), include, sizeof(include) - 1) == 0) {
path = STR(addr_buf) + sizeof(include) - 1;
status = deliver_include(state, usr_attr, path);
diff --git a/gnu/dist/postfix/src/master/Makefile.in b/gnu/dist/postfix/src/master/Makefile.in
index 47f3369af07..cbb2894bf3f 100644
--- a/gnu/dist/postfix/src/master/Makefile.in
+++ b/gnu/dist/postfix/src/master/Makefile.in
@@ -2,12 +2,13 @@ SHELL = /bin/sh
SRCS = master.c master_conf.c master_ent.c master_sig.c master_avail.c \
master_spawn.c master_service.c master_status.o master_listen.c \
master_proto.c single_server.c multi_server.c master_vars.c \
- master_wakeup.c
+ master_wakeup.c master_flow.c mail_flow.c
OBJS = master.o master_conf.o master_ent.o master_sig.o master_avail.o \
master_spawn.o master_service.o master_status.o master_listen.o \
- master_vars.o master_wakeup.o
-LIB_OBJ = single_server.o multi_server.o trigger_server.o master_proto.o
-HDRS = mail_server.h master_proto.h
+ master_vars.o master_wakeup.o master_flow.o
+LIB_OBJ = single_server.o multi_server.o trigger_server.o master_proto.o \
+ mail_flow.o
+HDRS = mail_server.h master_proto.h mail_flow.h
INT_HDR = master.h
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
@@ -78,6 +79,12 @@ depend: $(MAKES)
@$(EXPORT) make -f Makefile.in Makefile 1>&2
# do not edit below this line - it is generated by 'make depend'
+mail_flow.o: mail_flow.c
+mail_flow.o: ../../include/sys_defs.h
+mail_flow.o: ../../include/msg.h
+mail_flow.o: ../../include/iostuff.h
+mail_flow.o: mail_flow.h
+mail_flow.o: master_proto.h
master.o: master.c
master.o: ../../include/sys_defs.h
master.o: ../../include/events.h
@@ -93,6 +100,7 @@ master.o: ../../include/myflock.h
master.o: ../../include/watchdog.h
master.o: ../../include/clean_env.h
master.o: ../../include/argv.h
+master.o: ../../include/safe.h
master.o: ../../include/mail_params.h
master.o: ../../include/debug_process.h
master.o: ../../include/mail_task.h
@@ -125,10 +133,17 @@ master_ent.o: ../../include/inet_util.h
master_ent.o: ../../include/inet_addr_host.h
master_ent.o: ../../include/mail_proto.h
master_ent.o: ../../include/iostuff.h
+master_ent.o: ../../include/attr.h
master_ent.o: ../../include/mail_params.h
master_ent.o: ../../include/own_inet_addr.h
master_ent.o: master_proto.h
master_ent.o: master.h
+master_flow.o: master_flow.c
+master_flow.o: ../../include/sys_defs.h
+master_flow.o: ../../include/msg.h
+master_flow.o: ../../include/iostuff.h
+master_flow.o: master.h
+master_flow.o: master_proto.h
master_listen.o: master_listen.c
master_listen.o: ../../include/sys_defs.h
master_listen.o: ../../include/msg.h
@@ -140,6 +155,7 @@ master_listen.o: ../../include/vstring.h
master_listen.o: ../../include/vbuf.h
master_listen.o: ../../include/inet_addr_list.h
master_listen.o: ../../include/set_eugid.h
+master_listen.o: ../../include/set_ugid.h
master_listen.o: ../../include/mail_params.h
master_listen.o: master.h
master_proto.o: master_proto.c
@@ -188,10 +204,14 @@ master_wakeup.o: ../../include/sys_defs.h
master_wakeup.o: ../../include/msg.h
master_wakeup.o: ../../include/trigger.h
master_wakeup.o: ../../include/events.h
+master_wakeup.o: ../../include/set_eugid.h
+master_wakeup.o: ../../include/set_ugid.h
master_wakeup.o: ../../include/mail_proto.h
master_wakeup.o: ../../include/vstream.h
master_wakeup.o: ../../include/vbuf.h
master_wakeup.o: ../../include/iostuff.h
+master_wakeup.o: ../../include/attr.h
+master_wakeup.o: ../../include/mail_params.h
master_wakeup.o: mail_server.h
master_wakeup.o: master.h
multi_server.o: multi_server.c
@@ -219,6 +239,7 @@ multi_server.o: ../../include/mail_params.h
multi_server.o: ../../include/mail_conf.h
multi_server.o: ../../include/timed_ipc.h
multi_server.o: ../../include/resolve_local.h
+multi_server.o: mail_flow.h
multi_server.o: master_proto.h
multi_server.o: mail_server.h
single_server.o: single_server.c
@@ -246,6 +267,7 @@ single_server.o: ../../include/debug_process.h
single_server.o: ../../include/mail_conf.h
single_server.o: ../../include/timed_ipc.h
single_server.o: ../../include/resolve_local.h
+single_server.o: mail_flow.h
single_server.o: master_proto.h
single_server.o: mail_server.h
trigger_server.o: trigger_server.c
@@ -272,5 +294,6 @@ trigger_server.o: ../../include/mail_task.h
trigger_server.o: ../../include/debug_process.h
trigger_server.o: ../../include/mail_conf.h
trigger_server.o: ../../include/resolve_local.h
+trigger_server.o: mail_flow.h
trigger_server.o: master_proto.h
trigger_server.o: mail_server.h
diff --git a/gnu/dist/postfix/src/master/mail_flow.c b/gnu/dist/postfix/src/master/mail_flow.c
new file mode 100644
index 00000000000..efb59885409
--- /dev/null
+++ b/gnu/dist/postfix/src/master/mail_flow.c
@@ -0,0 +1,140 @@
+/*++
+/* NAME
+/* mail_flow 3
+/* SUMMARY
+/* global mail flow control
+/* SYNOPSIS
+/* #include <mail_flow.h>
+/*
+/* int mail_flow_get(count)
+/* int count;
+/*
+/* void mail_flow_put(count)
+/* int count;
+/*
+/* int mail_flow_count()
+/* DESCRIPTION
+/* This module implements a simple flow control mechanism that
+/* is based on tokens that are consumed by mail receiving processes
+/* and that are produced by mail sending processes.
+/*
+/* mail_flow_get() attempts to read specified number of tokens. The
+/* result is > 0 for success, < 0 for failure. In the latter case,
+/* the process is expected to slow down a little.
+/*
+/* mail_flow_put() produces the specified number of tokens. The
+/* token producing process is expected to produce new tokens
+/* whenever it falls idle and no more tokens are available.
+/*
+/* mail_flow_count() returns the number of available tokens.
+/* BUGS
+/* The producer needs to wake up periodically to ensure that
+/* tokens are not lost due to leakage.
+/* 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 <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_flow.h>
+
+/* Master library. */
+
+#include <master_proto.h>
+
+#define BUFFER_SIZE 1024
+
+/* mail_flow_get - read N tokens */
+
+int mail_flow_get(int len)
+{
+ char *myname = "mail_flow_get";
+ char buf[BUFFER_SIZE];
+ struct stat st;
+ int count;
+ int n = 0;
+
+ /*
+ * Sanity check.
+ */
+ if (len <= 0)
+ msg_panic("%s: bad length %d", myname, len);
+
+ /*
+ * Silence some wild claims.
+ */
+ if (fstat(MASTER_FLOW_WRITE, &st) < 0)
+ msg_fatal("fstat flow pipe write descriptor: %m");
+
+ /*
+ * Read and discard N bytes. XXX AIX read() can return 0 when an open
+ * pipe is empty.
+ */
+ for (count = len; count > 0; count -= n)
+ if ((n = read(MASTER_FLOW_READ, buf, count > BUFFER_SIZE ?
+ BUFFER_SIZE : count)) <= 0)
+ return (-1);
+ if (msg_verbose)
+ msg_info("%s: %d %d", myname, len, len - count);
+ return (len - count);
+}
+
+/* mail_flow_put - put N tokens */
+
+int mail_flow_put(int len)
+{
+ char *myname = "mail_flow_put";
+ char buf[BUFFER_SIZE];
+ int count;
+ int n = 0;
+
+ /*
+ * Sanity check.
+ */
+ if (len <= 0)
+ msg_panic("%s: bad length %d", myname, len);
+
+ /*
+ * Write or discard N bytes.
+ */
+ memset(buf, 0, len > BUFFER_SIZE ? BUFFER_SIZE : len);
+
+ for (count = len; count > 0; count -= n)
+ if ((n = write(MASTER_FLOW_WRITE, buf, count > BUFFER_SIZE ?
+ BUFFER_SIZE : count)) < 0)
+ return (-1);
+ if (msg_verbose)
+ msg_info("%s: %d %d", myname, len, len - count);
+ return (len - count);
+}
+
+/* mail_flow_count - return number of available tokens */
+
+int mail_flow_count(void)
+{
+ char *myname = "mail_flow_count";
+ int count;
+
+ if ((count = peekfd(MASTER_FLOW_READ)) < 0)
+ msg_warn("%s: %m", myname);
+ return (count);
+}
diff --git a/gnu/dist/postfix/src/master/mail_flow.h b/gnu/dist/postfix/src/master/mail_flow.h
new file mode 100644
index 00000000000..9e058598f5d
--- /dev/null
+++ b/gnu/dist/postfix/src/master/mail_flow.h
@@ -0,0 +1,32 @@
+#ifndef _MAIL_FLOW_H_INCLUDED_
+#define _MAIL_FLOW_H_INCLUDED_
+
+/*++
+/* NAME
+/* mail_flow 3h
+/* SUMMARY
+/* global mail flow control
+/* SYNOPSIS
+/* #include <mail_flow.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Functional interface.
+ */
+extern int mail_flow_get(int);
+extern int mail_flow_put(int);
+extern int mail_flow_count(void);
+
+/* 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/gnu/dist/postfix/src/master/mail_server.h b/gnu/dist/postfix/src/master/mail_server.h
index d03313b1ead..47b8cfeeed2 100644
--- a/gnu/dist/postfix/src/master/mail_server.h
+++ b/gnu/dist/postfix/src/master/mail_server.h
@@ -28,6 +28,8 @@
#define MAIL_SERVER_EXIT 13
#define MAIL_SERVER_PRE_ACCEPT 14
+#define MAIL_SERVER_IN_FLOW_DELAY 20
+
typedef void (*MAIL_SERVER_INIT_FN) (char *, char **);
typedef int (*MAIL_SERVER_LOOP_FN) (char *, char **);
typedef void (*MAIL_SERVER_EXIT_FN) (char *, char **);
diff --git a/gnu/dist/postfix/src/master/master.c b/gnu/dist/postfix/src/master/master.c
index 4299424f1ce..337ba7931b5 100644
--- a/gnu/dist/postfix/src/master/master.c
+++ b/gnu/dist/postfix/src/master/master.c
@@ -154,6 +154,7 @@
#include <watchdog.h>
#include <clean_env.h>
#include <argv.h>
+#include <safe.h>
/* Global library. */
@@ -241,6 +242,16 @@ int main(int argc, char **argv)
msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY);
/*
+ * The mail system must be run by the superuser so it can revoke
+ * privileges for selected operations. That's right - it takes privileges
+ * to toss privileges.
+ */
+ if (getuid() != 0)
+ msg_fatal("the master command is reserved for the superuser");
+ if (unsafe() != 0)
+ msg_fatal("the master command must not run as a set-uid process");
+
+ /*
* If started from a terminal, get rid of any tty association. This also
* means that all errors and warnings must go to the syslog daemon.
*/
@@ -256,8 +267,11 @@ int main(int argc, char **argv)
* when a service listens on many ports. In order to do this right we
* must change the master-child interface so that descriptors do not need
* to have fixed numbers.
+ *
+ * In a child we need two descriptors for the flow control pipe, one for
+ * child->master status updates and at least one for listening.
*/
- for (n = 0; n < 3; n++) {
+ for (n = 0; n < 5; n++) {
if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0)
msg_fatal("dup(0): %m");
}
@@ -349,6 +363,7 @@ int main(int argc, char **argv)
*/
master_config();
master_sigsetup();
+ master_flow_init();
msg_info("daemon started");
/*
diff --git a/gnu/dist/postfix/src/master/master_flow.c b/gnu/dist/postfix/src/master/master_flow.c
new file mode 100644
index 00000000000..ed9b492d22b
--- /dev/null
+++ b/gnu/dist/postfix/src/master/master_flow.c
@@ -0,0 +1,33 @@
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* Application-specific. */
+
+#include <master.h>
+#include <master_proto.h>
+
+int master_flow_pipe[2];
+
+/* master_flow_init - initialize the flow control channel */
+
+void master_flow_init(void)
+{
+ char *myname = "master_flow_init";
+
+ if (pipe(master_flow_pipe) < 0)
+ msg_fatal("%s: pipe: %m", myname);
+
+ non_blocking(master_flow_pipe[0], NON_BLOCKING);
+ non_blocking(master_flow_pipe[1], NON_BLOCKING);
+
+ close_on_exec(master_flow_pipe[0], CLOSE_ON_EXEC);
+ close_on_exec(master_flow_pipe[1], CLOSE_ON_EXEC);
+}
diff --git a/gnu/dist/postfix/src/master/master_proto.h b/gnu/dist/postfix/src/master/master_proto.h
index 2929ed46375..36c30f8d5bb 100644
--- a/gnu/dist/postfix/src/master/master_proto.h
+++ b/gnu/dist/postfix/src/master/master_proto.h
@@ -32,6 +32,14 @@ typedef struct MASTER_STATUS {
extern int master_notify(int, int); /* encapsulate status msg */
/*
+ * File descriptors inherited from the master process. The flow control pipe
+ * is read by receive processes and is written to by send processes. If
+ * receive processes get too far ahead they will pause for a brief moment.
+ */
+#define MASTER_FLOW_READ 3
+#define MASTER_FLOW_WRITE 4
+
+ /*
* File descriptors inherited from the master process. All processes that
* provide a given service share the same status file descriptor, and listen
* on the same service socket(s). The kernel decides what process gets the
@@ -41,8 +49,8 @@ extern int master_notify(int, int); /* encapsulate status msg */
* actually the lowest-numbered descriptor of a sequence of descriptors to
* listen on.
*/
-#define MASTER_STATUS_FD 3 /* shared channel to parent */
-#define MASTER_LISTEN_FD 4 /* accept connections here */
+#define MASTER_STATUS_FD 5 /* shared channel to parent */
+#define MASTER_LISTEN_FD 6 /* accept connections here */
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/master/master_spawn.c b/gnu/dist/postfix/src/master/master_spawn.c
index 75dcfafbeec..2eaf7bf96b3 100644
--- a/gnu/dist/postfix/src/master/master_spawn.c
+++ b/gnu/dist/postfix/src/master/master_spawn.c
@@ -175,6 +175,23 @@ void master_spawn(MASTER_SERV *serv)
case 0:
msg_cleanup((void (*) (void)) 0); /* disable exit handler */
closelog(); /* avoid filedes leak */
+
+ if (master_flow_pipe[0] <= MASTER_FLOW_READ)
+ msg_fatal("%s: flow pipe read descriptor <= %d",
+ myname, MASTER_FLOW_READ);
+ if (DUP2(master_flow_pipe[0], MASTER_FLOW_READ) < 0)
+ msg_fatal("%s: dup2: %m", myname);
+ if (close(master_flow_pipe[0]) < 0)
+ msg_fatal("close %d: %m", master_flow_pipe[0]);
+
+ if (master_flow_pipe[1] <= MASTER_FLOW_WRITE)
+ msg_fatal("%s: flow pipe read descriptor <= %d",
+ myname, MASTER_FLOW_WRITE);
+ if (DUP2(master_flow_pipe[1], MASTER_FLOW_WRITE) < 0)
+ msg_fatal("%s: dup2: %m", myname);
+ if (close(master_flow_pipe[1]) < 0)
+ msg_fatal("close %d: %m", master_flow_pipe[1]);
+
close(serv->status_fd[0]); /* status channel */
if (serv->status_fd[1] <= MASTER_STATUS_FD)
msg_fatal("%s: status file descriptor collision", myname);
diff --git a/gnu/dist/postfix/src/master/master_wakeup.c b/gnu/dist/postfix/src/master/master_wakeup.c
index 16cc7be5bcc..d3052e688ff 100644
--- a/gnu/dist/postfix/src/master/master_wakeup.c
+++ b/gnu/dist/postfix/src/master/master_wakeup.c
@@ -58,10 +58,13 @@
#include <msg.h>
#include <trigger.h>
#include <events.h>
+#include <set_eugid.h>
+#include <set_ugid.h>
/* Global library. */
#include <mail_proto.h> /* triggers */
+#include <mail_params.h>
/* Application-specific. */
@@ -101,8 +104,30 @@ static void master_wakeup_timer_event(int unused_event, char *context)
case MASTER_SERV_TYPE_UNIX:
status = LOCAL_TRIGGER(serv->name, &wakeup, sizeof(wakeup), BRIEFLY);
break;
+
+ /*
+ * If someone compromises the postfix account then this must not
+ * overwrite files outside the chroot jail. Countermeasures:
+ *
+ * - Limit the damage by accessing the FIFO as postfix not root.
+ *
+ * - Have fifo_trigger() call safe_open() so we won't follow
+ * arbitrary hard/symlinks to files in/outside the chroot jail.
+ *
+ * - All non-chroot postfix-related files must be root owned (or
+ * postfix check complains).
+ *
+ * - The postfix user and group ID must not be shared with other
+ * applications (says the INSTALL documentation).
+ *
+ * Result of a discussion with Michael Tokarev, who received his
+ * insights from Solar Designer, who tested Postfix with a kernel
+ * module that is paranoid about open() calls.
+ */
case MASTER_SERV_TYPE_FIFO:
+ set_eugid(var_owner_uid, var_owner_gid);
status = fifo_trigger(serv->name, &wakeup, sizeof(wakeup), BRIEFLY);
+ set_ugid(getuid(), getgid());
break;
default:
msg_panic("%s: unknown service type: %d", myname, serv->type);
diff --git a/gnu/dist/postfix/src/master/multi_server.c b/gnu/dist/postfix/src/master/multi_server.c
index c9df296f4f9..602db54c71a 100644
--- a/gnu/dist/postfix/src/master/multi_server.c
+++ b/gnu/dist/postfix/src/master/multi_server.c
@@ -95,6 +95,9 @@
/* Function to be executed prior to accepting a new connection.
/* .sp
/* Only the last instance of this parameter type is remembered.
+/* .IP "MAIL_SERVER_IN_FLOW_DELAY (none)"
+/* Pause $in_flow_delay seconds when no "mail flow control token"
+/* is available. A token is consumed for each connection request.
/* .PP
/* multi_server_disconnect() should be called by the application
/* when a client disconnects.
@@ -168,6 +171,7 @@
#include <mail_conf.h>
#include <timed_ipc.h>
#include <resolve_local.h>
+#include <mail_flow.h>
/* Process manager. */
@@ -190,6 +194,7 @@ static void (*multi_server_accept) (int, char *);
static void (*multi_server_onexit) (char *, char **);
static void (*multi_server_pre_accept) (char *, char **);
static VSTREAM *multi_server_lock;
+static int multi_server_in_flow_delay;
/* multi_server_exit - normal termination */
@@ -253,11 +258,21 @@ static void multi_server_execute(int unused_event, char *context)
event_request_timer(multi_server_timeout, (char *) 0, var_idle_limit);
}
+/* multi_server_enable_read - enable read events */
+
+static void multi_server_enable_read(int unused_event, char *context)
+{
+ VSTREAM *stream = (VSTREAM *) context;
+
+ event_enable_read(vstream_fileno(stream), multi_server_execute, (char *) stream);
+}
+
/* multi_server_wakeup - wake up application */
static void multi_server_wakeup(int fd)
{
VSTREAM *stream;
+ char *tmp;
if (msg_verbose)
msg_info("connection established fd %d", fd);
@@ -265,8 +280,15 @@ static void multi_server_wakeup(int fd)
close_on_exec(fd, CLOSE_ON_EXEC);
client_count++;
stream = vstream_fdopen(fd, O_RDWR);
+ tmp = concatenate(multi_server_name, " socket", (char *) 0);
+ vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END);
+ myfree(tmp);
timed_ipc_setup(stream);
- event_enable_read(fd, multi_server_execute, (char *) stream);
+ if (multi_server_in_flow_delay && mail_flow_get(1) < 0)
+ event_request_timer(multi_server_enable_read, (char *) stream,
+ var_in_flow_delay);
+ else
+ multi_server_enable_read(0, (char *) stream);
}
/* multi_server_accept_local - accept client connection request */
@@ -498,6 +520,9 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
case MAIL_SERVER_PRE_ACCEPT:
multi_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
break;
+ case MAIL_SERVER_IN_FLOW_DELAY:
+ multi_server_in_flow_delay = 1;
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
@@ -611,6 +636,8 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
}
event_enable_read(MASTER_STATUS_FD, multi_server_abort, (char *) 0);
close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
+ close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
+ close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
watchdog = watchdog_create(var_daemon_timeout, (WATCHDOG_FN) 0, (char *) 0);
/*
diff --git a/gnu/dist/postfix/src/master/single_server.c b/gnu/dist/postfix/src/master/single_server.c
index 9c6920a7d73..d110e40102c 100644
--- a/gnu/dist/postfix/src/master/single_server.c
+++ b/gnu/dist/postfix/src/master/single_server.c
@@ -92,6 +92,9 @@
/* Function to be executed prior to accepting a new connection.
/* .sp
/* Only the last instance of this parameter type is remembered.
+/* .IP "MAIL_SERVER_IN_FLOW_DELAY (none)"
+/* Pause $in_flow_delay seconds when no "mail flow control token"
+/* is available. A token is consumed for each connection request.
/* .PP
/* The var_use_limit variable limits the number of clients that
/* a server can service before it commits suicide.
@@ -162,6 +165,7 @@
#include <mail_conf.h>
#include <timed_ipc.h>
#include <resolve_local.h>
+#include <mail_flow.h>
/* Process manager. */
@@ -183,6 +187,7 @@ static void (*single_server_accept) (int, char *);
static void (*single_server_onexit) (char *, char **);
static void (*single_server_pre_accept) (char *, char **);
static VSTREAM *single_server_lock;
+static int single_server_in_flow_delay;
/* single_server_exit - normal termination */
@@ -216,6 +221,7 @@ static void single_server_timeout(int unused_event, char *unused_context)
static void single_server_wakeup(int fd)
{
VSTREAM *stream;
+ char *tmp;
/*
* If the accept() succeeds, be sure to disable non-blocking I/O, because
@@ -228,9 +234,14 @@ static void single_server_wakeup(int fd)
non_blocking(fd, BLOCKING);
close_on_exec(fd, CLOSE_ON_EXEC);
stream = vstream_fdopen(fd, O_RDWR);
+ tmp = concatenate(single_server_name, " socket", (char *) 0);
+ vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END);
+ myfree(tmp);
timed_ipc_setup(stream);
if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0)
single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
+ if (single_server_in_flow_delay && mail_flow_get(1) < 0)
+ doze(var_in_flow_delay * 1000000);
single_server_service(stream, single_server_name, single_server_argv);
(void) vstream_fclose(stream);
if (master_notify(var_pid, MASTER_STAT_AVAIL) < 0)
@@ -469,6 +480,9 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
case MAIL_SERVER_PRE_ACCEPT:
single_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
break;
+ case MAIL_SERVER_IN_FLOW_DELAY:
+ single_server_in_flow_delay = 1;
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
@@ -582,6 +596,8 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
}
event_enable_read(MASTER_STATUS_FD, single_server_abort, (char *) 0);
close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
+ close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
+ close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
watchdog = watchdog_create(var_daemon_timeout, (WATCHDOG_FN) 0, (char *) 0);
/*
diff --git a/gnu/dist/postfix/src/master/trigger_server.c b/gnu/dist/postfix/src/master/trigger_server.c
index 7edd05ff6d6..c84b8a8b519 100644
--- a/gnu/dist/postfix/src/master/trigger_server.c
+++ b/gnu/dist/postfix/src/master/trigger_server.c
@@ -99,6 +99,9 @@
/* Function to be executed prior to accepting a new request.
/* .sp
/* Only the last instance of this parameter type is remembered.
+/* .IP "MAIL_SERVER_IN_FLOW_DELAY (none)"
+/* Pause $in_flow_delay seconds when no "mail flow control token"
+/* is available. A token is consumed for each connection request.
/* .PP
/* The var_use_limit variable limits the number of clients that
/* a server can service before it commits suicide.
@@ -169,6 +172,7 @@
#include <debug_process.h>
#include <mail_conf.h>
#include <resolve_local.h>
+#include <mail_flow.h>
/* Process manager. */
@@ -190,6 +194,7 @@ static void (*trigger_server_accept) (int, char *);
static void (*trigger_server_onexit) (char *, char **);
static void (*trigger_server_pre_accept) (char *, char **);
static VSTREAM *trigger_server_lock;
+static int trigger_server_in_flow_delay;
/* trigger_server_exit - normal termination */
@@ -230,6 +235,8 @@ static void trigger_server_wakeup(int fd)
*/
if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0)
trigger_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
+ if (trigger_server_in_flow_delay && mail_flow_get(1) < 0)
+ doze(var_in_flow_delay * 1000000);
if ((len = read(fd, buf, sizeof(buf))) >= 0)
trigger_server_service(buf, len, trigger_server_name,
trigger_server_argv);
@@ -469,6 +476,9 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,..
case MAIL_SERVER_PRE_ACCEPT:
trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
break;
+ case MAIL_SERVER_IN_FLOW_DELAY:
+ trigger_server_in_flow_delay = 1;
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
@@ -593,6 +603,8 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,..
}
event_enable_read(MASTER_STATUS_FD, trigger_server_abort, (char *) 0);
close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
+ close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
+ close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
watchdog = watchdog_create(1000, (WATCHDOG_FN) 0, (char *) 0);
/*
diff --git a/gnu/dist/postfix/src/nqmgr/.printfck b/gnu/dist/postfix/src/nqmgr/.printfck
new file mode 100644
index 00000000000..66016ed453c
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/.printfck
@@ -0,0 +1,25 @@
+been_here_xt 2 0
+bounce_append 5 0
+cleanup_out_format 1 0
+defer_append 5 0
+mail_command 1 0
+mail_print 1 0
+msg_error 0 0
+msg_fatal 0 0
+msg_info 0 0
+msg_panic 0 0
+msg_warn 0 0
+opened 4 0
+post_mail_fprintf 1 0
+qmgr_message_bounce 2 0
+rec_fprintf 2 0
+sent 4 0
+smtp_cmd 1 0
+smtp_mesg_fail 2 0
+smtp_printf 1 0
+smtp_rcpt_fail 3 0
+smtp_site_fail 2 0
+udp_syslog 1 0
+vstream_fprintf 1 0
+vstream_printf 0 0
+vstring_sprintf 1 0
diff --git a/gnu/dist/postfix/src/nqmgr/Makefile.in b/gnu/dist/postfix/src/nqmgr/Makefile.in
new file mode 100644
index 00000000000..7396348c878
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/Makefile.in
@@ -0,0 +1,296 @@
+SHELL = /bin/sh
+SRCS = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \
+ qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \
+ qmgr_job.c qmgr_peer.c \
+ qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c
+OBJS = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \
+ qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \
+ qmgr_job.o qmgr_peer.o \
+ qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o
+HDRS = qmgr.h
+TESTSRC =
+WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
+ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
+ -Wunused
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG=
+PROG = qmgr
+INC_DIR = ../../include
+LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+Makefile: Makefile.in
+ (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
+
+test: $(TESTPROG)
+
+update: ../../libexec/n$(PROG)
+
+../../libexec/n$(PROG): $(PROG)
+ cp $(PROG) ../../libexec/n$(PROG)
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ cp *.h printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
+ done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+qmgr.o: qmgr.c
+qmgr.o: ../../include/sys_defs.h
+qmgr.o: ../../include/msg.h
+qmgr.o: ../../include/events.h
+qmgr.o: ../../include/vstream.h
+qmgr.o: ../../include/vbuf.h
+qmgr.o: ../../include/dict.h
+qmgr.o: ../../include/argv.h
+qmgr.o: ../../include/mail_queue.h
+qmgr.o: ../../include/vstring.h
+qmgr.o: ../../include/recipient_list.h
+qmgr.o: ../../include/mail_conf.h
+qmgr.o: ../../include/mail_params.h
+qmgr.o: ../../include/mail_proto.h
+qmgr.o: ../../include/iostuff.h
+qmgr.o: ../../include/attr.h
+qmgr.o: ../../include/mail_flow.h
+qmgr.o: ../../include/master_proto.h
+qmgr.o: ../../include/mail_server.h
+qmgr.o: qmgr.h
+qmgr.o: ../../include/scan_dir.h
+qmgr.o: ../../include/maps.h
+qmgr_active.o: qmgr_active.c
+qmgr_active.o: ../../include/sys_defs.h
+qmgr_active.o: ../../include/msg.h
+qmgr_active.o: ../../include/events.h
+qmgr_active.o: ../../include/mymalloc.h
+qmgr_active.o: ../../include/vstream.h
+qmgr_active.o: ../../include/vbuf.h
+qmgr_active.o: ../../include/mail_params.h
+qmgr_active.o: ../../include/mail_open_ok.h
+qmgr_active.o: ../../include/mail_queue.h
+qmgr_active.o: ../../include/vstring.h
+qmgr_active.o: ../../include/recipient_list.h
+qmgr_active.o: ../../include/bounce.h
+qmgr_active.o: ../../include/defer.h
+qmgr_active.o: ../../include/abounce.h
+qmgr_active.o: ../../include/rec_type.h
+qmgr_active.o: qmgr.h
+qmgr_active.o: ../../include/scan_dir.h
+qmgr_active.o: ../../include/maps.h
+qmgr_active.o: ../../include/dict.h
+qmgr_active.o: ../../include/argv.h
+qmgr_bounce.o: qmgr_bounce.c
+qmgr_bounce.o: ../../include/sys_defs.h
+qmgr_bounce.o: ../../include/bounce.h
+qmgr_bounce.o: ../../include/deliver_completed.h
+qmgr_bounce.o: ../../include/vstream.h
+qmgr_bounce.o: ../../include/vbuf.h
+qmgr_bounce.o: qmgr.h
+qmgr_bounce.o: ../../include/scan_dir.h
+qmgr_bounce.o: ../../include/maps.h
+qmgr_bounce.o: ../../include/dict.h
+qmgr_bounce.o: ../../include/argv.h
+qmgr_defer.o: qmgr_defer.c
+qmgr_defer.o: ../../include/sys_defs.h
+qmgr_defer.o: ../../include/msg.h
+qmgr_defer.o: ../../include/vstream.h
+qmgr_defer.o: ../../include/vbuf.h
+qmgr_defer.o: ../../include/defer.h
+qmgr_defer.o: ../../include/bounce.h
+qmgr_defer.o: qmgr.h
+qmgr_defer.o: ../../include/scan_dir.h
+qmgr_defer.o: ../../include/maps.h
+qmgr_defer.o: ../../include/dict.h
+qmgr_defer.o: ../../include/argv.h
+qmgr_deliver.o: qmgr_deliver.c
+qmgr_deliver.o: ../../include/sys_defs.h
+qmgr_deliver.o: ../../include/msg.h
+qmgr_deliver.o: ../../include/vstring.h
+qmgr_deliver.o: ../../include/vbuf.h
+qmgr_deliver.o: ../../include/vstream.h
+qmgr_deliver.o: ../../include/vstring_vstream.h
+qmgr_deliver.o: ../../include/events.h
+qmgr_deliver.o: ../../include/iostuff.h
+qmgr_deliver.o: ../../include/mail_queue.h
+qmgr_deliver.o: ../../include/mail_proto.h
+qmgr_deliver.o: ../../include/attr.h
+qmgr_deliver.o: ../../include/recipient_list.h
+qmgr_deliver.o: ../../include/mail_params.h
+qmgr_deliver.o: ../../include/deliver_request.h
+qmgr_deliver.o: ../../include/verp_sender.h
+qmgr_deliver.o: qmgr.h
+qmgr_deliver.o: ../../include/scan_dir.h
+qmgr_deliver.o: ../../include/maps.h
+qmgr_deliver.o: ../../include/dict.h
+qmgr_deliver.o: ../../include/argv.h
+qmgr_enable.o: qmgr_enable.c
+qmgr_enable.o: ../../include/sys_defs.h
+qmgr_enable.o: ../../include/msg.h
+qmgr_enable.o: ../../include/vstream.h
+qmgr_enable.o: ../../include/vbuf.h
+qmgr_enable.o: qmgr.h
+qmgr_enable.o: ../../include/scan_dir.h
+qmgr_enable.o: ../../include/maps.h
+qmgr_enable.o: ../../include/dict.h
+qmgr_enable.o: ../../include/argv.h
+qmgr_entry.o: qmgr_entry.c
+qmgr_entry.o: ../../include/sys_defs.h
+qmgr_entry.o: ../../include/msg.h
+qmgr_entry.o: ../../include/mymalloc.h
+qmgr_entry.o: ../../include/events.h
+qmgr_entry.o: ../../include/vstream.h
+qmgr_entry.o: ../../include/vbuf.h
+qmgr_entry.o: ../../include/mail_params.h
+qmgr_entry.o: qmgr.h
+qmgr_entry.o: ../../include/scan_dir.h
+qmgr_entry.o: ../../include/maps.h
+qmgr_entry.o: ../../include/dict.h
+qmgr_entry.o: ../../include/argv.h
+qmgr_job.o: qmgr_job.c
+qmgr_job.o: ../../include/sys_defs.h
+qmgr_job.o: ../../include/msg.h
+qmgr_job.o: ../../include/htable.h
+qmgr_job.o: ../../include/mymalloc.h
+qmgr_job.o: ../../include/sane_time.h
+qmgr_job.o: qmgr.h
+qmgr_job.o: ../../include/vstream.h
+qmgr_job.o: ../../include/vbuf.h
+qmgr_job.o: ../../include/scan_dir.h
+qmgr_job.o: ../../include/maps.h
+qmgr_job.o: ../../include/dict.h
+qmgr_job.o: ../../include/argv.h
+qmgr_message.o: qmgr_message.c
+qmgr_message.o: ../../include/sys_defs.h
+qmgr_message.o: ../../include/msg.h
+qmgr_message.o: ../../include/mymalloc.h
+qmgr_message.o: ../../include/vstring.h
+qmgr_message.o: ../../include/vbuf.h
+qmgr_message.o: ../../include/vstream.h
+qmgr_message.o: ../../include/split_at.h
+qmgr_message.o: ../../include/valid_hostname.h
+qmgr_message.o: ../../include/argv.h
+qmgr_message.o: ../../include/stringops.h
+qmgr_message.o: ../../include/myflock.h
+qmgr_message.o: ../../include/sane_time.h
+qmgr_message.o: ../../include/dict.h
+qmgr_message.o: ../../include/mail_queue.h
+qmgr_message.o: ../../include/mail_params.h
+qmgr_message.o: ../../include/canon_addr.h
+qmgr_message.o: ../../include/record.h
+qmgr_message.o: ../../include/rec_type.h
+qmgr_message.o: ../../include/sent.h
+qmgr_message.o: ../../include/deliver_completed.h
+qmgr_message.o: ../../include/mail_addr_find.h
+qmgr_message.o: ../../include/maps.h
+qmgr_message.o: ../../include/opened.h
+qmgr_message.o: ../../include/resolve_local.h
+qmgr_message.o: ../../include/verp_sender.h
+qmgr_message.o: ../../include/resolve_clnt.h
+qmgr_message.o: qmgr.h
+qmgr_message.o: ../../include/scan_dir.h
+qmgr_move.o: qmgr_move.c
+qmgr_move.o: ../../include/sys_defs.h
+qmgr_move.o: ../../include/msg.h
+qmgr_move.o: ../../include/scan_dir.h
+qmgr_move.o: ../../include/recipient_list.h
+qmgr_move.o: ../../include/mail_queue.h
+qmgr_move.o: ../../include/vstring.h
+qmgr_move.o: ../../include/vbuf.h
+qmgr_move.o: ../../include/vstream.h
+qmgr_move.o: ../../include/mail_scan_dir.h
+qmgr_move.o: qmgr.h
+qmgr_move.o: ../../include/maps.h
+qmgr_move.o: ../../include/dict.h
+qmgr_move.o: ../../include/argv.h
+qmgr_peer.o: qmgr_peer.c
+qmgr_peer.o: ../../include/sys_defs.h
+qmgr_peer.o: ../../include/msg.h
+qmgr_peer.o: ../../include/htable.h
+qmgr_peer.o: ../../include/mymalloc.h
+qmgr_peer.o: qmgr.h
+qmgr_peer.o: ../../include/vstream.h
+qmgr_peer.o: ../../include/vbuf.h
+qmgr_peer.o: ../../include/scan_dir.h
+qmgr_peer.o: ../../include/maps.h
+qmgr_peer.o: ../../include/dict.h
+qmgr_peer.o: ../../include/argv.h
+qmgr_queue.o: qmgr_queue.c
+qmgr_queue.o: ../../include/sys_defs.h
+qmgr_queue.o: ../../include/msg.h
+qmgr_queue.o: ../../include/mymalloc.h
+qmgr_queue.o: ../../include/events.h
+qmgr_queue.o: ../../include/htable.h
+qmgr_queue.o: ../../include/mail_params.h
+qmgr_queue.o: ../../include/recipient_list.h
+qmgr_queue.o: qmgr.h
+qmgr_queue.o: ../../include/vstream.h
+qmgr_queue.o: ../../include/vbuf.h
+qmgr_queue.o: ../../include/scan_dir.h
+qmgr_queue.o: ../../include/maps.h
+qmgr_queue.o: ../../include/dict.h
+qmgr_queue.o: ../../include/argv.h
+qmgr_rcpt_list.o: qmgr_rcpt_list.c
+qmgr_rcpt_list.o: ../../include/sys_defs.h
+qmgr_rcpt_list.o: ../../include/mymalloc.h
+qmgr_rcpt_list.o: qmgr.h
+qmgr_rcpt_list.o: ../../include/vstream.h
+qmgr_rcpt_list.o: ../../include/vbuf.h
+qmgr_rcpt_list.o: ../../include/scan_dir.h
+qmgr_rcpt_list.o: ../../include/maps.h
+qmgr_rcpt_list.o: ../../include/dict.h
+qmgr_rcpt_list.o: ../../include/argv.h
+qmgr_scan.o: qmgr_scan.c
+qmgr_scan.o: ../../include/sys_defs.h
+qmgr_scan.o: ../../include/msg.h
+qmgr_scan.o: ../../include/mymalloc.h
+qmgr_scan.o: ../../include/scan_dir.h
+qmgr_scan.o: ../../include/mail_scan_dir.h
+qmgr_scan.o: qmgr.h
+qmgr_scan.o: ../../include/vstream.h
+qmgr_scan.o: ../../include/vbuf.h
+qmgr_scan.o: ../../include/maps.h
+qmgr_scan.o: ../../include/dict.h
+qmgr_scan.o: ../../include/argv.h
+qmgr_transport.o: qmgr_transport.c
+qmgr_transport.o: ../../include/sys_defs.h
+qmgr_transport.o: ../../include/msg.h
+qmgr_transport.o: ../../include/htable.h
+qmgr_transport.o: ../../include/events.h
+qmgr_transport.o: ../../include/mymalloc.h
+qmgr_transport.o: ../../include/vstream.h
+qmgr_transport.o: ../../include/vbuf.h
+qmgr_transport.o: ../../include/iostuff.h
+qmgr_transport.o: ../../include/mail_proto.h
+qmgr_transport.o: ../../include/attr.h
+qmgr_transport.o: ../../include/recipient_list.h
+qmgr_transport.o: ../../include/mail_conf.h
+qmgr_transport.o: ../../include/mail_params.h
+qmgr_transport.o: qmgr.h
+qmgr_transport.o: ../../include/scan_dir.h
+qmgr_transport.o: ../../include/maps.h
+qmgr_transport.o: ../../include/dict.h
+qmgr_transport.o: ../../include/argv.h
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr.c b/gnu/dist/postfix/src/nqmgr/qmgr.c
new file mode 100644
index 00000000000..e259e47aa06
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr.c
@@ -0,0 +1,582 @@
+/*++
+/* NAME
+/* qmgr 8
+/* SUMMARY
+/* Postfix queue manager
+/* SYNOPSIS
+/* \fBqmgr\fR [generic Postfix daemon options]
+/* DESCRIPTION
+/* The \fBqmgr\fR daemon awaits the arrival of incoming mail
+/* and arranges for its delivery via Postfix delivery processes.
+/* The actual mail routing strategy is delegated to the
+/* \fBtrivial-rewrite\fR(8) daemon.
+/* This program expects to be run from the \fBmaster\fR(8) process
+/* manager.
+/*
+/* Mail addressed to the local \fBdouble-bounce\fR address is silently
+/* discarded. This stops potential loops caused by undeliverable
+/* bounce notifications.
+/*
+/* Mail addressed to a user listed in the optional \fBrelocated\fR
+/* database is bounced with a "user has moved to \fInew_location\fR"
+/* message. See \fBrelocated\fR(5) for a precise description.
+/* MAIL QUEUES
+/* .ad
+/* .fi
+/* The \fBqmgr\fR daemon maintains the following queues:
+/* .IP \fBincoming\fR
+/* Inbound mail from the network, or mail picked up by the
+/* local \fBpickup\fR agent from the \fBmaildrop\fR directory.
+/* .IP \fBactive\fR
+/* Messages that the queue manager has opened for delivery. Only
+/* a limited number of messages is allowed to enter the \fBactive\fR
+/* queue (leaky bucket strategy, for a fixed delivery rate).
+/* .IP \fBdeferred\fR
+/* Mail that could not be delivered upon the first attempt. The queue
+/* manager implements exponential backoff by doubling the time between
+/* delivery attempts.
+/* .IP \fBcorrupt\fR
+/* Unreadable or damaged queue files are moved here for inspection.
+/* DELIVERY STATUS REPORTS
+/* .ad
+/* .fi
+/* The \fBqmgr\fR daemon keeps an eye on per-message delivery status
+/* reports in the following directories. Each status report file has
+/* the same name as the corresponding message file:
+/* .IP \fBbounce\fR
+/* Per-recipient status information about why mail is bounced.
+/* These files are maintained by the \fBbounce\fR(8) daemon.
+/* .IP \fBdefer\fR
+/* Per-recipient status information about why mail is delayed.
+/* These files are maintained by the \fBdefer\fR(8) daemon.
+/* .PP
+/* The \fBqmgr\fR daemon is responsible for asking the
+/* \fBbounce\fR(8) or \fBdefer\fR(8) daemons to send non-delivery
+/* reports.
+/* STRATEGIES
+/* .ad
+/* .fi
+/* The queue manager implements a variety of strategies for
+/* either opening queue files (input) or for message delivery (output).
+/* .IP "\fBleaky bucket\fR"
+/* This strategy limits the number of messages in the \fBactive\fR queue
+/* and prevents the queue manager from running out of memory under
+/* heavy load.
+/* .IP \fBfairness\fR
+/* When the \fBactive\fR queue has room, the queue manager takes one
+/* message from the \fBincoming\fR queue and one from the \fBdeferred\fR
+/* queue. This prevents a large mail backlog from blocking the delivery
+/* of new mail.
+/* .IP "\fBslow start\fR"
+/* This strategy eliminates "thundering herd" problems by slowly
+/* adjusting the number of parallel deliveries to the same destination.
+/* .IP "\fBround robin\fR
+/* The queue manager sorts delivery requests by destination.
+/* Round-robin selection prevents one destination from dominating
+/* deliveries to other destinations.
+/* .IP "\fBexponential backoff\fR"
+/* Mail that cannot be delivered upon the first attempt is deferred.
+/* The time interval between delivery attempts is doubled after each
+/* attempt.
+/* .IP "\fBdestination status cache\fR"
+/* The queue manager avoids unnecessary delivery attempts by
+/* maintaining a short-term, in-memory list of unreachable destinations.
+/* .IP "\fBpreemptive message scheduling\fR"
+/* The queue manager attempts to minimize the average per-recipient delay
+/* while still preserving the correct per-message delays, using
+/* a sophisticated preemptive message scheduling.
+/* TRIGGERS
+/* .ad
+/* .fi
+/* On an idle system, the queue manager waits for the arrival of
+/* trigger events, or it waits for a timer to go off. A trigger
+/* is a one-byte message.
+/* Depending on the message received, the queue manager performs
+/* one of the following actions (the message is followed by the
+/* symbolic constant used internally by the software):
+/* .IP "\fBD (QMGR_REQ_SCAN_DEFERRED)\fR"
+/* Start a deferred queue scan. If a deferred queue scan is already
+/* in progress, that scan will be restarted as soon as it finishes.
+/* .IP "\fBI (QMGR_REQ_SCAN_INCOMING)\fR"
+/* Start an incoming queue scan. If an incoming queue scan is already
+/* in progress, that scan will be restarted as soon as it finishes.
+/* .IP "\fBA (QMGR_REQ_SCAN_ALL)\fR"
+/* Ignore deferred queue file time stamps. The request affects
+/* the next deferred queue scan.
+/* .IP "\fBF (QMGR_REQ_FLUSH_DEAD)\fR"
+/* Purge all information about dead transports and destinations.
+/* .IP "\fBW (TRIGGER_REQ_WAKEUP)\fR"
+/* Wakeup call, This is used by the master server to instantiate
+/* servers that should not go away forever. The action is to start
+/* an incoming queue scan.
+/* .PP
+/* The \fBqmgr\fR daemon reads an entire buffer worth of triggers.
+/* Multiple identical trigger requests are collapsed into one, and
+/* trigger requests are sorted so that \fBA\fR and \fBF\fR precede
+/* \fBD\fR and \fBI\fR. Thus, in order to force a deferred queue run,
+/* one would request \fBA F D\fR; in order to notify the queue manager
+/* of the arrival of new mail one would request \fBI\fR.
+/* STANDARDS
+/* .ad
+/* .fi
+/* None. The \fBqmgr\fR daemon does not interact with the outside world.
+/* SECURITY
+/* .ad
+/* .fi
+/* The \fBqmgr\fR daemon is not security sensitive. It reads
+/* single-character messages from untrusted local users, and thus may
+/* be susceptible to denial of service attacks. The \fBqmgr\fR daemon
+/* does not talk to the outside world, and it can be run at fixed low
+/* privilege in a chrooted environment.
+/* DIAGNOSTICS
+/* Problems and transactions are logged to the syslog daemon.
+/* Corrupted message files are saved to the \fBcorrupt\fR queue
+/* for further inspection.
+/*
+/* Depending on the setting of the \fBnotify_classes\fR parameter,
+/* the postmaster is notified of bounces and of other trouble.
+/* BUGS
+/* A single queue manager process has to compete for disk access with
+/* multiple front-end processes such as \fBsmtpd\fR. A sudden burst of
+/* inbound mail can negatively impact outbound delivery rates.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* The following \fBmain.cf\fR parameters are especially relevant to
+/* this program. See the Postfix \fBmain.cf\fR file for syntax details
+/* and for default values. Use the \fBpostfix reload\fR command after
+/* a configuration change.
+/* .SH Miscellaneous
+/* .ad
+/* .fi
+/* .IP \fBallow_min_user\fR
+/* Do not bounce recipient addresses that begin with '-'.
+/* .IP \fBrelocated_maps\fR
+/* Tables with contact information for users, hosts or domains
+/* that no longer exist. See \fBrelocated\fR(5).
+/* .IP \fBqueue_directory\fR
+/* Top-level directory of the Postfix queue.
+/* .SH "Active queue controls"
+/* .ad
+/* .fi
+/* In the text below, \fItransport\fR is the first field in a
+/* \fBmaster.cf\fR entry.
+/* .IP \fBqmgr_message_active_limit\fR
+/* Limit the number of messages in the active queue.
+/* .IP \fBqmgr_message_recipient_limit\fR
+/* Limit the number of in-memory recipients.
+/* .sp
+/* This parameter also limits the size of the short-term, in-memory
+/* destination cache.
+/* .IP \fBqmgr_message_recipient_minimum\fR
+/* Per message minimum of in-memory recipients.
+/* .IP \fBdefault_recipient_limit\fR
+/* Default limit on the number of in-memory recipients per transport.
+/* .IP \fItransport\fB_recipient_limit\fR
+/* Limit on the number of in-memory recipients, for the named
+/* message \fItransport\fR.
+/* .IP \fBdefault_extra_recipient_limit\fR
+/* Default limit on the total number of per transport in-memory
+/* recipients that the preempting messages can have.
+/* .IP \fItransport\fB_extra_recipient_limit\fR
+/* Limit on the number of in-memory recipients which all preempting
+/* messages delivered by the transport \fItransport\fR can have.
+/* .SH "Timing controls"
+/* .ad
+/* .fi
+/* .IP \fBmin_backoff\fR
+/* Minimal time in seconds between delivery attempts
+/* of a deferred message.
+/* .sp
+/* This parameter also limits the time an unreachable destination
+/* is kept in the short-term, in-memory destination status cache.
+/* .IP \fBmax_backoff\fR
+/* Maximal time in seconds between delivery attempts
+/* of a deferred message.
+/* .IP \fBmaximal_queue_lifetime\fR
+/* Maximal time in days a message is queued
+/* before it is sent back as undeliverable.
+/* .IP \fBqueue_run_delay\fR
+/* Time in seconds between deferred queue scans. Queue scans do
+/* not overlap.
+/* .IP \fBtransport_retry_time\fR
+/* Time in seconds between attempts to contact a broken
+/* delivery transport.
+/* .SH "Concurrency controls"
+/* .ad
+/* .fi
+/* .IP \fBinitial_destination_concurrency\fR
+/* Initial per-destination concurrency level for parallel delivery
+/* to the same destination.
+/* .IP \fBdefault_destination_concurrency_limit\fR
+/* Default limit on the number of parallel deliveries to the same
+/* destination.
+/* .IP \fItransport\fB_destination_concurrency_limit\fR
+/* Limit on the number of parallel deliveries to the same destination,
+/* for delivery via the named message \fItransport\fR.
+/* .SH "Recipient controls"
+/* .ad
+/* .fi
+/* .IP \fBdefault_destination_recipient_limit\fR
+/* Default limit on the number of recipients per message transfer.
+/* .IP \fItransport\fB_destination_recipient_limit\fR
+/* Limit on the number of recipients per message transfer, for the
+/* named message \fItransport\fR.
+/* .SH "Message scheduling"
+/* .ad
+/* .fi
+/* .IP "\fItransport\fB_delivery_slot_cost\fR (valid range: 0,2,3...)
+/* This parameter basically controls how often a message
+/* delivered by \fItransport\fR can be preempted by another
+/* message.
+/* An internal per-message/transport counter is incremented by one
+/* for each \fItransport\fB_delivery_slot_cost\fR
+/* deliveries handled by \fItransport\fR. This counter represents
+/* the number of "available delivery slots" for use by other messages.
+/* Current message can be preempted by another message when that
+/* other message can be delivered using less \fItransport\fR agents
+/* than the value of the "available delivery slots" counter.
+/* .sp
+/* Value equal to 0 disables the message preemption for \fItransport\fR.
+/* .IP \fItransport\fB_minimum_delivery_slots\fR
+/* Message preemption is not attempted at all whenever a message
+/* that can't ever accumulate at least \fItransport\fB_minimum_delivery_slots\fR
+/* available delivery slots is being delivered by \fItransport\fR.
+/* .IP "\fItransport\fB_delivery_slot_discount\fR (valid range: 0..100)"
+/* .IP \fItransport\fB_delivery_slot_loan\fR
+/* These parameters speed up the moment when a message preemption can happen.
+/* Instead of waiting until the full amount of delivery slots
+/* required is available, the preemption can happen when
+/* \fItransport\fB_delivery_slot_discount\fR percent of the required
+/* amount plus \fItransport\fB_delivery_slot_loan\fR still remains to
+/* be accumulated. Note that the full amount will still have to be
+/* accumulated before another preemption can take place later.
+/* .IP \fBdefault_delivery_slot_cost\fR
+/* .IP \fBdefault_minimum_delivery_slots\fR
+/* .IP \fBdefault_delivery_slot_discount\fR
+/* .IP \fBdefault_delivery_slot_loan\fR
+/* Default values for the transport specific parameters described above.
+/* SEE ALSO
+/* master(8), process manager
+/* relocated(5), format of the "user has moved" table
+/* syslogd(8) system logging
+/* trivial-rewrite(8), address routing
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <events.h>
+#include <vstream.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <mail_conf.h>
+#include <mail_params.h>
+#include <mail_proto.h> /* QMGR_SCAN constants */
+#include <mail_flow.h>
+
+/* Master process interface */
+
+#include <master_proto.h>
+#include <mail_server.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+ /*
+ * Tunables.
+ */
+int var_queue_run_delay;
+int var_min_backoff_time;
+int var_max_backoff_time;
+int var_max_queue_time;
+int var_qmgr_active_limit;
+int var_qmgr_rcpt_limit;
+int var_qmgr_msg_rcpt_limit;
+int var_xport_rcpt_limit;
+int var_stack_rcpt_limit;
+int var_delivery_slot_cost;
+int var_delivery_slot_loan;
+int var_delivery_slot_discount;
+int var_min_delivery_slots;
+int var_init_dest_concurrency;
+int var_transport_retry_time;
+int var_dest_con_limit;
+int var_dest_rcpt_limit;
+char *var_relocated_maps;
+char *var_virtual_maps;
+char *var_defer_xports;
+bool var_allow_min_user;
+int var_local_con_lim;
+int var_local_rcpt_lim;
+int var_proc_limit;
+bool var_verp_bounce_off;
+
+static QMGR_SCAN *qmgr_incoming;
+static QMGR_SCAN *qmgr_deferred;
+
+MAPS *qmgr_relocated;
+MAPS *qmgr_virtual;
+
+/* qmgr_deferred_run_event - queue manager heartbeat */
+
+static void qmgr_deferred_run_event(int unused_event, char *dummy)
+{
+
+ /*
+ * This routine runs when it is time for another deferred queue scan.
+ * Make sure this routine gets called again in the future.
+ */
+ qmgr_scan_request(qmgr_deferred, QMGR_SCAN_START);
+ event_request_timer(qmgr_deferred_run_event, dummy, var_queue_run_delay);
+}
+
+/* qmgr_trigger_event - respond to external trigger(s) */
+
+static void qmgr_trigger_event(char *buf, int len,
+ char *unused_service, char **argv)
+{
+ int incoming_flag = 0;
+ int deferred_flag = 0;
+ int i;
+
+ /*
+ * Sanity check. This service takes no command-line arguments.
+ */
+ if (argv[0])
+ msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+ /*
+ * Collapse identical requests that have arrived since we looked last
+ * time. There is no client feedback so there is no need to process each
+ * request in order. And as long as we don't have conflicting requests we
+ * are free to sort them into the most suitable order.
+ */
+ for (i = 0; i < len; i++) {
+ if (msg_verbose)
+ msg_info("request: %d (%c)",
+ buf[i], ISALNUM(buf[i]) ? buf[i] : '?');
+ switch (buf[i]) {
+ case TRIGGER_REQ_WAKEUP:
+ case QMGR_REQ_SCAN_INCOMING:
+ incoming_flag |= QMGR_SCAN_START;
+ break;
+ case QMGR_REQ_SCAN_DEFERRED:
+ deferred_flag |= QMGR_SCAN_START;
+ break;
+ case QMGR_REQ_FLUSH_DEAD:
+ deferred_flag |= QMGR_FLUSH_DEAD;
+ incoming_flag |= QMGR_FLUSH_DEAD;
+ break;
+ case QMGR_REQ_SCAN_ALL:
+ deferred_flag |= QMGR_SCAN_ALL;
+ incoming_flag |= QMGR_SCAN_ALL;
+ break;
+ default:
+ if (msg_verbose)
+ msg_info("request ignored");
+ break;
+ }
+ }
+
+ /*
+ * Process each request type at most once. Modifiers take effect upon the
+ * next queue run. If no queue run is in progress, and a queue scan is
+ * requested, the request takes effect immediately.
+ */
+ if (incoming_flag != 0)
+ qmgr_scan_request(qmgr_incoming, incoming_flag);
+ if (deferred_flag != 0)
+ qmgr_scan_request(qmgr_deferred, deferred_flag);
+}
+
+/* qmgr_loop - queue manager main loop */
+
+static int qmgr_loop(char *unused_name, char **unused_argv)
+{
+ char *in_path = 0;
+ char *df_path = 0;
+ int token_count;
+ int in_feed = 0;
+
+ /*
+ * This routine runs as part of the event handling loop, after the event
+ * manager has delivered a timer or I/O event (including the completion
+ * of a connection to a delivery process), or after it has waited for a
+ * specified amount of time. The result value of qmgr_loop() specifies
+ * how long the event manager should wait for the next event.
+ */
+#define DONT_WAIT 0
+#define WAIT_FOR_EVENT (-1)
+
+ /*
+ * Attempt to drain the active queue by allocating a suitable delivery
+ * process and by delivering mail via it. Delivery process allocation and
+ * mail delivery are asynchronous.
+ */
+ qmgr_active_drain();
+
+ /*
+ * Let some new blood into the active queue when the queue size is
+ * smaller than some configurable limit. When the system is under heavy
+ * load, favor new mail over old mail.
+ */
+ if (qmgr_message_count < var_qmgr_active_limit)
+ if ((in_path = qmgr_scan_next(qmgr_incoming)) != 0)
+ in_feed = qmgr_active_feed(qmgr_incoming, in_path);
+ if (qmgr_message_count < var_qmgr_active_limit)
+ if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0)
+ qmgr_active_feed(qmgr_deferred, df_path);
+
+ /*
+ * Global flow control. If enabled, slow down receiving processes that
+ * get ahead of the queue manager, but don't block them completely.
+ */
+ if (var_in_flow_delay > 0) {
+ token_count = mail_flow_count();
+ if (token_count < var_proc_limit) {
+ if (in_feed != 0)
+ mail_flow_put(1);
+ else if (qmgr_incoming->handle == 0)
+ mail_flow_put(var_proc_limit - token_count);
+ } else if (token_count > var_proc_limit) {
+ mail_flow_get(token_count - var_proc_limit);
+ }
+ }
+ if (in_path || df_path)
+ return (DONT_WAIT);
+ return (WAIT_FOR_EVENT);
+}
+
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(char *unused_name, char **unused_argv)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
+}
+
+/* qmgr_pre_init - pre-jail initialization */
+
+static void qmgr_pre_init(char *unused_name, char **unused_argv)
+{
+ if (*var_relocated_maps)
+ qmgr_relocated = maps_create("relocated", var_relocated_maps,
+ DICT_FLAG_LOCK);
+ if (*var_virtual_maps)
+ qmgr_virtual = maps_create("virtual", var_virtual_maps,
+ DICT_FLAG_LOCK);
+}
+
+/* qmgr_post_init - post-jail initialization */
+
+static void qmgr_post_init(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * This routine runs after the skeleton code has entered the chroot jail.
+ * Prevent automatic process suicide after a limited number of client
+ * requests or after a limited amount of idle time. Move any left-over
+ * entries from the active queue to the incoming queue, and give them a
+ * time stamp into the future, in order to allow ongoing deliveries to
+ * finish first. Start scanning the incoming and deferred queues.
+ * Left-over active queue entries are moved to the incoming queue because
+ * the incoming queue has priority; moving left-overs to the deferred
+ * queue could cause anomalous delays when "postfix reload/start" are
+ * issued often.
+ */
+ var_use_limit = 0;
+ var_idle_limit = 0;
+ qmgr_move(MAIL_QUEUE_ACTIVE, MAIL_QUEUE_INCOMING, event_time());
+ qmgr_incoming = qmgr_scan_create(MAIL_QUEUE_INCOMING);
+ qmgr_deferred = qmgr_scan_create(MAIL_QUEUE_DEFERRED);
+ qmgr_scan_request(qmgr_incoming, QMGR_SCAN_START);
+ qmgr_deferred_run_event(0, (char *) 0);
+}
+
+/* main - the main program */
+
+int main(int argc, char **argv)
+{
+ static CONFIG_STR_TABLE str_table[] = {
+ VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
+ VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0,
+ VAR_DEFER_XPORTS, DEF_DEFER_XPORTS, &var_defer_xports, 0, 0,
+ 0,
+ };
+ static CONFIG_TIME_TABLE time_table[] = {
+ VAR_QUEUE_RUN_DELAY, DEF_QUEUE_RUN_DELAY, &var_queue_run_delay, 1, 0,
+ VAR_MIN_BACKOFF_TIME, DEF_MIN_BACKOFF_TIME, &var_min_backoff_time, 1, 0,
+ VAR_MAX_BACKOFF_TIME, DEF_MAX_BACKOFF_TIME, &var_max_backoff_time, 1, 0,
+ VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 1, 8640000,
+ VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0,
+ 0,
+ };
+ static CONFIG_INT_TABLE int_table[] = {
+ VAR_QMGR_ACT_LIMIT, DEF_QMGR_ACT_LIMIT, &var_qmgr_active_limit, 1, 0,
+ VAR_QMGR_RCPT_LIMIT, DEF_QMGR_RCPT_LIMIT, &var_qmgr_rcpt_limit, 1, 0,
+ VAR_QMGR_MSG_RCPT_LIMIT, DEF_QMGR_MSG_RCPT_LIMIT, &var_qmgr_msg_rcpt_limit, 1, 0,
+ VAR_XPORT_RCPT_LIMIT, DEF_XPORT_RCPT_LIMIT, &var_xport_rcpt_limit, 0, 0,
+ VAR_STACK_RCPT_LIMIT, DEF_STACK_RCPT_LIMIT, &var_stack_rcpt_limit, 0, 0,
+ VAR_DELIVERY_SLOT_COST, DEF_DELIVERY_SLOT_COST, &var_delivery_slot_cost, 0, 0,
+ VAR_DELIVERY_SLOT_LOAN, DEF_DELIVERY_SLOT_LOAN, &var_delivery_slot_loan, 0, 0,
+ VAR_DELIVERY_SLOT_DISCOUNT, DEF_DELIVERY_SLOT_DISCOUNT, &var_delivery_slot_discount, 0, 100,
+ VAR_MIN_DELIVERY_SLOTS, DEF_MIN_DELIVERY_SLOTS, &var_min_delivery_slots, 0, 0,
+ VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0,
+ VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0,
+ VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0,
+ VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0,
+ VAR_LOCAL_CON_LIMIT, DEF_LOCAL_CON_LIMIT, &var_local_con_lim, 0, 0,
+ VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0,
+ 0,
+ };
+ static CONFIG_BOOL_TABLE bool_table[] = {
+ VAR_ALLOW_MIN_USER, DEF_ALLOW_MIN_USER, &var_allow_min_user,
+ VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off,
+ 0,
+ };
+
+ /*
+ * Use the trigger service skeleton, because no-one else should be
+ * monitoring our service port while this process runs, and because we do
+ * not talk back to the client.
+ */
+ trigger_server_main(argc, argv, qmgr_trigger_event,
+ MAIL_SERVER_INT_TABLE, int_table,
+ MAIL_SERVER_STR_TABLE, str_table,
+ MAIL_SERVER_BOOL_TABLE, bool_table,
+ MAIL_SERVER_TIME_TABLE, time_table,
+ MAIL_SERVER_PRE_INIT, qmgr_pre_init,
+ MAIL_SERVER_POST_INIT, qmgr_post_init,
+ MAIL_SERVER_LOOP, qmgr_loop,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ 0);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr.h b/gnu/dist/postfix/src/nqmgr/qmgr.h
new file mode 100644
index 00000000000..8749c35605c
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr.h
@@ -0,0 +1,431 @@
+/*++
+/* NAME
+/* qmgr 3h
+/* SUMMARY
+/* queue manager data structures
+/* SYNOPSIS
+/* #include "qmgr.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <scan_dir.h>
+
+ /*
+ * Global library.
+ */
+#include <maps.h>
+
+ /*
+ * The queue manager is built around lots of mutually-referring structures.
+ * These typedefs save some typing.
+ */
+typedef struct QMGR_TRANSPORT QMGR_TRANSPORT;
+typedef struct QMGR_QUEUE QMGR_QUEUE;
+typedef struct QMGR_ENTRY QMGR_ENTRY;
+typedef struct QMGR_MESSAGE QMGR_MESSAGE;
+typedef struct QMGR_JOB QMGR_JOB;
+typedef struct QMGR_PEER QMGR_PEER;
+typedef struct QMGR_TRANSPORT_LIST QMGR_TRANSPORT_LIST;
+typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST;
+typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST;
+typedef struct QMGR_JOB_LIST QMGR_JOB_LIST;
+typedef struct QMGR_PEER_LIST QMGR_PEER_LIST;
+typedef struct QMGR_RCPT QMGR_RCPT;
+typedef struct QMGR_RCPT_LIST QMGR_RCPT_LIST;
+typedef struct QMGR_SCAN QMGR_SCAN;
+
+ /*
+ * Hairy macros to update doubly-linked lists.
+ */
+#define QMGR_LIST_ROTATE(head, object, peers) { \
+ head.next->peers.prev = head.prev; \
+ head.prev->peers.next = head.next; \
+ head.next = object->peers.next; \
+ head.next->peers.prev = 0; \
+ head.prev = object; \
+ object->peers.next = 0; \
+}
+
+#define QMGR_LIST_UNLINK(head, type, object, peers) { \
+ type next = object->peers.next; \
+ type prev = object->peers.prev; \
+ if (prev) prev->peers.next = next; \
+ else head.next = next; \
+ if (next) next->peers.prev = prev; \
+ else head.prev = prev; \
+ object->peers.next = object->peers.prev = 0; \
+}
+
+#define QMGR_LIST_LINK(head, pred, object, succ, peers) { \
+ object->peers.prev = pred; \
+ object->peers.next = succ; \
+ if (pred) pred->peers.next = object; \
+ else head.next = object; \
+ if (succ) succ->peers.prev = object; \
+ else head.prev = object; \
+}
+
+#define QMGR_LIST_PREPEND(head, object, peers) { \
+ object->peers.next = head.next; \
+ object->peers.prev = 0; \
+ if (head.next) { \
+ head.next->peers.prev = object; \
+ } else { \
+ head.prev = object; \
+ } \
+ head.next = object; \
+}
+
+#define QMGR_LIST_APPEND(head, object, peers) { \
+ object->peers.prev = head.prev; \
+ object->peers.next = 0; \
+ if (head.prev) { \
+ head.prev->peers.next = object; \
+ } else { \
+ head.next = object; \
+ } \
+ head.prev = object; \
+}
+
+#define QMGR_LIST_INIT(head) { \
+ head.prev = 0; \
+ head.next = 0; \
+}
+
+ /*
+ * Transports are looked up by name (when we have resolved a message), or
+ * round-robin wise (when we want to distribute resources fairly).
+ */
+struct QMGR_TRANSPORT_LIST {
+ QMGR_TRANSPORT *next;
+ QMGR_TRANSPORT *prev;
+};
+
+extern struct HTABLE *qmgr_transport_byname; /* transport by name */
+extern QMGR_TRANSPORT_LIST qmgr_transport_list; /* transports, round robin */
+
+ /*
+ * Each transport (local, smtp-out, bounce) can have one queue per next hop
+ * name. Queues are looked up by next hop name (when we have resolved a
+ * message destination), or round-robin wise (when we want to deliver
+ * messages fairly).
+ */
+struct QMGR_QUEUE_LIST {
+ QMGR_QUEUE *next;
+ QMGR_QUEUE *prev;
+};
+
+struct QMGR_JOB_LIST {
+ QMGR_JOB *next;
+ QMGR_JOB *prev;
+};
+
+struct QMGR_TRANSPORT {
+ int flags; /* blocked, etc. */
+ char *name; /* transport name */
+ int dest_concurrency_limit; /* concurrency per domain */
+ int init_dest_concurrency; /* init. per-domain concurrency */
+ int recipient_limit; /* recipients per transaction */
+ int rcpt_per_stack; /* extra slots reserved for jobs put
+ * on the job stack */
+ int rcpt_unused; /* available in-core recipient slots */
+ int slot_cost; /* cost of new preemption slot (# of
+ * selected entries) */
+ int slot_loan; /* preemption boost offset and */
+ int slot_loan_factor; /* factor, see qmgr_job_preempt() */
+ int min_slots; /* when preemption can take effect at
+ * all */
+ struct HTABLE *queue_byname; /* queues indexed by domain */
+ QMGR_QUEUE_LIST queue_list; /* queues, round robin order */
+ struct HTABLE *job_byname; /* jobs indexed by queue id */
+ QMGR_JOB_LIST job_list; /* list of message jobs (1 per
+ * message) ordered by scheduler */
+ QMGR_JOB_LIST job_bytime; /* jobs ordered by time since queued */
+ QMGR_JOB *job_current; /* keeps track of the current job */
+ QMGR_JOB *job_next_unread; /* next job with unread recipients */
+ QMGR_JOB *candidate_cache; /* cached result from
+ * qmgr_job_candidate() */
+ QMGR_JOB *candidate_cache_current; /* current job tied to the candidate */
+ time_t candidate_cache_time; /* when candidate_cache was last
+ * updated */
+ int blocker_tag; /* for marking blocker jobs */
+ QMGR_TRANSPORT_LIST peers; /* linkage */
+ char *reason; /* why unavailable */
+};
+
+#define QMGR_TRANSPORT_STAT_DEAD (1<<1)
+#define QMGR_TRANSPORT_STAT_BUSY (1<<2)
+
+typedef void (*QMGR_TRANSPORT_ALLOC_NOTIFY) (QMGR_TRANSPORT *, VSTREAM *);
+extern QMGR_TRANSPORT *qmgr_transport_select(void);
+extern void qmgr_transport_alloc(QMGR_TRANSPORT *, QMGR_TRANSPORT_ALLOC_NOTIFY);
+extern void qmgr_transport_throttle(QMGR_TRANSPORT *, const char *);
+extern void qmgr_transport_unthrottle(QMGR_TRANSPORT *);
+extern QMGR_TRANSPORT *qmgr_transport_create(const char *);
+extern QMGR_TRANSPORT *qmgr_transport_find(const char *);
+
+ /*
+ * Each next hop (e.g., a domain name) has its own queue of pending message
+ * transactions. The "todo" queue contains messages that are to be delivered
+ * to this next hop. When a message is elected for transmission, it is moved
+ * from the "todo" queue to the "busy" queue. Messages are taken from the
+ * "todo" queue in round-robin order.
+ */
+struct QMGR_ENTRY_LIST {
+ QMGR_ENTRY *next;
+ QMGR_ENTRY *prev;
+};
+
+struct QMGR_QUEUE {
+ char *name; /* domain name */
+ int todo_refcount; /* queue entries (todo list) */
+ int busy_refcount; /* queue entries (busy list) */
+ int window; /* slow open algorithm */
+ QMGR_TRANSPORT *transport; /* transport linkage */
+ QMGR_ENTRY_LIST todo; /* todo queue entries */
+ QMGR_ENTRY_LIST busy; /* messages on the wire */
+ QMGR_QUEUE_LIST peers; /* neighbor queues */
+ char *reason; /* why unavailable */
+ int blocker_tag; /* tagged if blocks job list */
+};
+
+#define QMGR_QUEUE_TODO 1 /* waiting for service */
+#define QMGR_QUEUE_BUSY 2 /* recipients on the wire */
+
+extern int qmgr_queue_count;
+
+extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *);
+extern void qmgr_queue_done(QMGR_QUEUE *);
+extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *);
+extern void qmgr_queue_unthrottle(QMGR_QUEUE *);
+extern QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *, const char *);
+
+ /*
+ * Structure for a recipient list. Initially, it just contains recipient
+ * addresses and file offsets. After the address resolver has done its work,
+ * each recipient is accompanied by a reference to a specific queues (which
+ * implies a specific transport). This is an extended version of similar
+ * information maintained by the recipient_list(3) module.
+ */
+struct QMGR_RCPT {
+ long offset; /* REC_TYPE_RCPT byte */
+ char *address; /* complete address */
+ QMGR_QUEUE *queue; /* resolved queue */
+};
+
+struct QMGR_RCPT_LIST {
+ QMGR_RCPT *info;
+ int len;
+ int avail;
+};
+
+extern void qmgr_rcpt_list_init(QMGR_RCPT_LIST *);
+extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *);
+extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
+
+ /*
+ * Structure of one next-hop queue entry. In order to save some copying
+ * effort we allow multiple recipients per transaction.
+ */
+struct QMGR_ENTRY {
+ VSTREAM *stream; /* delivery process */
+ QMGR_MESSAGE *message; /* message info */
+ QMGR_RCPT_LIST rcpt_list; /* as many as it takes */
+ QMGR_QUEUE *queue; /* parent linkage */
+ QMGR_PEER *peer; /* parent linkage */
+ QMGR_ENTRY_LIST queue_peers; /* per queue neighbor entries */
+ QMGR_ENTRY_LIST peer_peers; /* per peer neighbor entries */
+};
+
+extern QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *);
+extern void qmgr_entry_unselect(QMGR_ENTRY *);
+extern void qmgr_entry_done(QMGR_ENTRY *, int);
+extern QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *, QMGR_MESSAGE *);
+
+ /*
+ * All common in-core information about a message is kept here. When all
+ * recipients have been tried the message file is linked to the "deferred"
+ * queue (some hosts not reachable), to the "bounce" queue (some recipients
+ * were rejected), and is then removed from the "active" queue.
+ */
+struct QMGR_MESSAGE {
+ int flags; /* delivery problems */
+ int qflags; /* queuing flags */
+ VSTREAM *fp; /* open queue file or null */
+ int refcount; /* queue entries */
+ int single_rcpt; /* send one rcpt at a time */
+ long arrival_time; /* time when queued */
+ time_t queued_time; /* time when moved to the active
+ * queue */
+ long warn_offset; /* warning bounce flag offset */
+ time_t warn_time; /* time next warning to be sent */
+ long data_offset; /* data seek offset */
+ char *queue_name; /* queue name */
+ char *queue_id; /* queue file */
+ char *sender; /* complete address */
+ char *verp_delims; /* VERP delimiters */
+ char *errors_to; /* error report address */
+ char *return_receipt; /* confirm receipt address */
+ char *filter_xport; /* filtering transport */
+ char *inspect_xport; /* inspecting transport */
+ long data_size; /* message content size */
+ long rcpt_offset; /* more recipients here */
+ long unread_offset; /* more unread recipients here */
+ QMGR_RCPT_LIST rcpt_list; /* complete addresses */
+ int rcpt_count; /* used recipient slots */
+ int rcpt_limit; /* maximum read in-core */
+ int rcpt_unread; /* # of recipients left in queue file */
+ QMGR_JOB_LIST job_list; /* jobs delivering this message (1
+ * per transport) */
+};
+
+#define QMGR_MESSAGE_LOCKED ((QMGR_MESSAGE *) 1)
+
+extern int qmgr_message_count;
+extern int qmgr_recipient_count;
+extern MAPS *qmgr_relocated;
+extern MAPS *qmgr_virtual;
+
+extern void qmgr_message_free(QMGR_MESSAGE *);
+extern void qmgr_message_update_warn(QMGR_MESSAGE *);
+extern QMGR_MESSAGE *qmgr_message_alloc(const char *, const char *, int);
+extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *);
+
+ /*
+ * Sometimes it's required to access the transport queues and entries on per
+ * message basis. That's what the QMGR_JOB structure is for - it groups all
+ * per message information within each transport using a list of QMGR_PEER
+ * structures. These structures in turn correspond with per message
+ * QMGR_QUEUE structure and list all per message QMGR_ENTRY structures.
+ */
+struct QMGR_PEER_LIST {
+ QMGR_PEER *next;
+ QMGR_PEER *prev;
+};
+
+struct QMGR_JOB {
+ QMGR_MESSAGE *message; /* message delivered by this job */
+ QMGR_TRANSPORT *transport; /* transport this job belongs to */
+ QMGR_JOB_LIST message_peers; /* per message neighbor linkage */
+ QMGR_JOB_LIST transport_peers; /* per transport neighbor linkage */
+ QMGR_JOB_LIST time_peers; /* by time neighbor linkage */
+ QMGR_JOB *stack_parent; /* stack parent */
+ QMGR_JOB_LIST stack_children; /* all stack children */
+ QMGR_JOB_LIST stack_siblings; /* stack children linkage */
+ int stack_level; /* job stack nesting level (-1 means
+ * it's not on the lists at all) */
+ int blocker_tag; /* tagged if blocks the job list */
+ struct HTABLE *peer_byname; /* message job peers, indexed by
+ * domain */
+ QMGR_PEER_LIST peer_list; /* list of message job peers */
+ int slots_used; /* slots used during preemption */
+ int slots_available; /* slots available for preemption (in
+ * multiples of slot_cost) */
+ int selected_entries; /* # of entries selected for delivery
+ * so far */
+ int read_entries; /* # of entries read in-core so far */
+ int rcpt_count; /* used recipient slots */
+ int rcpt_limit; /* available recipient slots */
+};
+
+struct QMGR_PEER {
+ QMGR_JOB *job; /* job handling this peer */
+ QMGR_QUEUE *queue; /* queue corresponding with this peer */
+ int refcount; /* peer entries */
+ QMGR_ENTRY_LIST entry_list; /* todo message entries queued for
+ * this peer */
+ QMGR_PEER_LIST peers; /* neighbor linkage */
+};
+
+extern QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *);
+extern QMGR_PEER *qmgr_peer_select(QMGR_JOB *);
+
+extern QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *, QMGR_TRANSPORT *);
+extern void qmgr_job_free(QMGR_JOB *);
+extern void qmgr_job_move_limits(QMGR_JOB *);
+
+extern QMGR_PEER *qmgr_peer_create(QMGR_JOB *, QMGR_QUEUE *);
+extern QMGR_PEER *qmgr_peer_find(QMGR_JOB *, QMGR_QUEUE *);
+extern void qmgr_peer_free(QMGR_PEER *);
+
+ /*
+ * qmgr_defer.c
+ */
+extern void qmgr_defer_transport(QMGR_TRANSPORT *, const char *);
+extern void qmgr_defer_todo(QMGR_QUEUE *, const char *);
+extern void qmgr_defer_recipient(QMGR_MESSAGE *, const char *, const char *);
+
+ /*
+ * qmgr_bounce.c
+ */
+extern void PRINTFLIKE(3, 4) qmgr_bounce_recipient(QMGR_MESSAGE *, QMGR_RCPT *, const char *,...);
+
+ /*
+ * qmgr_deliver.c
+ */
+extern int qmgr_deliver_concurrency;
+extern void qmgr_deliver(QMGR_TRANSPORT *, VSTREAM *);
+
+ /*
+ * qmgr_active.c
+ */
+extern int qmgr_active_feed(QMGR_SCAN *, const char *);
+extern void qmgr_active_drain(void);
+extern void qmgr_active_done(QMGR_MESSAGE *);
+
+ /*
+ * qmgr_move.c
+ */
+extern void qmgr_move(const char *, const char *, time_t);
+
+ /*
+ * qmgr_enable.c
+ */
+extern void qmgr_enable_all(void);
+extern void qmgr_enable_transport(QMGR_TRANSPORT *);
+extern void qmgr_enable_queue(QMGR_QUEUE *);
+
+ /*
+ * Queue scan context.
+ */
+struct QMGR_SCAN {
+ char *queue; /* queue name */
+ int flags; /* private, this run */
+ int nflags; /* private, next run */
+ struct SCAN_DIR *handle; /* scan */
+};
+
+ /*
+ * Flags that control queue scans or destination selection. These are
+ * similar to the QMGR_REQ_XXX request codes.
+ */
+#define QMGR_SCAN_START (1<<0) /* start now/restart when done */
+#define QMGR_SCAN_ALL (1<<1) /* all queue file time stamps */
+#define QMGR_FLUSH_DEAD (1<<2) /* all sites, all transports */
+
+ /*
+ * qmgr_scan.c
+ */
+extern QMGR_SCAN *qmgr_scan_create(const char *);
+extern void qmgr_scan_request(QMGR_SCAN *, int);
+extern char *qmgr_scan_next(QMGR_SCAN *);
+
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_active.c b/gnu/dist/postfix/src/nqmgr/qmgr_active.c
new file mode 100644
index 00000000000..722d810739c
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_active.c
@@ -0,0 +1,500 @@
+/*++
+/* NAME
+/* qmgr_active 3
+/* SUMMARY
+/* active queue management
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* void qmgr_active_feed(scan_info, queue_id)
+/* QMGR_SCAN *scan_info;
+/* const char *queue_id;
+/*
+/* void qmgr_active_drain()
+/*
+/* int qmgr_active_done(message)
+/* QMGR_MESSAGE *message;
+/* DESCRIPTION
+/* These functions maintain the active message queue: the set
+/* of messages that the queue manager is actually working on.
+/* The active queue is limited in size. Messages are drained
+/* from the active queue by allocating a delivery process and
+/* by delivering mail via that process. Messages leak into the
+/* active queue only when the active queue is small enough.
+/* Damaged message files are saved to the "corrupt" directory.
+/*
+/* qmgr_active_feed() inserts the named message file into
+/* the active queue. Message files with the wrong name or
+/* with other wrong properties are skipped but not removed.
+/* The following queue flags are recognized, other flags being
+/* ignored:
+/* .IP QMGR_SCAN_ALL
+/* Examine all queue files. Normally, deferred queue files with
+/* future time stamps are ignored, and incoming queue files with
+/* future time stamps are frowned upon.
+/* .PP
+/* qmgr_active_drain() allocates one delivery process.
+/* Process allocation is asynchronous. Once the delivery
+/* process is available, an attempt is made to deliver
+/* a message via it. Message delivery is asynchronous, too.
+/*
+/* qmgr_active_done() deals with a message after delivery
+/* has been tried for all in-core recipients. If the message
+/* was bounced, a bounce message is sent to the sender, or
+/* to the Errors-To: address if one was specified.
+/* If there are more on-file recipients, a new batch of
+/* in-core recipients is read from the queue file. Otherwise,
+/* if a delivery agent marked the queue file as corrupt,
+/* the queue file is moved to the "corrupt" queue (surprise);
+/* if at least one delivery failed, the message is moved
+/* to the deferred queue. The time stamps of a deferred queue
+/* file are set to the nearest wakeup time of its recipient
+/* sites (if delivery failed due to a problem with a next-hop
+/* host), are set into the future by the amount of time the
+/* message was queued (per-message exponential backoff), or are set
+/* into the future by a minimal backoff time, whichever is more.
+/* The minimal_backoff_time parameter specifies the minimal
+/* amount of time between delivery attempts; maximal_backoff_time
+/* specifies an upper limit.
+/* DIAGNOSTICS
+/* Fatal: queue file access failures, out of memory.
+/* Panic: interface violations, internal consistency errors.
+/* Warnings: corrupt message file. A corrupt message is saved
+/* to the "corrupt" queue for further inspection.
+/* 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 <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <utime.h>
+#include <errno.h>
+
+#ifndef S_IRWXU /* What? no POSIX system? */
+#define S_IRWXU 0700
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <events.h>
+#include <mymalloc.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_open_ok.h>
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <bounce.h>
+#include <defer.h>
+#include <abounce.h>
+#include <rec_type.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+ /*
+ * A bunch of call-back routines.
+ */
+static void qmgr_active_done_2_bounce_flush(int, char *);
+static void qmgr_active_done_2_generic(QMGR_MESSAGE *);
+static void qmgr_active_done_3_defer_flush(int, char *);
+static void qmgr_active_done_3_defer_warn(int, char *);
+static void qmgr_active_done_3_generic(QMGR_MESSAGE *);
+
+/* qmgr_active_corrupt - move corrupted file out of the way */
+
+static void qmgr_active_corrupt(const char *queue_id)
+{
+ char *myname = "qmgr_active_corrupt";
+
+ if (mail_queue_rename(queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT)) {
+ if (errno != ENOENT)
+ msg_fatal("%s: save corrupt file queue %s id %s: %m",
+ myname, MAIL_QUEUE_ACTIVE, queue_id);
+ msg_warn("%s: save corrupt file queue %s id %s: %m",
+ myname, MAIL_QUEUE_ACTIVE, queue_id);
+ } else {
+ msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"",
+ queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
+ }
+}
+
+/* qmgr_active_defer - defer queue file */
+
+static void qmgr_active_defer(const char *queue_name, const char *queue_id,
+ const char *dest_queue, int delay)
+{
+ char *myname = "qmgr_active_defer";
+ const char *path;
+ struct utimbuf tbuf;
+
+ if (msg_verbose)
+ msg_info("wakeup %s after %ld secs", queue_id, (long) delay);
+
+ tbuf.actime = tbuf.modtime = event_time() + delay;
+ path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
+ if (utime(path, &tbuf) < 0 && errno != ENOENT)
+ msg_fatal("%s: update %s time stamps: %m", myname, path);
+ if (mail_queue_rename(queue_id, queue_name, dest_queue)) {
+ if (errno != ENOENT)
+ msg_fatal("%s: rename %s from %s to %s: %m", myname,
+ queue_id, queue_name, dest_queue);
+ msg_warn("%s: rename %s from %s to %s: %m", myname,
+ queue_id, queue_name, dest_queue);
+ } else if (msg_verbose) {
+ msg_info("%s: defer %s", myname, queue_id);
+ }
+}
+
+/* qmgr_active_feed - feed one message into active queue */
+
+int qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
+{
+ char *myname = "qmgr_active_feed";
+ QMGR_MESSAGE *message;
+ struct stat st;
+ const char *path;
+
+ if (strcmp(scan_info->queue, MAIL_QUEUE_ACTIVE) == 0)
+ msg_panic("%s: bad queue %s", myname, scan_info->queue);
+ if (msg_verbose)
+ msg_info("%s: queue %s", myname, scan_info->queue);
+
+ /*
+ * Make sure this is something we are willing to open.
+ */
+ if (mail_open_ok(scan_info->queue, queue_id, &st, &path) == MAIL_OPEN_NO)
+ return (0);
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, path);
+
+ /*
+ * Skip files that have time stamps into the future. They need to cool
+ * down. Incoming and deferred files can have future time stamps.
+ */
+ if ((scan_info->flags & QMGR_SCAN_ALL) == 0
+ && st.st_mtime > time((time_t *) 0) + 1) {
+ if (msg_verbose)
+ msg_info("%s: skip %s (%ld seconds)", myname, queue_id,
+ (long) (st.st_mtime - event_time()));
+ return (0);
+ }
+
+ /*
+ * Move the message to the active queue. File access errors are fatal.
+ */
+ if (mail_queue_rename(queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE)) {
+ if (errno != ENOENT)
+ msg_fatal("%s: %s: rename from %s to %s: %m", myname,
+ queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
+ msg_warn("%s: %s: rename from %s to %s: %m", myname,
+ queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
+ return (0);
+ }
+
+ /*
+ * Extract envelope information: sender and recipients. At this point,
+ * mail addresses have been processed by the cleanup service so they
+ * should be in canonical form. Generate requests to deliver this
+ * message.
+ *
+ * Throwing away queue files seems bad, especially when they made it this
+ * far into the mail system. Therefore we save bad files to a separate
+ * directory for further inspection.
+ *
+ * After queue manager restart it is possible that a queue file is still
+ * being delivered. In that case (the file is locked), defer delivery by
+ * a minimal amount of time.
+ */
+ if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id,
+ scan_info->flags)) == 0) {
+ qmgr_active_corrupt(queue_id);
+ return (0);
+ } else if (message == QMGR_MESSAGE_LOCKED) {
+ qmgr_active_defer(MAIL_QUEUE_ACTIVE, queue_id, MAIL_QUEUE_INCOMING, 60);
+ return (0);
+ } else {
+
+ /*
+ * Special case if all recipients were already delivered. Send any
+ * bounces and clean up.
+ */
+ if (message->refcount == 0)
+ qmgr_active_done(message);
+ return (1);
+ }
+}
+
+/* qmgr_active_done - dispose of message after recipients have been tried */
+
+void qmgr_active_done(QMGR_MESSAGE *message)
+{
+ char *myname = "qmgr_active_done";
+ struct stat st;
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, message->queue_id);
+
+ /*
+ * During a previous iteration, an attempt to bounce this message may
+ * have failed, so there may still be a bounce log lying around. XXX By
+ * groping around in the bounce queue, we're trespassing on the bounce
+ * service's territory. But doing so is more robust than depending on the
+ * bounce daemon to do the lookup for us, and for us to do the deleting
+ * after we have received a successful status from the bounce service.
+ * The bounce queue directory blocks are most likely in memory anyway. If
+ * these lookups become a performance problem we will have to build an
+ * in-core cache into the bounce daemon.
+ *
+ * Don't bounce when the bounce log is empty. The bounce process obviously
+ * failed, and the delivery agent will have requested that the message be
+ * deferred.
+ *
+ * Bounces are sent asynchronously to avoid stalling while the cleanup
+ * daemon waits for the qmgr to accept the "new mail" trigger.
+ */
+ if (stat(mail_queue_path((VSTRING *) 0, MAIL_QUEUE_BOUNCE, message->queue_id), &st) == 0) {
+ if (st.st_size == 0) {
+ if (mail_queue_remove(MAIL_QUEUE_BOUNCE, message->queue_id))
+ msg_fatal("remove %s %s: %m",
+ MAIL_QUEUE_BOUNCE, message->queue_id);
+ } else {
+ if (msg_verbose)
+ msg_info("%s: bounce %s", myname, message->queue_id);
+ if (message->verp_delims == 0 || var_verp_bounce_off)
+ abounce_flush(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ qmgr_active_done_2_bounce_flush,
+ (char *) message);
+ else
+ abounce_flush_verp(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ message->verp_delims,
+ qmgr_active_done_2_bounce_flush,
+ (char *) message);
+ return;
+ }
+ }
+
+ /*
+ * Asynchronous processing does not reach this point.
+ */
+ qmgr_active_done_2_generic(message);
+}
+
+/* qmgr_active_done_2_bounce_flush - process abounce_flush() status */
+
+static void qmgr_active_done_2_bounce_flush(int status, char *context)
+{
+ QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
+
+ /*
+ * Process abounce_flush() status and continue processing.
+ */
+ message->flags |= status;
+ qmgr_active_done_2_generic(message);
+}
+
+/* qmgr_active_done_2_generic - continue processing */
+
+static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
+{
+ char *myname = "qmgr_active_done_2_generic";
+ const char *path;
+ struct stat st;
+
+ /*
+ * A delivery agent marks a queue file as corrupt by changing its
+ * attributes, and by pretending that delivery was deferred.
+ */
+ if (message->flags
+ && mail_open_ok(MAIL_QUEUE_ACTIVE, message->queue_id, &st, &path) == MAIL_OPEN_NO) {
+ qmgr_active_corrupt(message->queue_id);
+ qmgr_message_free(message);
+ return;
+ }
+
+ /*
+ * If we did not read all recipients from this file, go read some more,
+ * but remember whether some recipients have to be tried again.
+ *
+ * Throwing away queue files seems bad, especially when they made it this
+ * far into the mail system. Therefore we save bad files to a separate
+ * directory for further inspection by a human being.
+ */
+ if (message->rcpt_offset > 0) {
+ if (qmgr_message_realloc(message) == 0) {
+ qmgr_active_corrupt(message->queue_id);
+ qmgr_message_free(message);
+ } else {
+ if (message->refcount == 0)
+ qmgr_active_done(message); /* recurse for consistency */
+ }
+ return;
+ }
+
+ /*
+ * If we get to this point we have tried all recipients for this message.
+ * If the message is too old, try to bounce it.
+ *
+ * Bounces are sent asynchronously to avoid stalling while the cleanup
+ * daemon waits for the qmgr to accept the "new mail" trigger.
+ */
+ if (message->flags) {
+ if (event_time() > message->arrival_time + var_max_queue_time) {
+ msg_info("%s: from=<%s>, status=expired, returned to sender",
+ message->queue_id, message->sender);
+ if (message->verp_delims == 0 || var_verp_bounce_off)
+ adefer_flush(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ qmgr_active_done_3_defer_flush,
+ (char *) message);
+ else
+ adefer_flush_verp(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ message->verp_delims,
+ qmgr_active_done_3_defer_flush,
+ (char *) message);
+ return;
+ } else if (message->warn_time > 0
+ && event_time() > message->warn_time) {
+ if (msg_verbose)
+ msg_info("%s: sending defer warning for %s", myname, message->queue_id);
+ adefer_warn(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ qmgr_active_done_3_defer_warn,
+ (char *) message);
+ return;
+ }
+ }
+
+ /*
+ * Asynchronous processing does not reach this point.
+ */
+ qmgr_active_done_3_generic(message);
+}
+
+/* qmgr_active_done_3_defer_warn - continue after adefer_warn() completion */
+
+static void qmgr_active_done_3_defer_warn(int status, char *context)
+{
+ QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
+
+ /*
+ * Process adefer_warn() completion status and continue processing.
+ */
+ if (status == 0)
+ qmgr_message_update_warn(message);
+ qmgr_active_done_3_generic(message);
+}
+
+/* qmgr_active_done_3_defer_flush - continue after adefer_flush() completion */
+
+static void qmgr_active_done_3_defer_flush(int status, char *context)
+{
+ QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
+
+ /*
+ * Process adefer_flush() status and continue processing.
+ */
+ message->flags = status;
+ qmgr_active_done_3_generic(message);
+}
+
+/* qmgr_active_done_3_generic - continue processing */
+
+static void qmgr_active_done_3_generic(QMGR_MESSAGE *message)
+{
+ char *myname = "qmgr_active_done_3_generic";
+ int delay;
+
+ /*
+ * Some recipients need to be tried again. Move the queue file time
+ * stamps into the future by the amount of time that the message is
+ * delayed, and move the message to the deferred queue. Impose minimal
+ * and maximal backoff times.
+ *
+ * Since we look at actual time in queue, not time since last delivery
+ * attempt, backoff times will be distributed. However, we can still see
+ * spikes in delivery activity because the interval between deferred
+ * queue scans is finite.
+ */
+ if (message->flags) {
+ if (message->arrival_time > 0) {
+ delay = event_time() - message->arrival_time;
+ if (delay > var_max_backoff_time)
+ delay = var_max_backoff_time;
+ if (delay < var_min_backoff_time)
+ delay = var_min_backoff_time;
+ } else {
+ delay = var_min_backoff_time;
+ }
+ qmgr_active_defer(message->queue_name, message->queue_id,
+ MAIL_QUEUE_DEFERRED, delay);
+ }
+
+ /*
+ * All recipients done. Remove the queue file.
+ */
+ else {
+ if (mail_queue_remove(message->queue_name, message->queue_id)) {
+ if (errno != ENOENT)
+ msg_fatal("%s: remove %s from %s: %m", myname,
+ message->queue_id, message->queue_name);
+ msg_warn("%s: remove %s from %s: %m", myname,
+ message->queue_id, message->queue_name);
+ } else if (msg_verbose) {
+ msg_info("%s: remove %s", myname, message->queue_id);
+ }
+ }
+
+ /*
+ * Finally, delete the in-core message structure.
+ */
+ qmgr_message_free(message);
+}
+
+/* qmgr_active_drain - drain active queue by allocating a delivery process */
+
+void qmgr_active_drain(void)
+{
+ QMGR_TRANSPORT *transport;
+
+ /*
+ * Allocate one delivery process for every transport with pending mail.
+ * The process allocation completes asynchronously.
+ */
+ while ((transport = qmgr_transport_select()) != 0) {
+ if (msg_verbose)
+ msg_info("qmgr_active_drain: allocate %s", transport->name);
+ qmgr_transport_alloc(transport, qmgr_deliver);
+ }
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_bounce.c b/gnu/dist/postfix/src/nqmgr/qmgr_bounce.c
new file mode 100644
index 00000000000..e8f01405f4d
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_bounce.c
@@ -0,0 +1,79 @@
+/*++
+/* NAME
+/* qmgr_bounce
+/* SUMMARY
+/* deal with mail that will not be delivered
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* QMGR_QUEUE *qmgr_bounce_recipient(message, recipient, format, ...)
+/* QMGR_MESSAGE *message;
+/* QMGR_RCPT *recipient;
+/* const char *format;
+/* DESCRIPTION
+/* qmgr_bounce_recipient() produces a bounce log record.
+/* Once the bounce record is written successfully, the recipient
+/* is marked as done. When the bounce record cannot be written,
+/* the message structure is updated to reflect that the mail is
+/* deferred.
+/*
+/* Arguments:
+/* .IP message
+/* Open queue file with the message being bounced.
+/* .IP recipient
+/* The recipient that will not be delivered.
+/* .IP format
+/* Free-format text that describes why delivery will not happen.
+/* DIAGNOSTICS
+/* Panic: consistency check failure. Fatal: out of memory.
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdarg.h>
+
+/* Utility library. */
+
+/* Global library. */
+
+#include <bounce.h>
+#include <deliver_completed.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_bounce_recipient - bounce one message recipient */
+
+void qmgr_bounce_recipient(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
+ const char *format,...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, format);
+ status = vbounce_append(BOUNCE_FLAG_KEEP, message->queue_id,
+ recipient->address, "none",
+ message->arrival_time, format, ap);
+ va_end(ap);
+
+ if (status == 0)
+ deliver_completed(message->fp, recipient->offset);
+ else
+ message->flags |= status;
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_defer.c b/gnu/dist/postfix/src/nqmgr/qmgr_defer.c
new file mode 100644
index 00000000000..72d30aac927
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_defer.c
@@ -0,0 +1,164 @@
+/*++
+/* NAME
+/* qmgr_defer
+/* SUMMARY
+/* deal with mail that must be delivered later
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* void qmgr_defer_recipient(message, address, reason)
+/* QMGR_MESSAGE *message;
+/* const char *address;
+/* const char *reason;
+/*
+/* void qmgr_defer_todo(queue, reason)
+/* QMGR_QUEUE *queue;
+/* const char *reason;
+/*
+/* QMGR_QUEUE *qmgr_defer_transport(transport, reason)
+/* QMGR_TRANSPORT *transport;
+/* const char *reason;
+/* DESCRIPTION
+/* qmgr_defer_recipient() defers delivery of the named message to
+/* the named recipient. It updates the message structure and writes
+/* a log entry.
+/*
+/* qmgr_defer_todo() iterates over all "todo" deliveries queued for
+/* the named site, and calls qmgr_defer_recipient() for each recipient
+/* found. Side effects caused by qmgr_entry_done(), qmgr_queue_done(),
+/* and by qmgr_active_done(): in-core queue entries will disappear,
+/* in-core queues may disappear, in-core and on-disk messages may
+/* disappear, bounces may be sent, new in-core queues, queue entries
+/* and recipients may appear.
+/*
+/* qmgr_defer_transport() calls qmgr_defer_todo() for each queue
+/* that depends on the named transport. See there for side effects.
+/*
+/* Arguments:
+/* .IP recipient
+/* A recipient address; used for logging purposes, and for updating
+/* the message-specific \fIdefer\fR log.
+/* .IP queue
+/* Specifies a queue with delivery requests for a specific next-hop
+/* host (or local user).
+/* .IP transport
+/* Specifies a message delivery transport.
+/* .IP reason
+/* Free-format text that describes why delivery is deferred; this
+/* used for logging purposes, and for updating the message-specific
+/* \fIdefer\fR log.
+/* BUGS
+/* The side effects of calling this routine are quite dramatic.
+/* DIAGNOSTICS
+/* Panic: consistency check failure. Fatal: out of memory.
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <defer.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_defer_transport - defer todo entries for named transport */
+
+void qmgr_defer_transport(QMGR_TRANSPORT *transport, const char *reason)
+{
+ char *myname = "qmgr_defer_transport";
+ QMGR_QUEUE *queue;
+ QMGR_QUEUE *next;
+
+ /*
+ * Sanity checks.
+ */
+ if (reason == 0)
+ msg_panic("%s: null reason", myname);
+ if (msg_verbose)
+ msg_info("defer transport %s: %s", transport->name, reason);
+
+ /*
+ * Proceed carefully. Queues may disappear as a side effect.
+ */
+ for (queue = transport->queue_list.next; queue; queue = next) {
+ next = queue->peers.next;
+ qmgr_defer_todo(queue, reason);
+ }
+}
+
+/* qmgr_defer_todo - defer all todo queue entries for specific site */
+
+void qmgr_defer_todo(QMGR_QUEUE *queue, const char *reason)
+{
+ char *myname = "qmgr_defer_todo";
+ QMGR_ENTRY *entry;
+ QMGR_ENTRY *next;
+ QMGR_MESSAGE *message;
+ QMGR_RCPT *recipient;
+ int nrcpt;
+
+ /*
+ * Sanity checks.
+ */
+ if (reason == 0)
+ msg_panic("%s: null reason", myname);
+ if (msg_verbose)
+ msg_info("defer site %s: %s", queue->name, reason);
+
+ /*
+ * Proceed carefully. Queue entries will disappear as a side effect.
+ */
+ for (entry = queue->todo.next; entry != 0; entry = next) {
+ next = entry->queue_peers.next;
+ message = entry->message;
+ for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
+ recipient = entry->rcpt_list.info + nrcpt;
+ qmgr_defer_recipient(message, recipient->address, reason);
+ }
+ qmgr_entry_done(entry, QMGR_QUEUE_TODO);
+ }
+}
+
+/* qmgr_defer_recipient - defer delivery of specific recipient */
+
+void qmgr_defer_recipient(QMGR_MESSAGE *message, const char *address,
+ const char *reason)
+{
+ char *myname = "qmgr_defer_recipient";
+
+ /*
+ * Sanity checks.
+ */
+ if (reason == 0)
+ msg_panic("%s: reason 0", myname);
+
+ /*
+ * Update the message structure and log the message disposition.
+ */
+ message->flags |= defer_append(BOUNCE_FLAG_KEEP, message->queue_id,
+ address, "none", message->arrival_time,
+ "%s", reason);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_deliver.c b/gnu/dist/postfix/src/nqmgr/qmgr_deliver.c
new file mode 100644
index 00000000000..394b267d6a9
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_deliver.c
@@ -0,0 +1,349 @@
+/*++
+/* NAME
+/* qmgr_deliver 3
+/* SUMMARY
+/* deliver one per-site queue entry to that site
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* int qmgr_deliver_concurrency;
+/*
+/* int qmgr_deliver(transport, fp)
+/* QMGR_TRANSPORT *transport;
+/* VSTREAM *fp;
+/* DESCRIPTION
+/* This module implements the client side of the `queue manager
+/* to delivery agent' protocol. The queue manager uses
+/* asynchronous I/O so that it can drive multiple delivery
+/* agents in parallel. Depending on the outcome of a delivery
+/* attempt, the status of messages, queues and transports is
+/* updated.
+/*
+/* qmgr_deliver_concurrency is a global counter that says how
+/* many delivery processes are in use. This can be used, for
+/* example, to control the size of the `active' message queue.
+/*
+/* qmgr_deliver() executes when a delivery process announces its
+/* availability for the named transport. It arranges for delivery
+/* of a suitable queue entry. The \fIfp\fR argument specifies a
+/* stream that is connected to the delivery process. Upon completion
+/* of delivery (successful or not), the stream is closed, so that the
+/* delivery process is released.
+/* DIAGNOSTICS
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <time.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <events.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <recipient_list.h>
+#include <mail_params.h>
+#include <deliver_request.h>
+#include <verp_sender.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+int qmgr_deliver_concurrency;
+
+ /*
+ * Message delivery status codes.
+ */
+#define DELIVER_STAT_OK 0 /* all recipients delivered */
+#define DELIVER_STAT_DEFER 1 /* try some recipients later */
+#define DELIVER_STAT_CRASH 2 /* mailer internal problem */
+
+/* qmgr_deliver_initial_reply - retrieve initial delivery process response */
+
+static int qmgr_deliver_initial_reply(VSTREAM *stream)
+{
+ int stat;
+
+ if (peekfd(vstream_fileno(stream)) < 0) {
+ msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
+ return (DELIVER_STAT_CRASH);
+ } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+ ATTR_TYPE_END) != 1) {
+ msg_warn("%s: malformed response", VSTREAM_PATH(stream));
+ return (DELIVER_STAT_CRASH);
+ } else {
+ return (stat ? DELIVER_STAT_DEFER : 0);
+ }
+}
+
+/* qmgr_deliver_final_reply - retrieve final delivery process response */
+
+static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
+{
+ int stat;
+
+ if (peekfd(vstream_fileno(stream)) < 0) {
+ msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
+ return (DELIVER_STAT_CRASH);
+ } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+ ATTR_TYPE_END) != 2) {
+ msg_warn("%s: malformed response", VSTREAM_PATH(stream));
+ return (DELIVER_STAT_CRASH);
+ } else {
+ return (stat ? DELIVER_STAT_DEFER : 0);
+ }
+}
+
+/* qmgr_deliver_send_request - send delivery request to delivery process */
+
+static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
+{
+ QMGR_RCPT_LIST list = entry->rcpt_list;
+ QMGR_RCPT *recipient;
+ QMGR_MESSAGE *message = entry->message;
+ char *cp;
+ VSTRING *sender_buf = 0;
+ char *sender;
+ int flags;
+ char *nexthop;
+
+ /*
+ * If variable envelope return path is requested, change prefix+@origin
+ * into prefix+user=domain@origin. Note that with VERP there is only one
+ * recipient per delivery.
+ */
+ if (message->verp_delims == 0) {
+ sender = message->sender;
+ } else {
+ sender_buf = vstring_alloc(100);
+ verp_sender(sender_buf, message->verp_delims,
+ message->sender, list.info->address);
+ sender = vstring_str(sender_buf);
+ }
+
+ /*
+ * With mail transports that accept only one recipient per delivery, the
+ * queue name is user@nexthop, so that we can implement per-recipient
+ * concurrency limits. However, the delivery agent protocol expects
+ * nexthop only, so we must strip off the recipient local part.
+ */
+ flags = message->inspect_xport ?
+ DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT;
+ nexthop = (cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ?
+ cp + 1 : entry->queue->name;
+ attr_print(stream, ATTR_FLAG_MORE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, message->data_offset,
+ ATTR_TYPE_LONG, MAIL_ATTR_SIZE, message->data_size,
+ ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
+ ATTR_TYPE_STR, MAIL_ATTR_RRCPT, message->return_receipt,
+ ATTR_TYPE_LONG, MAIL_ATTR_TIME, message->arrival_time,
+ ATTR_TYPE_END);
+ if (sender_buf != 0)
+ vstring_free(sender_buf);
+ for (recipient = list.info; recipient < list.info + list.len; recipient++)
+ attr_print(stream, ATTR_FLAG_MORE,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
+ ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
+ ATTR_TYPE_END);
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
+ ATTR_TYPE_END);
+ if (vstream_fflush(stream) != 0) {
+ msg_warn("write to process (%s): %m", entry->queue->transport->name);
+ return (-1);
+ } else {
+ if (msg_verbose)
+ msg_info("qmgr_deliver: site `%s'", entry->queue->name);
+ return (0);
+ }
+}
+
+/* qmgr_deliver_abort - transport response watchdog */
+
+static void qmgr_deliver_abort(int unused_event, char *context)
+{
+ QMGR_ENTRY *entry = (QMGR_ENTRY *) context;
+ QMGR_QUEUE *queue = entry->queue;
+ QMGR_TRANSPORT *transport = queue->transport;
+ QMGR_MESSAGE *message = entry->message;
+
+ msg_fatal("%s: timeout receiving delivery status from transport: %s",
+ message->queue_id, transport->name);
+}
+
+/* qmgr_deliver_update - process delivery status report */
+
+static void qmgr_deliver_update(int unused_event, char *context)
+{
+ QMGR_ENTRY *entry = (QMGR_ENTRY *) context;
+ QMGR_QUEUE *queue = entry->queue;
+ QMGR_TRANSPORT *transport = queue->transport;
+ QMGR_MESSAGE *message = entry->message;
+ VSTRING *reason = vstring_alloc(1);
+ int status;
+
+ /*
+ * The message transport has responded. Stop the watchdog timer.
+ */
+ event_cancel_timer(qmgr_deliver_abort, context);
+
+ /*
+ * Retrieve the delivery agent status report. The numerical status code
+ * indicates if delivery should be tried again. The reason text is sent
+ * only when a site should be avoided for a while, so that the queue
+ * manager can log why it does not even try to schedule delivery to the
+ * affected recipients.
+ */
+ status = qmgr_deliver_final_reply(entry->stream, reason);
+
+ /*
+ * The mail delivery process failed for some reason (although delivery
+ * may have been successful). Back off with this transport type for a
+ * while. Dispose of queue entries for this transport that await
+ * selection (the todo lists). Stay away from queue entries that have
+ * been selected (the busy lists), or we would have dangling pointers.
+ * The queue itself won't go away before we dispose of the current queue
+ * entry.
+ */
+ if (status == DELIVER_STAT_CRASH) {
+ message->flags |= DELIVER_STAT_DEFER;
+ qmgr_transport_throttle(transport, "unknown mail transport error");
+ msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
+ transport->name);
+ qmgr_defer_transport(transport, transport->reason);
+ }
+
+ /*
+ * This message must be tried again.
+ *
+ * If we have a problem talking to this site, back off with this site for a
+ * while; dispose of queue entries for this site that await selection
+ * (the todo list); stay away from queue entries that have been selected
+ * (the busy list), or we would have dangling pointers. The queue itself
+ * won't go away before we dispose of the current queue entry.
+ */
+ if (status == DELIVER_STAT_DEFER) {
+ message->flags |= DELIVER_STAT_DEFER;
+ if (VSTRING_LEN(reason)) {
+ qmgr_queue_throttle(queue, vstring_str(reason));
+ if (queue->window == 0)
+ qmgr_defer_todo(queue, queue->reason);
+ }
+ }
+
+ /*
+ * No problems detected. Mark the transport and queue as alive. The queue
+ * itself won't go away before we dispose of the current queue entry.
+ */
+ if (status == 0) {
+ qmgr_transport_unthrottle(transport);
+ qmgr_queue_unthrottle(queue);
+ }
+
+ /*
+ * Release the delivery process, and give some other queue entry a chance
+ * to be delivered. When all recipients for a message have been tried,
+ * decide what to do next with this message: defer, bounce, delete.
+ */
+ event_disable_readwrite(vstream_fileno(entry->stream));
+ if (vstream_fclose(entry->stream) != 0)
+ msg_warn("qmgr_deliver_update: close delivery stream: %m");
+ entry->stream = 0;
+ qmgr_deliver_concurrency--;
+ qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
+ vstring_free(reason);
+}
+
+/* qmgr_deliver - deliver one per-site queue entry */
+
+void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
+{
+ QMGR_ENTRY *entry;
+
+ /*
+ * Find out if this delivery process is really available. Once elected,
+ * the delivery process is supposed to express its happiness. If there is
+ * a problem, wipe the pending deliveries for this transport. This
+ * routine runs in response to an external event, so it does not run
+ * while some other queue manipulation is happening.
+ */
+ if (qmgr_deliver_initial_reply(stream) != 0) {
+ qmgr_transport_throttle(transport, "mail transport unavailable");
+ qmgr_defer_transport(transport, transport->reason);
+ (void) vstream_fclose(stream);
+ return;
+ }
+
+ /*
+ * Find a suitable queue entry. Things may have changed since this
+ * transport was allocated. If no suitable entry is found,
+ * unceremoniously disconnect from the delivery process. The delivery
+ * agent request reading routine is prepared for the queue manager to
+ * change its mind for no apparent reason.
+ */
+ if ((entry = qmgr_job_entry_select(transport)) == 0) {
+ (void) vstream_fclose(stream);
+ return;
+ }
+
+ /*
+ * Send the queue file info and recipient info to the delivery process.
+ * If there is a problem, wipe the pending deliveries for this transport.
+ * This routine runs in response to an external event, so it does not run
+ * while some other queue manipulation is happening.
+ */
+ if (qmgr_deliver_send_request(entry, stream) < 0) {
+ qmgr_entry_unselect(entry);
+ qmgr_transport_throttle(transport, "mail transport unavailable");
+ qmgr_defer_transport(transport, transport->reason);
+ /* warning: entry may be a dangling pointer here */
+ (void) vstream_fclose(stream);
+ return;
+ }
+
+ /*
+ * If we get this far, go wait for the delivery status report.
+ */
+ qmgr_deliver_concurrency++;
+ entry->stream = stream;
+ event_enable_read(vstream_fileno(stream),
+ qmgr_deliver_update, (char *) entry);
+
+ /*
+ * Guard against broken systems.
+ */
+ event_request_timer(qmgr_deliver_abort, (char *) entry, var_daemon_timeout);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_enable.c b/gnu/dist/postfix/src/nqmgr/qmgr_enable.c
new file mode 100644
index 00000000000..5f0c3841b91
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_enable.c
@@ -0,0 +1,107 @@
+/*++
+/* NAME
+/* qmgr_enable
+/* SUMMARY
+/* enable dead transports or sites
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* void qmgr_enable_queue(queue)
+/* QMGR_QUEUE *queue;
+/*
+/* QMGR_QUEUE *qmgr_enable_transport(transport)
+/* QMGR_TRANSPORT *transport;
+/*
+/* void qmgr_enable_all(void)
+/* DESCRIPTION
+/* This module purges dead in-core state information, effectively
+/* re-enabling delivery.
+/*
+/* qmgr_enable_queue() enables deliveries to the named dead site.
+/* Empty queues are destroyed. The existed solely to indicate that
+/* a site is dead.
+/*
+/* qmgr_enable_transport() enables deliveries via the specified
+/* transport, and calls qmgr_enable_queue() for each destination
+/* on that transport. Empty queues are destroyed.
+/*
+/* qmgr_enable_all() enables all transports and queues.
+/* See above for the side effects caused by doing this.
+/* BUGS
+/* The side effects of calling this module can be quite dramatic.
+/* DIAGNOSTICS
+/* Panic: consistency check failure. Fatal: out of memory.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_enable_all - enable transports and queues */
+
+void qmgr_enable_all(void)
+{
+ QMGR_TRANSPORT *xport;
+
+ if (msg_verbose)
+ msg_info("qmgr_enable_all");
+
+ /*
+ * The number of transports does not change as a side effect, so this can
+ * be a straightforward loop.
+ */
+ for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next)
+ qmgr_enable_transport(xport);
+}
+
+/* qmgr_enable_transport - defer todo entries for named transport */
+
+void qmgr_enable_transport(QMGR_TRANSPORT *transport)
+{
+ QMGR_QUEUE *queue;
+ QMGR_QUEUE *next;
+
+ /*
+ * Proceed carefully. Queues may disappear as a side effect.
+ */
+ if (transport->flags & QMGR_TRANSPORT_STAT_DEAD) {
+ if (msg_verbose)
+ msg_info("enable transport %s", transport->name);
+ qmgr_transport_unthrottle(transport);
+ }
+ for (queue = transport->queue_list.next; queue; queue = next) {
+ next = queue->peers.next;
+ qmgr_enable_queue(queue);
+ }
+}
+
+/* qmgr_enable_queue - enable and possibly delete queue */
+
+void qmgr_enable_queue(QMGR_QUEUE *queue)
+{
+ if (queue->window == 0) {
+ if (msg_verbose)
+ msg_info("enable site %s/%s", queue->transport->name, queue->name);
+ qmgr_queue_unthrottle(queue);
+ }
+ if (queue->todo.next == 0 && queue->busy.next == 0)
+ qmgr_queue_done(queue);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_entry.c b/gnu/dist/postfix/src/nqmgr/qmgr_entry.c
new file mode 100644
index 00000000000..64cec0b28f8
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_entry.c
@@ -0,0 +1,268 @@
+/*++
+/* NAME
+/* qmgr_entry 3
+/* SUMMARY
+/* per-site queue entries
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* QMGR_ENTRY *qmgr_entry_create(peer, message)
+/* QMGR_PEER *peer;
+/* QMGR_MESSAGE *message;
+/*
+/* void qmgr_entry_done(entry, which)
+/* QMGR_ENTRY *entry;
+/* int which;
+/*
+/* QMGR_ENTRY *qmgr_entry_select(queue)
+/* QMGR_QUEUE *queue;
+/*
+/* void qmgr_entry_unselect(queue, entry)
+/* QMGR_QUEUE *queue;
+/* QMGR_ENTRY *entry;
+/* DESCRIPTION
+/* These routines add/delete/manipulate per-site message
+/* delivery requests.
+/*
+/* qmgr_entry_create() creates an entry for the named peer and message,
+/* and appends the entry to the peer's list and its queue's todo list.
+/* Filling in and cleaning up the recipients is the responsibility
+/* of the caller.
+/*
+/* qmgr_entry_done() discards a per-site queue entry. The
+/* \fIwhich\fR argument is either QMGR_QUEUE_BUSY for an entry
+/* of the site's `busy' list (i.e. queue entries that have been
+/* selected for actual delivery), or QMGR_QUEUE_TODO for an entry
+/* of the site's `todo' list (i.e. queue entries awaiting selection
+/* for actual delivery).
+/*
+/* qmgr_entry_done() discards its peer structure when the peer
+/* is not referenced anymore.
+/*
+/* qmgr_entry_done() triggers cleanup of the per-site queue when
+/* the site has no pending deliveries, and the site is either
+/* alive, or the site is dead and the number of in-core queues
+/* exceeds a configurable limit (see qmgr_queue_done()).
+/*
+/* qmgr_entry_done() triggers special action when the last in-core
+/* queue entry for a message is done with: either read more
+/* recipients from the queue file, delete the queue file, or move
+/* the queue file to the deferred queue; send bounce reports to the
+/* message originator (see qmgr_active_done()).
+/*
+/* qmgr_entry_select() selects first entry from the named
+/* per-site queue's `todo' list for actual delivery. The entry is
+/* moved to the queue's `busy' list: the list of messages being
+/* delivered. The entry is also removed from its peer list.
+/*
+/* qmgr_entry_unselect() takes the named entry off the named
+/* per-site queue's `busy' list and moves it to the queue's
+/* `todo' list. The entry is also appended to its peer list again.
+/* DIAGNOSTICS
+/* Panic: interface violations, internal inconsistencies.
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_entry_select - select queue entry for delivery */
+
+QMGR_ENTRY *qmgr_entry_select(QMGR_PEER *peer)
+{
+ QMGR_ENTRY *entry;
+ QMGR_QUEUE *queue;
+
+ if ((entry = peer->entry_list.next) != 0) {
+ queue = entry->queue;
+ QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers);
+ queue->todo_refcount--;
+ QMGR_LIST_APPEND(queue->busy, entry, queue_peers);
+ queue->busy_refcount++;
+ QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers);
+ peer->job->selected_entries++;
+ }
+ return (entry);
+}
+
+/* qmgr_entry_unselect - unselect queue entry for delivery */
+
+void qmgr_entry_unselect(QMGR_ENTRY *entry)
+{
+ QMGR_PEER *peer = entry->peer;
+ QMGR_QUEUE *queue = entry->queue;
+
+ QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers);
+ queue->busy_refcount--;
+ QMGR_LIST_APPEND(queue->todo, entry, queue_peers);
+ queue->todo_refcount++;
+ QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers);
+ peer->job->selected_entries--;
+}
+
+/* qmgr_entry_done - dispose of queue entry */
+
+void qmgr_entry_done(QMGR_ENTRY *entry, int which)
+{
+ QMGR_QUEUE *queue = entry->queue;
+ QMGR_MESSAGE *message = entry->message;
+ QMGR_PEER *peer = entry->peer;
+ QMGR_JOB *sponsor,
+ *job = peer->job;
+ QMGR_TRANSPORT *transport = job->transport;
+
+ /*
+ * Take this entry off the in-core queue.
+ */
+ if (entry->stream != 0)
+ msg_panic("qmgr_entry_done: file is open");
+ if (which == QMGR_QUEUE_BUSY) {
+ QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers);
+ queue->busy_refcount--;
+ } else if (which == QMGR_QUEUE_TODO) {
+ QMGR_LIST_UNLINK(peer->entry_list, QMGR_ENTRY *, entry, peer_peers);
+ job->selected_entries++;
+ QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers);
+ queue->todo_refcount--;
+ } else {
+ msg_panic("qmgr_entry_done: bad queue spec: %d", which);
+ }
+
+ /*
+ * Decrease the in-core recipient counts and free the recipient list and
+ * the structure itself.
+ */
+ job->rcpt_count -= entry->rcpt_list.len;
+ message->rcpt_count -= entry->rcpt_list.len;
+ qmgr_recipient_count -= entry->rcpt_list.len;
+ qmgr_rcpt_list_free(&entry->rcpt_list);
+ myfree((char *) entry);
+
+ /*
+ * Make sure that the transport of any retired or finishing job that
+ * donated recipient slots to this message gets them back first. Then, if
+ * possible, pass the remaining unused recipient slots to the next job on
+ * the job list.
+ */
+ for (sponsor = message->job_list.next; sponsor; sponsor = sponsor->message_peers.next) {
+ if (sponsor->rcpt_count >= sponsor->rcpt_limit || sponsor == job)
+ continue;
+ if (sponsor->stack_level < 0 || message->rcpt_offset == 0)
+ qmgr_job_move_limits(sponsor);
+ }
+ if (message->rcpt_offset == 0) {
+ qmgr_job_move_limits(job);
+ }
+
+ /*
+ * If the queue was blocking some of the jobs on the job list, check if
+ * the concurrency limit has lifted. If there are still some pending
+ * deliveries, give it a try and unmark all transport blockers at once.
+ * The qmgr_job_entry_select() will do the rest. In either case make sure
+ * the queue is not marked as a blocker anymore, with extra handling of
+ * queues which were declared dead.
+ *
+ * Note that changing the blocker status also affects the candidate cache.
+ * Most of the cases would be automatically recognized by the current job
+ * change, but we play safe and reset the cache explicitly below.
+ *
+ * Keeping the transport blocker tag odd is an easy way to make sure the tag
+ * never matches jobs that are not explicitly marked as blockers.
+ */
+ if (queue->blocker_tag == transport->blocker_tag) {
+ if (queue->window > queue->busy_refcount && queue->todo.next != 0) {
+ transport->blocker_tag += 2;
+ transport->job_current = transport->job_list.next;
+ transport->candidate_cache_current = 0;
+ }
+ if (queue->window > queue->busy_refcount || queue->window == 0)
+ queue->blocker_tag = 0;
+ }
+
+ /*
+ * When there are no more entries for this peer, discard the peer
+ * structure.
+ */
+ peer->refcount--;
+ if (peer->refcount == 0)
+ qmgr_peer_free(peer);
+
+ /*
+ * When the in-core queue for this site is empty and when this site is
+ * not dead, discard the in-core queue. When this site is dead, but the
+ * number of in-core queues exceeds some threshold, get rid of this
+ * in-core queue anyway, in order to avoid running out of memory.
+ */
+ if (queue->todo.next == 0 && queue->busy.next == 0) {
+ if (queue->window == 0 && qmgr_queue_count > 2 * var_qmgr_rcpt_limit)
+ qmgr_queue_unthrottle(queue);
+ if (queue->window > 0)
+ qmgr_queue_done(queue);
+ }
+
+ /*
+ * Update the in-core message reference count. When the in-core message
+ * structure has no more references, dispose of the message.
+ */
+ message->refcount--;
+ if (message->refcount == 0)
+ qmgr_active_done(message);
+}
+
+/* qmgr_entry_create - create queue todo entry */
+
+QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message)
+{
+ QMGR_ENTRY *entry;
+ QMGR_QUEUE *queue = peer->queue;
+
+ /*
+ * Sanity check.
+ */
+ if (queue->window == 0)
+ msg_panic("qmgr_entry_create: dead queue: %s", queue->name);
+
+ entry = (QMGR_ENTRY *) mymalloc(sizeof(QMGR_ENTRY));
+ entry->stream = 0;
+ entry->message = message;
+ qmgr_rcpt_list_init(&entry->rcpt_list);
+ message->refcount++;
+ entry->peer = peer;
+ QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers);
+ peer->refcount++;
+ entry->queue = queue;
+ QMGR_LIST_APPEND(queue->todo, entry, queue_peers);
+ queue->todo_refcount++;
+ return (entry);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_job.c b/gnu/dist/postfix/src/nqmgr/qmgr_job.c
new file mode 100644
index 00000000000..094838b77e2
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_job.c
@@ -0,0 +1,912 @@
+/*++
+/* NAME
+/* qmgr_job 3
+/* SUMMARY
+/* per-transport jobs
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* QMGR_JOB *qmgr_job_obtain(message, transport)
+/* QMGR_MESSAGE *message;
+/* QMGR_TRANSPORT *transport;
+/*
+/* void qmgr_job_free(job)
+/* QMGR_JOB *job;
+/*
+/* void qmgr_job_move_limits(job)
+/* QMGR_JOB *job;
+/*
+/* QMGR_ENTRY *qmgr_job_entry_select(transport)
+/* QMGR_TRANSPORT *transport;
+/* DESCRIPTION
+/* These routines add/delete/manipulate per-transport jobs.
+/* Each job corresponds to a specific transport and message.
+/* Each job has a peer list containing all pending delivery
+/* requests for that message.
+/*
+/* qmgr_job_obtain() finds an existing job for named message and
+/* transport combination. New empty job is created if no existing can
+/* be found. In either case, the job is prepared for assignement of
+/* (more) message recipients.
+/*
+/* qmgr_job_free() disposes of a per-transport job after all
+/* its entries have been taken care of. It is an error to dispose
+/* of a job that is still in use.
+/*
+/* qmgr_job_entry_select() attempts to find the next entry suitable
+/* for delivery. The job preempting algorithm is also exercised.
+/* If necessary, an attempt to read more recipients into core is made.
+/* This can result in creation of more job, queue and entry structures.
+/*
+/* qmgr_job_move_limits() takes care of proper distribution of the
+/* per-transport recipients limit among the per-transport jobs.
+/* Should be called whenever a job's recipient slot becomes available.
+/* DIAGNOSTICS
+/* Panic: consistency check failure.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Patrik Rak
+/* patrik@raxoft.cz
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <mymalloc.h>
+#include <sane_time.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* Forward declarations */
+
+static void qmgr_job_pop(QMGR_JOB *);
+
+/* Helper macros */
+
+#define HAS_ENTRIES(job) ((job)->selected_entries < (job)->read_entries)
+
+/*
+ * The MIN_ENTRIES macro may underestimate a lot but we can't use message->rcpt_unread
+ * because we don't know if all those unread recipients go to our transport yet.
+ */
+
+#define MIN_ENTRIES(job) ((job)->read_entries)
+#define MAX_ENTRIES(job) ((job)->read_entries + (job)->message->rcpt_unread)
+
+#define RESET_CANDIDATE_CACHE(transport) ((transport)->candidate_cache_current = 0)
+
+#define IS_BLOCKER(job,transport) ((job)->blocker_tag == (transport)->blocker_tag)
+
+/* qmgr_job_create - create and initialize message job structure */
+
+static QMGR_JOB *qmgr_job_create(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport)
+{
+ QMGR_JOB *job;
+
+ job = (QMGR_JOB *) mymalloc(sizeof(QMGR_JOB));
+ job->message = message;
+ QMGR_LIST_APPEND(message->job_list, job, message_peers);
+ htable_enter(transport->job_byname, message->queue_id, (char *) job);
+ job->transport = transport;
+ QMGR_LIST_INIT(job->transport_peers);
+ QMGR_LIST_INIT(job->time_peers);
+ job->stack_parent = 0;
+ QMGR_LIST_INIT(job->stack_children);
+ QMGR_LIST_INIT(job->stack_siblings);
+ job->stack_level = -1;
+ job->blocker_tag = 0;
+ job->peer_byname = htable_create(0);
+ QMGR_LIST_INIT(job->peer_list);
+ job->slots_used = 0;
+ job->slots_available = 0;
+ job->selected_entries = 0;
+ job->read_entries = 0;
+ job->rcpt_count = 0;
+ job->rcpt_limit = 0;
+ return (job);
+}
+
+/* qmgr_job_link - append the job to the job lists based on the time it was queued */
+
+static void qmgr_job_link(QMGR_JOB *job)
+{
+ QMGR_TRANSPORT *transport = job->transport;
+ QMGR_MESSAGE *message = job->message;
+ QMGR_JOB *prev,
+ *next,
+ *list_prev,
+ *list_next,
+ *unread,
+ *current;
+ int delay;
+
+ /*
+ * Sanity checks.
+ */
+ if (job->stack_level >= 0)
+ msg_panic("qmgr_job_link: already on the job lists (%d)", job->stack_level);
+
+ /*
+ * Traverse the time list and the scheduler list from the end and stop
+ * when we found job older than the one beeing linked.
+ *
+ * During the traversals keep track if we have come accross either the
+ * current job or the first unread job on the job list. If this is the
+ * case, these pointers will be adjusted below as required.
+ *
+ * Although both lists are exactly the same when only jobs on the stack
+ * level zero are considered, it's easier to traverse them separately.
+ * Otherwise it's impossible to keep track of the current job pointer
+ * effectively.
+ *
+ * This may look inefficient but under normal operation it is expected that
+ * the loops will stop right away, resulting in normal list appends
+ * below. However, this code is necessary for reviving retired jobs and
+ * for jobs which are created long after the first chunk of recipients
+ * was read in-core (either of these can happen only for multi-transport
+ * messages).
+ */
+ current = transport->job_current;
+ for (next = 0, prev = transport->job_list.prev; prev;
+ next = prev, prev = prev->transport_peers.prev) {
+ if (prev->stack_parent == 0) {
+ delay = message->queued_time - prev->message->queued_time;
+ if (delay >= 0)
+ break;
+ }
+ if (current == prev)
+ current = 0;
+ }
+ list_prev = prev;
+ list_next = next;
+
+ unread = transport->job_next_unread;
+ for (next = 0, prev = transport->job_bytime.prev; prev;
+ next = prev, prev = prev->time_peers.prev) {
+ delay = message->queued_time - prev->message->queued_time;
+ if (delay >= 0)
+ break;
+ if (unread == prev)
+ unread = 0;
+ }
+
+ /*
+ * Link the job into the proper place on the job lists and mark it so we
+ * know it has been linked.
+ */
+ job->stack_level = 0;
+ QMGR_LIST_LINK(transport->job_list, list_prev, job, list_next, transport_peers);
+ QMGR_LIST_LINK(transport->job_bytime, prev, job, next, time_peers);
+
+ /*
+ * Update the current job pointer if necessary.
+ */
+ if (current == 0)
+ transport->job_current = job;
+
+ /*
+ * Update the pointer to the first unread job on the job list and steal
+ * the unused recipient slots from the old one.
+ */
+ if (unread == 0) {
+ unread = transport->job_next_unread;
+ transport->job_next_unread = job;
+ if (unread != 0)
+ qmgr_job_move_limits(unread);
+ }
+
+ /*
+ * Get as much recipient slots as possible. The excess will be returned
+ * to the transport pool as soon as the exact amount required is known
+ * (which is usually after all recipients have been read in core).
+ */
+ if (transport->rcpt_unused > 0) {
+ job->rcpt_limit += transport->rcpt_unused;
+ message->rcpt_limit += transport->rcpt_unused;
+ transport->rcpt_unused = 0;
+ }
+}
+
+/* qmgr_job_find - lookup job associated with named message and transport */
+
+static QMGR_JOB *qmgr_job_find(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport)
+{
+
+ /*
+ * Instead of traversing the message job list, we use single per
+ * transport hash table. This is better (at least with respect to memory
+ * usage) than having single hash table (usually almost empty) for each
+ * message.
+ */
+ return ((QMGR_JOB *) htable_find(transport->job_byname, message->queue_id));
+}
+
+/* qmgr_job_obtain - find/create the appropriate job and make it ready for new recipients */
+
+QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *message, QMGR_TRANSPORT *transport)
+{
+ QMGR_JOB *job;
+
+ /*
+ * Try finding an existing job, reviving it if it was already retired.
+ * Create a new job for this transport/message combination otherwise. In
+ * either case, the job ends linked on the job lists.
+ */
+ if ((job = qmgr_job_find(message, transport)) == 0)
+ job = qmgr_job_create(message, transport);
+ if (job->stack_level < 0)
+ qmgr_job_link(job);
+
+ /*
+ * Reset the candidate cache because of the new expected recipients. Make
+ * sure the job is not marked as a blocker for the same reason. Note that
+ * this can result in having a non-blocker followed by more blockers.
+ * Consequently, we can't just update the current job pointer, we have to
+ * reset it. Fortunately qmgr_job_entry_select() will easily deal with
+ * this and will lookup the real current job for us.
+ */
+ RESET_CANDIDATE_CACHE(transport);
+ if (IS_BLOCKER(job, transport)) {
+ job->blocker_tag = 0;
+ transport->job_current = transport->job_list.next;
+ }
+ return (job);
+}
+
+/* qmgr_job_move_limits - move unused recipient slots to the next unread job */
+
+void qmgr_job_move_limits(QMGR_JOB *job)
+{
+ QMGR_TRANSPORT *transport = job->transport;
+ QMGR_MESSAGE *message = job->message;
+ QMGR_JOB *next = transport->job_next_unread;
+ int rcpt_unused,
+ msg_rcpt_unused;
+
+ /*
+ * Find next unread job on the job list if necessary. Cache it for later.
+ * This makes the amortized efficiency of this routine O(1) per job. Note
+ * that we use the time list whose ordering doesn't change over time.
+ */
+ if (job == next) {
+ for (next = next->time_peers.next; next; next = next->time_peers.next)
+ if (next->message->rcpt_offset != 0)
+ break;
+ transport->job_next_unread = next;
+ }
+
+ /*
+ * Calculate the number of available unused slots.
+ */
+ rcpt_unused = job->rcpt_limit - job->rcpt_count;
+ msg_rcpt_unused = message->rcpt_limit - message->rcpt_count;
+ if (msg_rcpt_unused < rcpt_unused)
+ rcpt_unused = msg_rcpt_unused;
+
+ /*
+ * Transfer the unused recipient slots back to the transport pool and to
+ * the next not-fully-read job. Job's message limits are adjusted
+ * accordingly. Note that the transport pool can be negative if we used
+ * some of the rcpt_per_stack slots.
+ */
+ if (rcpt_unused > 0) {
+ job->rcpt_limit -= rcpt_unused;
+ message->rcpt_limit -= rcpt_unused;
+ transport->rcpt_unused += rcpt_unused;
+ if (next != 0 && (rcpt_unused = transport->rcpt_unused) > 0) {
+ next->rcpt_limit += rcpt_unused;
+ next->message->rcpt_limit += rcpt_unused;
+ transport->rcpt_unused = 0;
+ }
+ }
+}
+
+/* qmgr_job_parent_gone - take care of orphaned stack children */
+
+static void qmgr_job_parent_gone(QMGR_JOB *job, QMGR_JOB *parent)
+{
+ QMGR_JOB *child;
+
+ while ((child = job->stack_children.next) != 0) {
+ QMGR_LIST_UNLINK(job->stack_children, QMGR_JOB *, child, stack_siblings);
+ if (parent != 0)
+ QMGR_LIST_APPEND(parent->stack_children, child, stack_siblings);
+ child->stack_parent = parent;
+ }
+}
+
+/* qmgr_job_unlink - unlink the job from the job lists */
+
+static void qmgr_job_unlink(QMGR_JOB *job)
+{
+ char *myname = "qmgr_job_unlink";
+ QMGR_TRANSPORT *transport = job->transport;
+
+ /*
+ * Sanity checks.
+ */
+ if (job->stack_level != 0)
+ msg_panic("%s: non-zero stack level (%d)", myname, job->stack_level);
+ if (job->stack_parent != 0)
+ msg_panic("%s: parent present", myname);
+ if (job->stack_siblings.next != 0)
+ msg_panic("%s: siblings present", myname);
+
+ /*
+ * Make sure that children of job on zero stack level are informed that
+ * their parent is gone too.
+ */
+ qmgr_job_parent_gone(job, 0);
+
+ /*
+ * Update the current job pointer if necessary.
+ */
+ if (transport->job_current == job)
+ transport->job_current = job->transport_peers.next;
+
+ /*
+ * Invalidate the candidate selection cache if necessary.
+ */
+ if (job == transport->candidate_cache
+ || job == transport->candidate_cache_current)
+ RESET_CANDIDATE_CACHE(transport);
+
+ /*
+ * Remove the job from the job lists and mark it as unlinked.
+ */
+ QMGR_LIST_UNLINK(transport->job_list, QMGR_JOB *, job, transport_peers);
+ QMGR_LIST_UNLINK(transport->job_bytime, QMGR_JOB *, job, time_peers);
+ job->stack_level = -1;
+}
+
+/* qmgr_job_retire - remove the job from the job lists while waiting for recipients to deliver */
+
+static void qmgr_job_retire(QMGR_JOB *job)
+{
+ if (msg_verbose)
+ msg_info("qmgr_job_retire: %s", job->message->queue_id);
+
+ /*
+ * Pop the job from the job stack if necessary.
+ */
+ if (job->stack_level > 0)
+ qmgr_job_pop(job);
+
+ /*
+ * Make sure this job is not cached as the next unread job for this
+ * transport. The qmgr_entry_done() will make sure that the slots donated
+ * by this job are moved back to the transport pool as soon as possible.
+ */
+ qmgr_job_move_limits(job);
+
+ /*
+ * Remove the job from the job lists. Note that it remains on the message
+ * job list, though, and that it can be revived by using
+ * qmgr_job_obtain(). Also note that the available slot counter is left
+ * intact.
+ */
+ qmgr_job_unlink(job);
+}
+
+/* qmgr_job_free - release the job structure */
+
+void qmgr_job_free(QMGR_JOB *job)
+{
+ char *myname = "qmgr_job_free";
+ QMGR_MESSAGE *message = job->message;
+ QMGR_TRANSPORT *transport = job->transport;
+
+ if (msg_verbose)
+ msg_info("%s: %s %s", myname, message->queue_id, transport->name);
+
+ /*
+ * Sanity checks.
+ */
+ if (job->rcpt_count)
+ msg_panic("%s: non-zero recipient count (%d)", myname, job->rcpt_count);
+
+ /*
+ * Pop the job from the job stack if necessary.
+ */
+ if (job->stack_level > 0)
+ qmgr_job_pop(job);
+
+ /*
+ * Return any remaining recipient slots back to the recipient slots pool.
+ */
+ qmgr_job_move_limits(job);
+ if (job->rcpt_limit)
+ msg_panic("%s: recipient slots leak (%d)", myname, job->rcpt_limit);
+
+ /*
+ * Unlink and discard the structure. Check if the job is still linked on
+ * the job lists or if it was already retired before unlinking it.
+ */
+ if (job->stack_level >= 0)
+ qmgr_job_unlink(job);
+ QMGR_LIST_UNLINK(message->job_list, QMGR_JOB *, job, message_peers);
+ htable_delete(transport->job_byname, message->queue_id, (void (*) (char *)) 0);
+ htable_free(job->peer_byname, (void (*) (char *)) 0);
+ myfree((char *) job);
+}
+
+/* qmgr_job_count_slots - maintain the delivery slot counters */
+
+static void qmgr_job_count_slots(QMGR_JOB *job)
+{
+
+ /*
+ * Count the number of delivery slots used during the delivery of the
+ * selected job. Also count the number of delivery slots available for
+ * its preemption.
+ *
+ * Despite its trivial look, this is one of the key parts of the theory
+ * behind this preempting scheduler.
+ */
+ job->slots_available++;
+ job->slots_used++;
+
+ /*
+ * If the selected job is not the original current job, reset the
+ * candidate cache because the change above have slightly increased the
+ * chance of this job becoming a candidate next time.
+ *
+ * Don't expect that the change of the current jobs this turn will render
+ * the candidate cache invalid the next turn - it can happen that the
+ * next turn the original current job will be selected again and the
+ * cache would be considered valid in such case.
+ */
+ if (job != job->transport->candidate_cache_current)
+ RESET_CANDIDATE_CACHE(job->transport);
+}
+
+/* qmgr_job_candidate - find best job candidate for preempting given job */
+
+static QMGR_JOB *qmgr_job_candidate(QMGR_JOB *current)
+{
+ QMGR_TRANSPORT *transport = current->transport;
+ QMGR_JOB *job,
+ *best_job = 0;
+ double score,
+ best_score = 0.0;
+ int max_slots,
+ max_needed_entries,
+ max_total_entries;
+ int delay;
+ time_t now = sane_time();
+
+ /*
+ * Fetch the result directly from the cache if the cache is still valid.
+ *
+ * Note that we cache negative results too, so the cache must be invalidated
+ * by resetting the cached current job pointer, not the candidate pointer
+ * itself.
+ *
+ * In case the cache is valid and contains no candidate, we can ignore the
+ * time change, as it affects only which candidate is the best, not if
+ * one exists. However, this feature requires that we no longer relax the
+ * cache resetting rules, depending on the automatic cache timeout.
+ */
+ if (transport->candidate_cache_current == current
+ && (transport->candidate_cache_time == now
+ || transport->candidate_cache == 0))
+ return (transport->candidate_cache);
+
+ /*
+ * Estimate the minimum amount of delivery slots that can ever be
+ * accumulated for the given job. All jobs that won't fit into these
+ * slots are excluded from the candidate selection.
+ */
+ max_slots = (MIN_ENTRIES(current) - current->selected_entries
+ + current->slots_available) / transport->slot_cost;
+
+ /*
+ * Select the candidate with best time_since_queued/total_recipients
+ * score. In addition to jobs which don't meet the max_slots limit, skip
+ * also jobs which don't have any selectable entries at the moment.
+ *
+ * Instead of traversing the whole job list we traverse it just from the
+ * current job forward. This has several advantages. First, we skip some
+ * of the blocker jobs and the current job itself right away. But the
+ * really important advantage is that we are sure that we don't consider
+ * any jobs that are already stack children of the current job. Thanks to
+ * this we can easily include all encountered jobs which are leaf
+ * children of some of the preempting stacks as valid candidates. All we
+ * need to do is to make sure we do not include any of the stack parents.
+ * And, because the leaf children are not ordered by the time since
+ * queued, we have to exclude them from the early loop end test.
+ *
+ * However, don't bother searching if we can't find anything suitable
+ * anyway.
+ */
+ if (max_slots > 0) {
+ for (job = current->transport_peers.next; job; job = job->transport_peers.next) {
+ if (job->stack_children.next != 0 || IS_BLOCKER(job, transport))
+ continue;
+ max_total_entries = MAX_ENTRIES(job);
+ max_needed_entries = max_total_entries - job->selected_entries;
+ delay = now - job->message->queued_time + 1;
+ if (max_needed_entries > 0 && max_needed_entries <= max_slots) {
+ score = (double) delay / max_total_entries;
+ if (score > best_score) {
+ best_score = score;
+ best_job = job;
+ }
+ }
+
+ /*
+ * Stop early if the best score is as good as it can get.
+ */
+ if (delay <= best_score && job->stack_level == 0)
+ break;
+ }
+ }
+
+ /*
+ * Cache the result for later use.
+ */
+ transport->candidate_cache = best_job;
+ transport->candidate_cache_current = current;
+ transport->candidate_cache_time = now;
+
+ return (best_job);
+}
+
+/* qmgr_job_preempt - preempt large message with smaller one */
+
+static QMGR_JOB *qmgr_job_preempt(QMGR_JOB *current)
+{
+ char *myname = "qmgr_job_preempt";
+ QMGR_TRANSPORT *transport = current->transport;
+ QMGR_JOB *job,
+ *prev;
+ int expected_slots;
+ int rcpt_slots;
+
+ /*
+ * Suppress preempting completely if the current job is not big enough to
+ * accumulate even the mimimal number of slots required.
+ *
+ * Also, don't look for better job candidate if there are no available slots
+ * yet (the count can get negative due to the slot loans below).
+ */
+ if (current->slots_available <= 0
+ || MAX_ENTRIES(current) < transport->min_slots * transport->slot_cost)
+ return (current);
+
+ /*
+ * Find best candidate for preempting the current job.
+ *
+ * Note that the function also takes care that the candidate fits within the
+ * number of delivery slots which the current job is still able to
+ * accumulate.
+ */
+ if ((job = qmgr_job_candidate(current)) == 0)
+ return (current);
+
+ /*
+ * Sanity checks.
+ */
+ if (job == current)
+ msg_panic("%s: attempt to preempt itself", myname);
+ if (job->stack_children.next != 0)
+ msg_panic("%s: already on the job stack (%d)", myname, job->stack_level);
+ if (job->stack_level < 0)
+ msg_panic("%s: not on the job list (%d)", myname, job->stack_level);
+
+ /*
+ * Check if there is enough available delivery slots accumulated to
+ * preempt the current job.
+ *
+ * The slot loaning scheme improves the average message response time. Note
+ * that the loan only allows the preemption happen earlier, though. It
+ * doesn't affect how many slots have to be "paid" - in either case the
+ * full number of slots required has to be accumulated later before the
+ * current job can be preempted again.
+ */
+ expected_slots = MAX_ENTRIES(job) - job->selected_entries;
+ if (current->slots_available / transport->slot_cost + transport->slot_loan
+ < expected_slots * transport->slot_loan_factor / 100.0)
+ return (current);
+
+ /*
+ * Preempt the current job.
+ *
+ * This involves placing the selected candidate in front of the current job
+ * on the job list and updating the stack parent/child/sibling pointers
+ * appropriately. But first we need to make sure that the candidate is
+ * taken from its previous job stack which it might be top of.
+ */
+ if (job->stack_level > 0)
+ qmgr_job_pop(job);
+ QMGR_LIST_UNLINK(transport->job_list, QMGR_JOB *, job, transport_peers);
+ prev = current->transport_peers.prev;
+ QMGR_LIST_LINK(transport->job_list, prev, job, current, transport_peers);
+ job->stack_parent = current;
+ QMGR_LIST_APPEND(current->stack_children, job, stack_siblings);
+ job->stack_level = current->stack_level + 1;
+
+ /*
+ * Update the current job pointer and explicitly reset the candidate
+ * cache.
+ */
+ transport->job_current = job;
+ RESET_CANDIDATE_CACHE(transport);
+
+ /*
+ * Since the single job can be preempted by several jobs at the same
+ * time, we have to adjust the available slot count now to prevent using
+ * the same slots multiple times. To do that we subtract the number of
+ * slots the preempting job will supposedly use. This number will be
+ * corrected later when that job is popped from the stack to reflect the
+ * number of slots really used.
+ *
+ * As long as we don't need to keep track of how many slots were really
+ * used, we can (ab)use the slots_used counter for counting the
+ * difference between the real and expected amounts instead of the
+ * absolute amount.
+ */
+ current->slots_available -= expected_slots * transport->slot_cost;
+ job->slots_used = -expected_slots;
+
+ /*
+ * Add part of extra recipient slots reserved for preempting jobs to the
+ * new current job if necessary.
+ *
+ * Note that transport->rcpt_unused is within <-rcpt_per_stack,0> in such
+ * case.
+ */
+ if (job->message->rcpt_offset != 0) {
+ rcpt_slots = (transport->rcpt_per_stack + transport->rcpt_unused + 1) / 2;
+ job->rcpt_limit += rcpt_slots;
+ job->message->rcpt_limit += rcpt_slots;
+ transport->rcpt_unused -= rcpt_slots;
+ }
+ if (msg_verbose)
+ msg_info("%s: %s by %s, level %d", myname, current->message->queue_id,
+ job->message->queue_id, job->stack_level);
+
+ return (job);
+}
+
+/* qmgr_job_pop - remove the job from its job preemption stack */
+
+static void qmgr_job_pop(QMGR_JOB *job)
+{
+ char *myname = "qmgr_job_pop";
+ QMGR_TRANSPORT *transport = job->transport;
+ QMGR_JOB *parent;
+
+ if (msg_verbose)
+ msg_info("%s: %s", myname, job->message->queue_id);
+
+ /*
+ * Sanity checks.
+ */
+ if (job->stack_level <= 0)
+ msg_panic("%s: not on the job stack (%d)", myname, job->stack_level);
+
+ /*
+ * Adjust the number of delivery slots available to preempt job's parent.
+ *
+ * Note that we intentionally do not adjust slots_used of the parent. Doing
+ * so would decrease the maximum per message inflation factor if the
+ * preemption appeared near the end of parent delivery.
+ *
+ * For the same reason we do not adjust parent's slots_available if the
+ * parent is not the original parent that was preempted by this job
+ * (i.e., the original parent job has already completed).
+ *
+ * This is another key part of the theory behind this preempting scheduler.
+ */
+ if ((parent = job->stack_parent) != 0
+ && job->stack_level == parent->stack_level + 1)
+ parent->slots_available -= job->slots_used * transport->slot_cost;
+
+ /*
+ * Remove the job from its parent's children list.
+ */
+ if (parent != 0) {
+ QMGR_LIST_UNLINK(parent->stack_children, QMGR_JOB *, job, stack_siblings);
+ job->stack_parent = 0;
+ }
+
+ /*
+ * If there is a parent, let it adopt all those orphaned children.
+ * Otherwise at least notify the children that their parent is gone.
+ */
+ qmgr_job_parent_gone(job, parent);
+
+ /*
+ * Put the job back to stack level zero.
+ */
+ job->stack_level = 0;
+
+ /*
+ * Explicitely reset the candidate cache. It's not worth trying to skip
+ * this under some complicated conditions - in most cases the popped job
+ * is the current job so we would have to reset it anyway.
+ */
+ RESET_CANDIDATE_CACHE(transport);
+
+ /*
+ * Here we leave the remaining work involving the proper placement on the
+ * job list to the caller. The most important reason for this is that it
+ * allows us not to look up where exactly to place the job.
+ *
+ * The caller is also made responsible for invalidating the current job
+ * cache if necessary.
+ */
+#if 0
+ QMGR_LIST_UNLINK(transport->job_list, QMGR_JOB *, job, transport_peers);
+ QMGR_LIST_LINK(transport->job_list, some_prev, job, some_next, transport_peers);
+
+ if (transport->job_current == job)
+ transport->job_current = job->transport_peers.next;
+#endif
+}
+
+/* qmgr_job_peer_select - select next peer suitable for delivery */
+
+static QMGR_PEER *qmgr_job_peer_select(QMGR_JOB *job)
+{
+ QMGR_PEER *peer;
+ QMGR_MESSAGE *message = job->message;
+
+ if (HAS_ENTRIES(job) && (peer = qmgr_peer_select(job)) != 0)
+ return (peer);
+
+ /*
+ * Try reading in more recipients. Note that we do not try to read them
+ * as soon as possible as that would decrease the chance of per-site
+ * recipient grouping. We waited until reading more is really necessary.
+ *
+ * XXX Workaround for logic mismatch. The message->refcount test needs
+ * explanation. If the refcount is zero, it means that qmgr_active_done()
+ * is beeing completed asynchronously. In such case, we can't read in
+ * more recipients as bad things would happen after qmgr_active_done()
+ * continues processing. Note that this results in the given job beeing
+ * stalled for some time, but fortunately this particular situation is so
+ * rare that it is not critical. Still we seek for better solution.
+ */
+ if (message->rcpt_offset != 0
+ && message->rcpt_limit > message->rcpt_count
+ && message->refcount > 0) {
+ qmgr_message_realloc(message);
+ if (HAS_ENTRIES(job))
+ return (qmgr_peer_select(job));
+ }
+ return (0);
+}
+
+/* qmgr_job_entry_select - select next entry suitable for delivery */
+
+QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *transport)
+{
+ QMGR_JOB *job,
+ *next;
+ QMGR_PEER *peer;
+ QMGR_ENTRY *entry;
+
+ /*
+ * Get the current job if there is one.
+ */
+ if ((job = transport->job_current) == 0)
+ return (0);
+
+ /*
+ * Exercise the preempting algorithm if enabled.
+ *
+ * The slot_cost equal to 1 causes the algorithm to degenerate and is
+ * therefore disabled too.
+ */
+ if (transport->slot_cost >= 2)
+ job = qmgr_job_preempt(job);
+
+ /*
+ * Select next entry suitable for delivery. In case the current job can't
+ * provide one because of the per-destination concurrency limits, we mark
+ * it as a "blocker" job and continue with the next job on the job list.
+ *
+ * Note that the loop also takes care of getting the "stall" jobs (job with
+ * no entries currently available) out of the way if necessary. Stall
+ * jobs can appear in case of multi-transport messages whose recipients
+ * don't fit in-core at once. Some jobs created by such message may have
+ * only few recipients and would stay on the job list until all other
+ * jobs of that message are delivered, blocking precious recipient slots
+ * available to this transport. Or it can happen that the job has some
+ * more entries but suddenly they all get deferred. Whatever the reason,
+ * we retire such jobs below if we happen to come across some.
+ */
+ for ( /* empty */ ; job; job = next) {
+ next = job->transport_peers.next;
+
+ /*
+ * Don't bother if the job is known to have no available entries
+ * because of the per-destination concurrency limits.
+ */
+ if (IS_BLOCKER(job, transport))
+ continue;
+
+ if ((peer = qmgr_job_peer_select(job)) != 0) {
+
+ /*
+ * We have found a suitable peer. Select one of its entries and
+ * adjust the delivery slot counters.
+ */
+ entry = qmgr_entry_select(peer);
+ qmgr_job_count_slots(job);
+
+ /*
+ * Remember the current job for the next time so we don't have to
+ * crawl over all those blockers again. They will be reconsidered
+ * when the concurrency limit permits.
+ */
+ transport->job_current = job;
+
+ /*
+ * In case we selected the very last job entry, remove the job
+ * from the job lists right now.
+ *
+ * This action uses the assumption that once the job entry has been
+ * selected, it can be unselected only before the message ifself
+ * is deferred. Thus the job with all entries selected can't
+ * re-appear with more entries available for selection again
+ * (without reading in more entries from the queue file, which in
+ * turn invokes qmgr_job_obtain() which re-links the job back on
+ * the lists if necessary).
+ *
+ * Note that qmgr_job_move_limits() transfers the recipients slots
+ * correctly even if the job is unlinked from the job list thanks
+ * to the job_next_unread caching.
+ */
+ if (!HAS_ENTRIES(job) && job->message->rcpt_offset == 0)
+ qmgr_job_retire(job);
+
+ /*
+ * Finally. Hand back the fruit of our tedious effort.
+ */
+ return (entry);
+ } else if (HAS_ENTRIES(job)) {
+
+ /*
+ * The job can't be selected due the concurrency limits. Mark it
+ * together with its queues so we know they are blocking the job
+ * list and they get the appropriate treatment. In particular,
+ * all blockers will be reconsidered when one of the problematic
+ * queues will accept more deliveries. And the job itself will be
+ * reconsidered if it is assigned some more entries.
+ */
+ job->blocker_tag = transport->blocker_tag;
+ for (peer = job->peer_list.next; peer; peer = peer->peers.next)
+ if (peer->entry_list.next != 0)
+ peer->queue->blocker_tag = transport->blocker_tag;
+ } else {
+
+ /*
+ * The job is "stalled". Retire it until it either gets freed or
+ * gets more entries later.
+ */
+ qmgr_job_retire(job);
+ }
+ }
+
+ /*
+ * We have not found any entry we could use for delivery. Well, things
+ * must have changed since this transport was selected for asynchronous
+ * allocation. Never mind. Clear the current job pointer and reluctantly
+ * report back that we have failed in our task.
+ */
+ transport->job_current = 0;
+ return (0);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_message.c b/gnu/dist/postfix/src/nqmgr/qmgr_message.c
new file mode 100644
index 00000000000..f83f7e0d105
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_message.c
@@ -0,0 +1,1025 @@
+/*++
+/* NAME
+/* qmgr_message 3
+/* SUMMARY
+/* in-core message structures
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* int qmgr_message_count;
+/* int qmgr_recipient_count;
+/*
+/* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags)
+/* const char *class;
+/* const char *name;
+/* int qflags;
+/*
+/* QMGR_MESSAGE *qmgr_message_realloc(message)
+/* QMGR_MESSAGE *message;
+/*
+/* void qmgr_message_free(message)
+/* QMGR_MESSAGE *message;
+/*
+/* void qmgr_message_update_warn(message)
+/* QMGR_MESSAGE *message;
+/* DESCRIPTION
+/* This module performs en-gross operations on queue messages.
+/*
+/* qmgr_message_count is a global counter for the total number
+/* of in-core message structures (i.e. the total size of the
+/* `active' message queue).
+/*
+/* qmgr_recipient_count is a global counter for the total number
+/* of in-core recipient structures (i.e. the sum of all recipients
+/* in all in-core message structures).
+/*
+/* qmgr_message_alloc() creates an in-core message structure
+/* with sender and recipient information taken from the named queue
+/* file. A null result means the queue file could not be read or
+/* that the queue file contained incorrect information. A result
+/* QMGR_MESSAGE_LOCKED means delivery must be deferred. The number
+/* of recipients read from a queue file is limited by the global
+/* var_qmgr_rcpt_limit configuration parameter. When the limit
+/* is reached, the \fIrcpt_offset\fR structure member is set to
+/* the position where the read was terminated. Recipients are
+/* run through the resolver, and are assigned to destination
+/* queues. Recipients that cannot be assigned are deferred or
+/* bounced. Mail that has bounced twice is silently absorbed.
+/*
+/* qmgr_message_realloc() resumes reading recipients from the queue
+/* file, and updates the recipient list and \fIrcpt_offset\fR message
+/* structure members. A null result means that the file could not be
+/* read or that the file contained incorrect information. Recipient
+/* limit imposed this time is based on the position of the message
+/* job(s) on corresponding transport job list(s). It's considered
+/* an error to call this when the recipient slots can't be allocated.
+/*
+/* qmgr_message_free() destroys an in-core message structure and makes
+/* the resources available for reuse. It is an error to destroy
+/* a message structure that is still referenced by queue entry structures.
+/*
+/* qmgr_message_update_warn() takes a closed message, opens it, updates
+/* the warning field, and closes it again.
+/* DIAGNOSTICS
+/* Warnings: malformed message file. Fatal errors: out of memory.
+/* SEE ALSO
+/* envelope(3) message envelope parser
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h> /* sscanf() */
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <split_at.h>
+#include <valid_hostname.h>
+#include <argv.h>
+#include <stringops.h>
+#include <myflock.h>
+#include <sane_time.h>
+
+/* Global library. */
+
+#include <dict.h>
+#include <mail_queue.h>
+#include <mail_params.h>
+#include <canon_addr.h>
+#include <record.h>
+#include <rec_type.h>
+#include <sent.h>
+#include <deliver_completed.h>
+#include <mail_addr_find.h>
+#include <opened.h>
+#include <resolve_local.h>
+#include <verp_sender.h>
+
+/* Client stubs. */
+
+#include <resolve_clnt.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+int qmgr_message_count;
+int qmgr_recipient_count;
+
+/* qmgr_message_create - create in-core message structure */
+
+static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
+ const char *queue_id, int qflags)
+{
+ QMGR_MESSAGE *message;
+
+ message = (QMGR_MESSAGE *) mymalloc(sizeof(QMGR_MESSAGE));
+ qmgr_message_count++;
+ message->flags = 0;
+ message->qflags = qflags;
+ message->fp = 0;
+ message->refcount = 0;
+ message->single_rcpt = 0;
+ message->arrival_time = 0;
+ message->queued_time = sane_time();
+ message->data_offset = 0;
+ message->queue_id = mystrdup(queue_id);
+ message->queue_name = mystrdup(queue_name);
+ message->sender = 0;
+ message->errors_to = 0;
+ message->return_receipt = 0;
+ message->filter_xport = 0;
+ message->inspect_xport = 0;
+ message->data_size = 0;
+ message->warn_offset = 0;
+ message->warn_time = 0;
+ message->rcpt_offset = 0;
+ message->verp_delims = 0;
+ message->unread_offset = 0;
+ qmgr_rcpt_list_init(&message->rcpt_list);
+ message->rcpt_count = 0;
+ message->rcpt_limit = var_qmgr_msg_rcpt_limit;
+ message->rcpt_unread = 0;
+ QMGR_LIST_INIT(message->job_list);
+ return (message);
+}
+
+/* qmgr_message_close - close queue file */
+
+static void qmgr_message_close(QMGR_MESSAGE *message)
+{
+ vstream_fclose(message->fp);
+ message->fp = 0;
+}
+
+/* qmgr_message_open - open queue file */
+
+static int qmgr_message_open(QMGR_MESSAGE *message)
+{
+
+ /*
+ * Sanity check.
+ */
+ if (message->fp)
+ msg_panic("%s: queue file is open", message->queue_id);
+
+ /*
+ * Open this queue file. Skip files that we cannot open. Back off when
+ * the system appears to be running out of resources.
+ */
+ if ((message->fp = mail_queue_open(message->queue_name,
+ message->queue_id,
+ O_RDWR, 0)) == 0) {
+ if (errno != ENOENT)
+ msg_fatal("open %s %s: %m", message->queue_name, message->queue_id);
+ msg_warn("open %s %s: %m", message->queue_name, message->queue_id);
+ return (-1);
+ }
+ return (0);
+}
+
+/* qmgr_message_oldstyle_scan - extract required information from an old style queue file */
+
+static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message)
+{
+ VSTRING *buf;
+ long orig_offset,
+ curr_offset,
+ extra_offset;
+ int rec_type;
+ char *start;
+
+ /*
+ * Initialize. No early returns or we have a memory leak.
+ */
+ buf = vstring_alloc(100);
+ if ((orig_offset = vstream_ftell(message->fp)) < 0)
+ msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
+
+ /*
+ * Rewind to the very beginning to make sure we see all records.
+ */
+ if (vstream_fseek(message->fp, 0, SEEK_SET) < 0)
+ msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
+
+ /*
+ * Scan through the old style queue file. Count the total number of
+ * recipients and find the data/extra sections offsets. Note that the new
+ * queue files require that data_size equals extra_offset - data_offset,
+ * so we set data_size to this as well and ignore the size record itself
+ * completely.
+ */
+ message->rcpt_unread = 0;
+ do {
+ if ((curr_offset = vstream_ftell(message->fp)) < 0)
+ msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
+ rec_type = rec_get(message->fp, buf, 0);
+ start = vstring_str(buf);
+ if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_RCPT) {
+ message->rcpt_unread++;
+ } else if (rec_type == REC_TYPE_MESG) {
+ if ((message->data_offset = vstream_ftell(message->fp)) < 0)
+ msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
+ if ((extra_offset = atol(start)) <= curr_offset)
+ msg_fatal("bad extra offset %s file %s",
+ start, VSTREAM_PATH(message->fp));
+ if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0)
+ msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
+ message->data_size = extra_offset - message->data_offset;
+ }
+ } while (rec_type > 0 && rec_type != REC_TYPE_END);
+
+ /*
+ * Clean up.
+ */
+ if (vstream_fseek(message->fp, orig_offset, SEEK_SET) < 0)
+ msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
+ vstring_free(buf);
+
+ /*
+ * Sanity checks. Verify that all required information was found,
+ * including the queue file end marker.
+ */
+ if (message->data_offset == 0 || rec_type != REC_TYPE_END)
+ msg_fatal("%s: envelope records out of order", message->queue_id);
+}
+
+/* qmgr_message_read - read envelope records */
+
+static int qmgr_message_read(QMGR_MESSAGE *message)
+{
+ VSTRING *buf;
+ long extra_offset;
+ int rec_type;
+ long curr_offset;
+ long save_offset = message->rcpt_offset; /* save a flag */
+ char *start;
+ int recipient_limit;
+
+ /*
+ * Initialize. No early returns or we have a memory leak.
+ */
+ buf = vstring_alloc(100);
+
+ /*
+ * If we re-open this file, skip over on-file recipient records that we
+ * already looked at, and refill the in-core recipient address list.
+ *
+ * For the first time, the message recipient limit is calculated from the
+ * global recipient limit. This is to avoid reading little recipients
+ * when the active queue is near empty. When the queue becomes full, only
+ * the necessary amount is read in core. Such priming is necessary
+ * because there are no message jobs yet.
+ *
+ * For the next time, the recipient limit is based solely on the message
+ * jobs' positions in the job lists and/or job stacks.
+ */
+ if (message->rcpt_offset) {
+ if (message->rcpt_list.len)
+ msg_panic("%s: recipient list not empty on recipient reload", message->queue_id);
+ if (vstream_fseek(message->fp, message->rcpt_offset, SEEK_SET) < 0)
+ msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
+ message->rcpt_offset = 0;
+ recipient_limit = message->rcpt_limit - message->rcpt_count;
+ } else {
+ recipient_limit = var_qmgr_rcpt_limit - qmgr_recipient_count;
+ if (recipient_limit < message->rcpt_limit)
+ recipient_limit = message->rcpt_limit;
+ }
+ if (recipient_limit <= 0)
+ msg_panic("%s: no recipient slots available", message->queue_id);
+
+ /*
+ * Read envelope records. XXX Rely on the front-end programs to enforce
+ * record size limits. Read up to recipient_limit recipients from the
+ * queue file, to protect against memory exhaustion. Recipient records
+ * may appear before or after the message content, so we keep reading
+ * from the queue file until we have enough recipients (rcpt_offset != 0)
+ * and until we know where the message content starts (data_offset != 0).
+ *
+ * Note that the total recipient count record is accurate only for fresh
+ * queue files. After some of the recipients are marked as done and the
+ * queue file is deferred, it can be used as upper bound estimate only.
+ * Fortunately, this poses no major problem on the scheduling algorithm,
+ * as the only impact is that the already deferred messages are not
+ * chosen by qmgr_job_candidate() as often as they could.
+ */
+ do {
+ if ((curr_offset = vstream_ftell(message->fp)) < 0)
+ msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
+ if (curr_offset == message->data_offset && curr_offset > 0) {
+ extra_offset = curr_offset + message->data_size;
+ if (extra_offset <= curr_offset)
+ msg_fatal("bad extra offset %ld file %s",
+ extra_offset, VSTREAM_PATH(message->fp));
+ if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0)
+ msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
+ curr_offset = extra_offset;
+ }
+ rec_type = rec_get(message->fp, buf, 0);
+ start = vstring_str(buf);
+ if (rec_type == REC_TYPE_SIZE) {
+ if (message->data_size == 0) {
+ switch (sscanf(start, "%ld %ld %d", &message->data_size,
+ &message->data_offset, &message->rcpt_unread)) {
+ case 1:
+
+ /*
+ * Gather data_size, data_offset and rcpt_unread values
+ * from the old style queue file.
+ */
+ qmgr_message_oldstyle_scan(message);
+ break;
+ case 3:
+
+ /*
+ * No extra work for new style queue files.
+ */
+ break;
+ default:
+ msg_fatal("%s: weird size record", message->queue_id);
+ break;
+ }
+ }
+ } else if (rec_type == REC_TYPE_TIME) {
+ if (message->arrival_time == 0)
+ message->arrival_time = atol(start);
+ } else if (rec_type == REC_TYPE_FILT) {
+ if (message->filter_xport == 0)
+ message->filter_xport = mystrdup(start);
+ } else if (rec_type == REC_TYPE_INSP) {
+ if (message->inspect_xport == 0)
+ message->inspect_xport = mystrdup(start);
+ } else if (rec_type == REC_TYPE_FROM) {
+ if (message->sender == 0) {
+ message->sender = mystrdup(start);
+ opened(message->queue_id, message->sender,
+ message->data_size, message->rcpt_unread,
+ "queue %s", message->queue_name);
+ }
+ } else if (rec_type == REC_TYPE_DONE) {
+ if (curr_offset > message->unread_offset) {
+ message->unread_offset = curr_offset;
+ message->rcpt_unread--;
+ }
+ } else if (rec_type == REC_TYPE_RCPT) {
+ if (message->rcpt_list.len < recipient_limit) {
+ message->rcpt_unread--;
+ qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, start);
+ if (message->rcpt_list.len >= recipient_limit) {
+ if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
+ msg_fatal("vstream_ftell %s: %m",
+ VSTREAM_PATH(message->fp));
+ if (message->data_offset != 0
+ && message->errors_to != 0
+ && message->return_receipt != 0)
+ break;
+ }
+ }
+ } else if (rec_type == REC_TYPE_ERTO) {
+ if (message->errors_to == 0) {
+ message->errors_to = mystrdup(start);
+ if (message->data_offset != 0
+ && message->rcpt_offset != 0
+ && message->return_receipt != 0)
+ break;
+ }
+ } else if (rec_type == REC_TYPE_RRTO) {
+ if (message->return_receipt == 0) {
+ message->return_receipt = mystrdup(start);
+ if (message->data_offset != 0
+ && message->rcpt_offset != 0
+ && message->errors_to != 0)
+ break;
+ }
+ } else if (rec_type == REC_TYPE_WARN) {
+ if (message->warn_offset == 0) {
+ message->warn_offset = curr_offset;
+ message->warn_time = atol(start);
+ }
+ } else if (rec_type == REC_TYPE_VERP) {
+ if (message->verp_delims == 0) {
+ if (verp_delims_verify(start) != 0) {
+ msg_warn("%s: bad VERP record content: \"%s\"",
+ message->queue_id, start);
+ } else {
+ message->single_rcpt = 1;
+ message->verp_delims = mystrdup(start);
+ }
+ }
+ }
+ } while (rec_type > 0 && rec_type != REC_TYPE_END);
+
+ /*
+ * Avoid clumsiness elsewhere in the program. When sending data across an
+ * IPC channel, sending an empty string is more convenient than sending a
+ * null pointer.
+ */
+ if (message->errors_to == 0)
+ message->errors_to = mystrdup("");
+ if (message->return_receipt == 0)
+ message->return_receipt = mystrdup("");
+
+ /*
+ * Clean up.
+ */
+ vstring_free(buf);
+
+ /*
+ * Sanity checks. Verify that all required information was found,
+ * including the queue file end marker.
+ */
+ if (message->rcpt_unread < 0
+ || (message->rcpt_offset == 0 && message->rcpt_unread != 0)) {
+ msg_warn("%s: rcpt count mismatch (%d)",
+ message->queue_id, message->rcpt_unread);
+ message->rcpt_unread = 0;
+ }
+ if (message->arrival_time == 0
+ || message->sender == 0
+ || message->data_offset == 0
+ || (message->rcpt_offset == 0 && rec_type != REC_TYPE_END)) {
+ msg_warn("%s: envelope records out of order", message->queue_id);
+ message->rcpt_offset = save_offset; /* restore flag */
+ message->rcpt_unread += message->rcpt_list.len;
+ qmgr_rcpt_list_free(&message->rcpt_list);
+ qmgr_rcpt_list_init(&message->rcpt_list);
+ return (-1);
+ } else {
+ return (0);
+ }
+}
+
+/* qmgr_message_update_warn - update the time of next delay warning */
+
+void qmgr_message_update_warn(QMGR_MESSAGE *message)
+{
+
+ /*
+ * XXX eventually this should let us schedule multiple warnings, right
+ * now it just allows for one.
+ */
+ if (qmgr_message_open(message)
+ || vstream_fseek(message->fp, message->warn_offset, SEEK_SET) < 0
+ || rec_fprintf(message->fp, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT, 0L) < 0
+ || vstream_fflush(message->fp))
+ msg_fatal("update queue file %s: %m", VSTREAM_PATH(message->fp));
+ qmgr_message_close(message);
+}
+
+/* qmgr_message_sort_compare - compare recipient information */
+
+static int qmgr_message_sort_compare(const void *p1, const void *p2)
+{
+ QMGR_RCPT *rcpt1 = (QMGR_RCPT *) p1;
+ QMGR_RCPT *rcpt2 = (QMGR_RCPT *) p2;
+ QMGR_QUEUE *queue1;
+ QMGR_QUEUE *queue2;
+ char *at1;
+ char *at2;
+ int result;
+
+ /*
+ * Compare most significant to least significant recipient attributes.
+ */
+ if ((queue1 = rcpt1->queue) != 0 && (queue2 = rcpt2->queue) != 0) {
+
+ /*
+ * Compare message transport.
+ */
+ if ((result = strcmp(queue1->transport->name,
+ queue2->transport->name)) != 0)
+ return (result);
+
+ /*
+ * Compare (already lowercased) next-hop hostname.
+ */
+ if ((result = strcmp(queue1->name, queue2->name)) != 0)
+ return (result);
+ }
+
+ /*
+ * Compare recipient domain.
+ */
+ if ((at1 = strrchr(rcpt1->address, '@')) != 0
+ && (at2 = strrchr(rcpt2->address, '@')) != 0
+ && (result = strcasecmp(at1, at2)) != 0)
+ return (result);
+
+ /*
+ * Compare recipient address.
+ */
+ return (strcmp(rcpt1->address, rcpt2->address));
+}
+
+/* qmgr_message_sort - sort message recipient addresses by domain */
+
+static void qmgr_message_sort(QMGR_MESSAGE *message)
+{
+ qsort((char *) message->rcpt_list.info, message->rcpt_list.len,
+ sizeof(message->rcpt_list.info[0]), qmgr_message_sort_compare);
+ if (msg_verbose) {
+ QMGR_RCPT_LIST list = message->rcpt_list;
+ QMGR_RCPT *rcpt;
+
+ msg_info("start sorted recipient list");
+ for (rcpt = list.info; rcpt < list.info + list.len; rcpt++)
+ msg_info("qmgr_message_sort: %s", rcpt->address);
+ msg_info("end sorted recipient list");
+ }
+}
+
+/* qmgr_message_resolve - resolve recipients */
+
+static void qmgr_message_resolve(QMGR_MESSAGE *message)
+{
+ static ARGV *defer_xport_argv;
+ QMGR_RCPT_LIST list = message->rcpt_list;
+ QMGR_RCPT *recipient;
+ QMGR_TRANSPORT *transport = 0;
+ QMGR_QUEUE *queue = 0;
+ RESOLVE_REPLY reply;
+ const char *newloc;
+ char *at;
+ char **cpp;
+ char *domain;
+ const char *junk;
+ char *nexthop;
+ int len;
+
+#define STREQ(x,y) (strcmp(x,y) == 0)
+#define STR vstring_str
+#define LEN VSTRING_LEN
+#define UPDATE(ptr,new) { myfree(ptr); ptr = mystrdup(new); }
+
+ resolve_clnt_init(&reply);
+ for (recipient = list.info; recipient < list.info + list.len; recipient++) {
+
+ /*
+ * This may be a bit late in the game, but it is the most convenient
+ * place to scrutinize the destination address syntax. We have a
+ * complete queue file, so bouncing is easy. That luxury is not
+ * available to the cleanup service. The main issue is that we want
+ * to have this test in one place, instead of having to do this in
+ * every front-ent program.
+ */
+ if ((at = strrchr(recipient->address, '@')) != 0
+ && (at + 1)[strspn(at + 1, "[]0123456789.")] != 0
+ && valid_hostname(at + 1, DONT_GRIPE) == 0) {
+ qmgr_bounce_recipient(message, recipient,
+ "bad host/domain syntax: \"%s\"", at + 1);
+ continue;
+ }
+
+ /*
+ * Resolve the destination to (transport, nexthop, address). The
+ * result address may differ from the one specified by the sender.
+ */
+ resolve_clnt_query(recipient->address, &reply);
+ if (message->filter_xport) {
+ vstring_strcpy(reply.transport, message->filter_xport);
+ if ((nexthop = split_at(STR(reply.transport), ':')) == 0
+ || *nexthop == 0)
+ nexthop = var_myhostname;
+ vstring_strcpy(reply.nexthop, nexthop);
+ } else {
+ if (!STREQ(recipient->address, STR(reply.recipient)))
+ UPDATE(recipient->address, STR(reply.recipient));
+ }
+
+ /*
+ * XXX The nexthop destination is also used as lookup key for the
+ * per-destination queue. Fold the nexthop to lower case so that we
+ * don't have multiple queues for the same site.
+ */
+ lowercase(STR(reply.nexthop));
+
+ /*
+ * Bounce recipients that have moved. We do it here instead of in the
+ * local delivery agent. The benefit is that we can bounce mail for
+ * virtual addresses, not just local addresses only, and that there
+ * is no need to run a local delivery agent just for the sake of
+ * relocation notices. The downside is that this table has no effect
+ * on local alias expansion results, so that mail will have to make
+ * almost an entire iteration through the mail system.
+ */
+#define IGNORE_ADDR_EXTENSION ((char **) 0)
+
+ if (qmgr_relocated != 0) {
+ if ((newloc = mail_addr_find(qmgr_relocated, recipient->address,
+ IGNORE_ADDR_EXTENSION)) != 0) {
+ qmgr_bounce_recipient(message, recipient,
+ "user has moved to %s", newloc);
+ continue;
+ } else if (dict_errno != 0) {
+ qmgr_defer_recipient(message, recipient->address,
+ "relocated map lookup failure");
+ continue;
+ }
+ }
+
+ /*
+ * Bounce mail to non-existent users in virtual domains.
+ */
+ if (qmgr_virtual != 0
+ && (at = strrchr(recipient->address, '@')) != 0
+ && !resolve_local(at + 1)) {
+ domain = lowercase(mystrdup(at + 1));
+ junk = maps_find(qmgr_virtual, domain, 0);
+ myfree(domain);
+ if (junk) {
+ qmgr_bounce_recipient(message, recipient,
+ "unknown user: \"%s\"", recipient->address);
+ continue;
+ }
+ }
+
+ /*
+ * Bounce recipient addresses that start with `-'. External commands
+ * may misinterpret such addresses as command-line options.
+ *
+ * In theory I could say people should always carefully set up their
+ * master.cf pipe mailer entries with `--' before the first
+ * non-option argument, but mistakes will happen regardless.
+ *
+ * Therefore the protection is put in place here, in the queue manager,
+ * where it cannot be bypassed.
+ */
+ if (var_allow_min_user == 0 && recipient->address[0] == '-') {
+ qmgr_bounce_recipient(message, recipient,
+ "invalid recipient syntax: \"%s\"",
+ recipient->address);
+ continue;
+ }
+
+ /*
+ * Queues are identified by the transport name and by the next-hop
+ * hostname. When the delivery agent accepts only one recipient per
+ * delivery, give each recipient its own queue, so that deliveries to
+ * different recipients of the same message can happen in parallel.
+ * This also has the benefit that one bad recipient cannot interfere
+ * with deliveries to other recipients. XXX Should split the address
+ * on the recipient delimiter if one is defined, but doing a proper
+ * job requires knowledge of local aliases. Yuck! I don't want to
+ * duplicate delivery-agent specific knowledge in the queue manager.
+ * XXX The nexthop field is overloaded to serve as destination and as
+ * queue name. Should have separate fields for queue name and for
+ * destination.
+ */
+ at = strrchr(STR(reply.recipient), '@');
+ len = (at ? (at - STR(reply.recipient)) : strlen(STR(reply.recipient)));
+
+ /*
+ * Look up or instantiate the proper transport. We're working a
+ * little ahead, doing queue management stuff that used to be done
+ * way down.
+ */
+ if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
+ if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
+ transport = qmgr_transport_create(STR(reply.transport));
+ queue = 0;
+ }
+ if (transport->recipient_limit == 1) {
+ VSTRING_SPACE(reply.nexthop, len + 1);
+ memmove(STR(reply.nexthop) + len + 1, STR(reply.nexthop),
+ LEN(reply.nexthop) + 1);
+ memcpy(STR(reply.nexthop), STR(reply.recipient), len);
+ STR(reply.nexthop)[len] = '@';
+ lowercase(STR(reply.nexthop));
+ }
+
+ /*
+ * Discard mail to the local double bounce address here, so this
+ * system can run without a local delivery agent. They'd still have
+ * to configure something for mail directed to the local postmaster,
+ * though, but that is an RFC requirement anyway.
+ */
+ if (at == 0 || resolve_local(at + 1)) {
+ if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
+ len) == 0
+ && !var_double_bounce_sender[len]) {
+ sent(message->queue_id, recipient->address,
+ "none", message->arrival_time, "discarded");
+ deliver_completed(message->fp, recipient->offset);
+ msg_warn("%s: undeliverable postmaster notification discarded",
+ message->queue_id);
+ continue;
+ }
+ }
+
+ /*
+ * Optionally defer deliveries over specific transports, unless the
+ * restriction is lifted temporarily.
+ */
+ if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DEAD) == 0) {
+ if (defer_xport_argv == 0)
+ defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,");
+ for (cpp = defer_xport_argv->argv; *cpp; cpp++)
+ if (strcmp(*cpp, STR(reply.transport)) == 0)
+ break;
+ if (*cpp) {
+ qmgr_defer_recipient(message, recipient->address,
+ "deferred transport");
+ continue;
+ }
+ }
+
+ /*
+ * XXX Gross hack alert. We want to group recipients by transport and
+ * by next-hop hostname, in order to minimize the number of network
+ * transactions. However, it would be wasteful to have an in-memory
+ * resolver reply structure for each in-core recipient. Instead, we
+ * bind each recipient to an in-core queue instance which is needed
+ * anyway. That gives all information needed for recipient grouping.
+ */
+#if 0
+
+ /*
+ * Look up or instantiate the proper transport.
+ */
+ if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
+ if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
+ transport = qmgr_transport_create(STR(reply.transport));
+ queue = 0;
+ }
+#endif
+
+ /*
+ * This transport is dead. Defer delivery to this recipient.
+ */
+ if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
+ qmgr_defer_recipient(message, recipient->address, transport->reason);
+ continue;
+ }
+
+ /*
+ * This transport is alive. Find or instantiate a queue for this
+ * recipient.
+ */
+ if (queue == 0 || !STREQ(queue->name, STR(reply.nexthop))) {
+ if ((queue = qmgr_queue_find(transport, STR(reply.nexthop))) == 0)
+ queue = qmgr_queue_create(transport, STR(reply.nexthop));
+ }
+
+ /*
+ * This queue is dead. Defer delivery to this recipient.
+ */
+ if (queue->window == 0) {
+ qmgr_defer_recipient(message, recipient->address, queue->reason);
+ continue;
+ }
+
+ /*
+ * This queue is alive. Bind this recipient to this queue instance.
+ */
+ recipient->queue = queue;
+ }
+ resolve_clnt_free(&reply);
+}
+
+/* qmgr_message_assign - assign recipients to specific delivery requests */
+
+static void qmgr_message_assign(QMGR_MESSAGE *message)
+{
+ QMGR_RCPT_LIST list = message->rcpt_list;
+ QMGR_RCPT *recipient;
+ QMGR_ENTRY *entry = 0;
+ QMGR_QUEUE *queue;
+ QMGR_JOB *job = 0;
+ QMGR_PEER *peer = 0;
+
+ /*
+ * Try to bundle as many recipients in a delivery request as we can. When
+ * the recipient resolves to the same site and transport as the previous
+ * recipient, do not create a new queue entry, just move that recipient
+ * to the recipient list of the existing queue entry. All this provided
+ * that we do not exceed the transport-specific limit on the number of
+ * recipients per transaction. Skip recipients with a dead transport or
+ * destination.
+ */
+#define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit)))
+
+ for (recipient = list.info; recipient < list.info + list.len; recipient++) {
+ if ((queue = recipient->queue) != 0) {
+ if (message->single_rcpt || entry == 0 || entry->queue != queue
+ || !LIMIT_OK(queue->transport->recipient_limit,
+ entry->rcpt_list.len)) {
+
+ /*
+ * Lookup or instantiate the message job if necessary.
+ */
+ if (job == 0 || queue->transport != job->transport) {
+ job = qmgr_job_obtain(message, queue->transport);
+ peer = 0;
+ }
+
+ /*
+ * Lookup or instantiate job peer if necessary.
+ */
+ if (peer == 0 || queue != peer->queue) {
+ if ((peer = qmgr_peer_find(job, queue)) == 0)
+ peer = qmgr_peer_create(job, queue);
+ }
+
+ /*
+ * Create new peer entry.
+ */
+ entry = qmgr_entry_create(peer, message);
+ job->read_entries++;
+ }
+
+ /*
+ * Add the recipient to the current entry and increase all those
+ * recipient counters accordingly.
+ */
+ qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset, recipient->address);
+ job->rcpt_count++;
+ message->rcpt_count++;
+ qmgr_recipient_count++;
+ }
+ }
+
+ /*
+ * Release the message recipient list and reinitialize it for the next
+ * time.
+ */
+ qmgr_rcpt_list_free(&message->rcpt_list);
+ qmgr_rcpt_list_init(&message->rcpt_list);
+
+ /*
+ * Note that even if qmgr_job_obtain() reset the job candidate cache of
+ * all transports to which we assigned new recipients, this message may
+ * have other jobs which we didn't touch at all this time. But the number
+ * of unread recipients affecting the candidate selection might have
+ * changed considerably, so we must invalidate the caches if it might be
+ * of some use.
+ */
+ for (job = message->job_list.next; job; job = job->message_peers.next)
+ if (job->selected_entries < job->read_entries
+ && job->blocker_tag != job->transport->blocker_tag)
+ job->transport->candidate_cache_current = 0;
+}
+
+/* qmgr_message_move_limits - recycle unused recipient slots */
+
+static void qmgr_message_move_limits(QMGR_MESSAGE *message)
+{
+ QMGR_JOB *job;
+
+ for (job = message->job_list.next; job; job = job->message_peers.next)
+ qmgr_job_move_limits(job);
+}
+
+/* qmgr_message_free - release memory for in-core message structure */
+
+void qmgr_message_free(QMGR_MESSAGE *message)
+{
+ QMGR_JOB *job;
+
+ if (message->refcount != 0)
+ msg_panic("qmgr_message_free: reference len: %d", message->refcount);
+ if (message->fp)
+ msg_panic("qmgr_message_free: queue file is open");
+ while ((job = message->job_list.next) != 0)
+ qmgr_job_free(job);
+ myfree(message->queue_id);
+ myfree(message->queue_name);
+ if (message->sender)
+ myfree(message->sender);
+ if (message->verp_delims)
+ myfree(message->verp_delims);
+ if (message->errors_to)
+ myfree(message->errors_to);
+ if (message->return_receipt)
+ myfree(message->return_receipt);
+ if (message->filter_xport)
+ myfree(message->filter_xport);
+ if (message->inspect_xport)
+ myfree(message->inspect_xport);
+ qmgr_rcpt_list_free(&message->rcpt_list);
+ qmgr_message_count--;
+ myfree((char *) message);
+}
+
+/* qmgr_message_alloc - create in-core message structure */
+
+QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
+ int qflags)
+{
+ char *myname = "qmgr_message_alloc";
+ QMGR_MESSAGE *message;
+
+ if (msg_verbose)
+ msg_info("%s: %s %s", myname, queue_name, queue_id);
+
+ /*
+ * Create an in-core message structure.
+ */
+ message = qmgr_message_create(queue_name, queue_id, qflags);
+
+ /*
+ * Extract message envelope information: time of arrival, sender address,
+ * recipient addresses. Skip files with malformed envelope information.
+ */
+#define QMGR_LOCK_MODE (MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT)
+
+ if (qmgr_message_open(message) < 0) {
+ qmgr_message_free(message);
+ return (0);
+ }
+ if (myflock(vstream_fileno(message->fp), INTERNAL_LOCK, QMGR_LOCK_MODE) < 0) {
+ msg_info("%s: skipped, still being delivered", queue_id);
+ qmgr_message_close(message);
+ qmgr_message_free(message);
+ return (QMGR_MESSAGE_LOCKED);
+ }
+ if (qmgr_message_read(message) < 0) {
+ qmgr_message_close(message);
+ qmgr_message_free(message);
+ return (0);
+ } else {
+
+ /*
+ * Reset the defer log. This code should not be here, but we must
+ * reset the defer log *after* acquiring the exclusive lock on the
+ * queue file and *before* resolving new recipients. Since all those
+ * operations are encapsulated so nicely by this routine, the defer
+ * log reset has to be done here as well.
+ */
+ if (mail_queue_remove(MAIL_QUEUE_DEFER, queue_id) && errno != ENOENT)
+ msg_fatal("%s: %s: remove %s %s: %m", myname,
+ queue_id, MAIL_QUEUE_DEFER, queue_id);
+ qmgr_message_sort(message);
+ qmgr_message_resolve(message);
+ qmgr_message_sort(message);
+ qmgr_message_assign(message);
+ qmgr_message_close(message);
+ if (message->rcpt_offset == 0)
+ qmgr_message_move_limits(message);
+ return (message);
+ }
+}
+
+/* qmgr_message_realloc - refresh in-core message structure */
+
+QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message)
+{
+ char *myname = "qmgr_message_realloc";
+
+ /*
+ * Sanity checks.
+ */
+ if (message->rcpt_offset <= 0)
+ msg_panic("%s: invalid offset: %ld", myname, message->rcpt_offset);
+ if (msg_verbose)
+ msg_info("%s: %s %s offset %ld", myname, message->queue_name,
+ message->queue_id, message->rcpt_offset);
+
+ /*
+ * Extract recipient addresses. Skip files with malformed envelope
+ * information.
+ */
+ if (qmgr_message_open(message) < 0)
+ return (0);
+ if (qmgr_message_read(message) < 0) {
+ qmgr_message_close(message);
+ return (0);
+ } else {
+ qmgr_message_sort(message);
+ qmgr_message_resolve(message);
+ qmgr_message_sort(message);
+ qmgr_message_assign(message);
+ qmgr_message_close(message);
+ if (message->rcpt_offset == 0)
+ qmgr_message_move_limits(message);
+ return (message);
+ }
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_move.c b/gnu/dist/postfix/src/nqmgr/qmgr_move.c
new file mode 100644
index 00000000000..a4e55f0585f
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_move.c
@@ -0,0 +1,104 @@
+/*++
+/* NAME
+/* qmgr_move 3
+/* SUMMARY
+/* move queue entries to another queue
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* void qmgr_move(from, to, time_stamp)
+/* const char *from;
+/* const char *to;
+/* time_t time_stamp;
+/* DESCRIPTION
+/* The \fBqmgr_move\fR routine scans the \fIfrom\fR queue for entries
+/* with valid queue names and moves them to the \fIto\fR queue.
+/* If \fItime_stamp\fR is non-zero, the queue file time stamps are
+/* set to the specified value.
+/* Entries with invalid names are left alone. No attempt is made to
+/* look for other badness such as multiple links or weird file types.
+/* These issues are dealt with when a queue file is actually opened.
+/* 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 <sys/stat.h>
+#include <string.h>
+#include <utime.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <scan_dir.h>
+#include <recipient_list.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_scan_dir.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_move - move queue entries to another queue, leave bad files alone */
+
+void qmgr_move(const char *src_queue, const char *dst_queue,
+ time_t time_stamp)
+{
+ char *myname = "qmgr_move";
+ SCAN_DIR *queue_dir;
+ char *queue_id;
+ struct utimbuf tbuf;
+ const char *path;
+
+ if (strcmp(src_queue, dst_queue) == 0)
+ msg_panic("%s: source queue %s is destination", myname, src_queue);
+ if (msg_verbose)
+ msg_info("start move queue %s -> %s", src_queue, dst_queue);
+
+ queue_dir = scan_dir_open(src_queue);
+ while ((queue_id = mail_scan_dir_next(queue_dir)) != 0) {
+ if (mail_queue_id_ok(queue_id)) {
+ if (time_stamp > 0) {
+ tbuf.actime = tbuf.modtime = time_stamp;
+ path = mail_queue_path((VSTRING *) 0, src_queue, queue_id);
+ if (utime(path, &tbuf) < 0) {
+ if (errno != ENOENT)
+ msg_fatal("%s: update %s time stamps: %m", myname, path);
+ msg_warn("%s: update %s time stamps: %m", myname, path);
+ continue;
+ }
+ }
+ if (mail_queue_rename(queue_id, src_queue, dst_queue)) {
+ if (errno != ENOENT)
+ msg_fatal("%s: rename %s from %s to %s: %m",
+ myname, queue_id, src_queue, dst_queue);
+ msg_warn("%s: rename %s from %s to %s: %m",
+ myname, queue_id, src_queue, dst_queue);
+ continue;
+ }
+ if (msg_verbose)
+ msg_info("%s: moved %s from %s to %s",
+ myname, queue_id, src_queue, dst_queue);
+ } else {
+ msg_warn("%s: ignored: queue %s id %s",
+ myname, src_queue, queue_id);
+ }
+ }
+ scan_dir_close(queue_dir);
+
+ if (msg_verbose)
+ msg_info("end move queue %s -> %s", src_queue, dst_queue);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_peer.c b/gnu/dist/postfix/src/nqmgr/qmgr_peer.c
new file mode 100644
index 00000000000..9b61e64e521
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_peer.c
@@ -0,0 +1,136 @@
+/*++
+/* NAME
+/* qmgr_peer 3
+/* SUMMARY
+/* per-job peers
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* QMGR_PEER *qmgr_peer_create(job, queue)
+/* QMGR_JOB *job;
+/* QMGR_QUEUE *queue;
+/*
+/* QMGR_PEER *qmgr_peer_find(job, queue)
+/* QMGR_JOB *job;
+/* QMGR_QUEUE *queue;
+/*
+/* void qmgr_peer_free(peer)
+/* QMGR_PEER *peer;
+/*
+/* QMGR_PEER *qmgr_peer_select(job)
+/* QMGR_JOB *job;
+/*
+/* DESCRIPTION
+/* These routines add/delete/manipulate per-job peers.
+/* Each queue corresponds to a specific job and destination.
+/* It is similar to per-transport queue structure, but groups
+/* only the entries of the given job.
+/*
+/* qmgr_peer_create() creates an empty peer structure for the named
+/* job and destination. It is an error to call this function
+/* if a peer for given combination already exists.
+/*
+/* qmgr_peer_find() looks up the peer for the named destination
+/* for the named job. A null result means that the peer
+/* was not found.
+/*
+/* qmgr_peer_free() disposes of a per-job peer after all
+/* its entries have been taken care of. It is an error to dispose
+/* of a peer still in use.
+/*
+/* qmgr_peer_select() attempts to find a peer of named job that
+/* has messages pending delivery. This routine implements
+/* round-robin search among job's peers.
+/* DIAGNOSTICS
+/* Panic: consistency check failure.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Patrik Rak
+/* patrik@raxoft.cz
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <mymalloc.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_peer_create - create and initialize message peer structure */
+
+QMGR_PEER *qmgr_peer_create(QMGR_JOB *job, QMGR_QUEUE *queue)
+{
+ QMGR_PEER *peer;
+
+ peer = (QMGR_PEER *) mymalloc(sizeof(QMGR_PEER));
+ peer->queue = queue;
+ peer->job = job;
+ QMGR_LIST_APPEND(job->peer_list, peer, peers);
+ htable_enter(job->peer_byname, queue->name, (char *) peer);
+ peer->refcount = 0;
+ QMGR_LIST_INIT(peer->entry_list);
+ return (peer);
+}
+
+/* qmgr_peer_free - release peer structure */
+
+void qmgr_peer_free(QMGR_PEER *peer)
+{
+ char *myname = "qmgr_peer_free";
+ QMGR_JOB *job = peer->job;
+ QMGR_QUEUE *queue = peer->queue;
+
+ /*
+ * Sanity checks. It is an error to delete a referenced peer structure.
+ */
+ if (peer->refcount != 0)
+ msg_panic("%s: refcount: %d", myname, peer->refcount);
+ if (peer->entry_list.next != 0)
+ msg_panic("%s: entry list not empty: %s", myname, queue->name);
+
+ QMGR_LIST_UNLINK(job->peer_list, QMGR_PEER *, peer, peers);
+ htable_delete(job->peer_byname, queue->name, (void (*) (char *)) 0);
+ myfree((char *) peer);
+}
+
+/* qmgr_peer_find - lookup peer associated with given job and queue */
+
+QMGR_PEER *qmgr_peer_find(QMGR_JOB *job, QMGR_QUEUE *queue)
+{
+ return ((QMGR_PEER *) htable_find(job->peer_byname, queue->name));
+}
+
+/* qmgr_peer_select - select next peer suitable for delivery within given job */
+
+QMGR_PEER *qmgr_peer_select(QMGR_JOB *job)
+{
+ QMGR_PEER *peer;
+ QMGR_QUEUE *queue;
+
+ /*
+ * If we find a suitable site, rotate the list to enforce round-robin
+ * selection. See similar selection code in qmgr_transport_select().
+ */
+ for (peer = job->peer_list.next; peer; peer = peer->peers.next) {
+ queue = peer->queue;
+ if (queue->window > queue->busy_refcount && peer->entry_list.next != 0) {
+ QMGR_LIST_ROTATE(job->peer_list, peer, peers);
+ if (msg_verbose)
+ msg_info("qmgr_peer_select: %s %s %s (%d of %d)",
+ job->message->queue_id, queue->transport->name, queue->name,
+ queue->busy_refcount + 1, queue->window);
+ return (peer);
+ }
+ }
+ return (0);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_queue.c b/gnu/dist/postfix/src/nqmgr/qmgr_queue.c
new file mode 100644
index 00000000000..d4aced58e23
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_queue.c
@@ -0,0 +1,251 @@
+/*++
+/* NAME
+/* qmgr_queue 3
+/* SUMMARY
+/* per-destination queues
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* int qmgr_queue_count;
+/*
+/* QMGR_QUEUE *qmgr_queue_create(transport, site)
+/* QMGR_TRANSPORT *transport;
+/* const char *site;
+/*
+/* void qmgr_queue_done(queue)
+/* QMGR_QUEUE *queue;
+/*
+/* QMGR_QUEUE *qmgr_queue_find(transport, site)
+/* QMGR_TRANSPORT *transport;
+/* const char *site;
+/*
+/* void qmgr_queue_throttle(queue, reason)
+/* QMGR_QUEUE *queue;
+/* const char *reason;
+/*
+/* void qmgr_queue_unthrottle(queue)
+/* QMGR_QUEUE *queue;
+/* DESCRIPTION
+/* These routines add/delete/manipulate per-destination queues.
+/* Each queue corresponds to a specific transport and destination.
+/* Each queue has a `todo' list of delivery requests for that
+/* destination, and a `busy' list of delivery requests in progress.
+/*
+/* qmgr_queue_count is a global counter for the total number
+/* of in-core queue structures.
+/*
+/* qmgr_queue_create() creates an empty queue for the named
+/* transport and destination. The queue is given an initial
+/* concurrency limit as specified with the
+/* \fIinitial_destination_concurrency\fR configuration parameter,
+/* provided that it does not exceed the transport-specific
+/* concurrency limit.
+/*
+/* qmgr_queue_done() disposes of a per-destination queue after all
+/* its entries have been taken care of. It is an error to dispose
+/* of a dead queue.
+/*
+/* qmgr_queue_find() looks up the queue for the named destination
+/* for the named transport. A null result means that the queue
+/* was not found.
+/*
+/* qmgr_queue_throttle() handles a delivery error, and decrements the
+/* concurrency limit for the destination. When the concurrency limit
+/* for a destination becomes zero, qmgr_queue_throttle() starts a timer
+/* to re-enable delivery to the destination after a configurable delay.
+/*
+/* qmgr_queue_unthrottle() undoes qmgr_queue_throttle()'s effects.
+/* The concurrency limit for the destination is incremented,
+/* provided that it does not exceed the destination concurrency
+/* limit specified for the transport. This routine implements
+/* "slow open" mode, and eliminates the "thundering herd" problem.
+/* DIAGNOSTICS
+/* Panic: consistency check failure.
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <recipient_list.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+int qmgr_queue_count;
+
+/* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */
+
+static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
+{
+ QMGR_QUEUE *queue = (QMGR_QUEUE *) context;
+
+ /*
+ * This routine runs when a wakeup timer goes off; it does not run in the
+ * context of some queue manipulation. Therefore, it is safe to discard
+ * this in-core queue when it is empty and when this site is not dead.
+ */
+ qmgr_queue_unthrottle(queue);
+ if (queue->window > 0 && queue->todo.next == 0 && queue->busy.next == 0)
+ qmgr_queue_done(queue);
+}
+
+/* qmgr_queue_unthrottle - give this destination another chance */
+
+void qmgr_queue_unthrottle(QMGR_QUEUE *queue)
+{
+ char *myname = "qmgr_queue_unthrottle";
+ QMGR_TRANSPORT *transport = queue->transport;
+
+ if (msg_verbose)
+ msg_info("%s: queue %s", myname, queue->name);
+
+ /*
+ * Special case when this site was dead.
+ */
+ if (queue->window == 0) {
+ event_cancel_timer(qmgr_queue_unthrottle_wrapper, (char *) queue);
+ if (queue->reason == 0)
+ msg_panic("%s: queue %s: window 0 reason 0", myname, queue->name);
+ myfree(queue->reason);
+ queue->reason = 0;
+ queue->window = transport->init_dest_concurrency;
+ return;
+ }
+
+ /*
+ * Increase the destination's concurrency limit until we reach the
+ * transport's concurrency limit. Allow for a margin the size of the
+ * initial destination concurrency, so that we're not too gentle.
+ */
+ if (transport->dest_concurrency_limit == 0
+ || transport->dest_concurrency_limit > queue->window)
+ if (queue->window < queue->busy_refcount + transport->init_dest_concurrency)
+ queue->window++;
+}
+
+/* qmgr_queue_throttle - handle destination delivery failure */
+
+void qmgr_queue_throttle(QMGR_QUEUE *queue, const char *reason)
+{
+ char *myname = "qmgr_queue_throttle";
+
+ /*
+ * Sanity checks.
+ */
+ if (queue->reason)
+ msg_panic("%s: queue %s: spurious reason %s",
+ myname, queue->name, queue->reason);
+ if (msg_verbose)
+ msg_info("%s: queue %s: %s", myname, queue->name, reason);
+
+ /*
+ * Decrease the destination's concurrency limit until we reach zero, at
+ * which point the destination is declared dead. Decrease the concurrency
+ * limit by one, instead of using actual concurrency - 1, to avoid
+ * declaring a host dead after just one single delivery failure.
+ */
+ if (queue->window > 0)
+ queue->window--;
+
+ /*
+ * Special case for a site that just was declared dead.
+ */
+ if (queue->window == 0) {
+ queue->reason = mystrdup(reason);
+ event_request_timer(qmgr_queue_unthrottle_wrapper,
+ (char *) queue, var_min_backoff_time);
+ }
+}
+
+/* qmgr_queue_done - delete in-core queue for site */
+
+void qmgr_queue_done(QMGR_QUEUE *queue)
+{
+ char *myname = "qmgr_queue_done";
+ QMGR_TRANSPORT *transport = queue->transport;
+
+ /*
+ * Sanity checks. It is an error to delete an in-core queue with pending
+ * messages or timers.
+ */
+ if (queue->busy_refcount != 0 || queue->todo_refcount != 0)
+ msg_panic("%s: refcount: %d", myname,
+ queue->busy_refcount + queue->todo_refcount);
+ if (queue->todo.next || queue->busy.next)
+ msg_panic("%s: queue not empty: %s", myname, queue->name);
+ if (queue->window <= 0)
+ msg_panic("%s: window %d", myname, queue->window);
+ if (queue->reason)
+ msg_panic("%s: queue %s: spurious reason %s",
+ myname, queue->name, queue->reason);
+
+ /*
+ * Clean up this in-core queue.
+ */
+ QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue, peers);
+ htable_delete(transport->queue_byname, queue->name, (void (*) (char *)) 0);
+ myfree(queue->name);
+ qmgr_queue_count--;
+ myfree((char *) queue);
+}
+
+/* qmgr_queue_create - create in-core queue for site */
+
+QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
+{
+ QMGR_QUEUE *queue;
+
+ /*
+ * If possible, choose an initial concurrency of > 1 so that one bad
+ * message or one bad network won't slow us down unnecessarily.
+ */
+
+ queue = (QMGR_QUEUE *) mymalloc(sizeof(QMGR_QUEUE));
+ qmgr_queue_count++;
+ queue->name = mystrdup(site);
+ queue->todo_refcount = 0;
+ queue->busy_refcount = 0;
+ queue->transport = transport;
+ queue->window = transport->init_dest_concurrency;
+ QMGR_LIST_INIT(queue->todo);
+ QMGR_LIST_INIT(queue->busy);
+ queue->reason = 0;
+ queue->blocker_tag = 0;
+ QMGR_LIST_APPEND(transport->queue_list, queue, peers);
+ htable_enter(transport->queue_byname, site, (char *) queue);
+ return (queue);
+}
+
+/* qmgr_queue_find - find in-core queue for site */
+
+QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *transport, const char *site)
+{
+ return ((QMGR_QUEUE *) htable_find(transport->queue_byname, site));
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_rcpt_list.c b/gnu/dist/postfix/src/nqmgr/qmgr_rcpt_list.c
new file mode 100644
index 00000000000..2bb292e016e
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_rcpt_list.c
@@ -0,0 +1,96 @@
+/*++
+/* NAME
+/* qmgr_rcpt_list 3
+/* SUMMARY
+/* in-core recipient structures
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* void qmgr_rcpt_list_init(list)
+/* QMGR_RCPT_LIST *list;
+/*
+/* void qmgr_rcpt_list_add(list, offset, recipient)
+/* QMGR_RCPT_LIST *list;
+/* long offset;
+/* const char *recipient;
+/*
+/* void qmgr_rcpt_list_free(list)
+/* QMGR_RCPT_LIST *list;
+/* DESCRIPTION
+/* This module maintains lists of queue manager recipient structures.
+/* These structures are extended versions of the structures maintained
+/* by the recipient_list(3) module. The extension is that the queue
+/* manager version of a recipient can have a reference to a queue
+/* structure.
+/*
+/* qmgr_rcpt_list_init() creates an empty recipient structure list.
+/* The list argument is initialized such that it can be given to
+/* qmgr_rcpt_list_add() and qmgr_rcpt_list_free().
+/*
+/* qmgr_rcpt_list_add() adds a recipient to the specified list.
+/* The recipient name is copied.
+/*
+/* qmgr_rcpt_list_free() releases memory for the specified list
+/* of recipient structures.
+/* SEE ALSO
+/* qmgr_rcpt_list(3h) data structure
+/* recipient_list(3) same code, different data structure.
+/* DIAGNOSTICS
+/* Fatal errors: memory allocation.
+/* 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>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_rcpt_list_init - initialize */
+
+void qmgr_rcpt_list_init(QMGR_RCPT_LIST *list)
+{
+ list->avail = 1;
+ list->len = 0;
+ list->info = (QMGR_RCPT *) mymalloc(sizeof(QMGR_RCPT));
+}
+
+/* qmgr_rcpt_list_add - add rcpt to list */
+
+void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset, const char *rcpt)
+{
+ if (list->len >= list->avail) {
+ list->avail *= 2;
+ list->info = (QMGR_RCPT *)
+ myrealloc((char *) list->info, list->avail * sizeof(QMGR_RCPT));
+ }
+ list->info[list->len].address = mystrdup(rcpt);
+ list->info[list->len].offset = offset;
+ list->info[list->len].queue = 0;
+ list->len++;
+}
+
+/* qmgr_rcpt_list_free - release memory for in-core recipient structure */
+
+void qmgr_rcpt_list_free(QMGR_RCPT_LIST *list)
+{
+ QMGR_RCPT *rcpt;
+
+ for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
+ myfree(rcpt->address);
+ myfree((char *) list->info);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_scan.c b/gnu/dist/postfix/src/nqmgr/qmgr_scan.c
new file mode 100644
index 00000000000..e0d9fc079ab
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_scan.c
@@ -0,0 +1,159 @@
+/*++
+/* NAME
+/* qmgr_scan 3
+/* SUMMARY
+/* queue scanning
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* QMGR_SCAN *qmgr_scan_create(queue_name)
+/* const char *queue_name;
+/*
+/* char *qmgr_scan_next(scan_info)
+/* QMGR_SCAN *scan_info;
+/*
+/* void qmgr_scan_request(scan_info, flags)
+/* QMGR_SCAN *scan_info;
+/* int flags;
+/* DESCRIPTION
+/* This module implements queue scans. A queue scan always runs
+/* to completion, so that all files get a fair chance. The caller
+/* can request that a queue scan be restarted once it completes.
+/*
+/* qmgr_scan_create() creates a context for scanning the named queue,
+/* but does not start a queue scan.
+/*
+/* qmgr_scan_next() returns the base name of the next queue file.
+/* A null pointer means that no file was found. qmgr_scan_next()
+/* automagically restarts a queue scan when a scan request had
+/* arrived while the scan was in progress.
+/*
+/* qmgr_scan_request() records a request for the next queue scan. The
+/* flags argument is the bit-wise OR of zero or more of the following,
+/* unrecognized flags being ignored:
+/* .IP QMGR_FLUSH_DEAD
+/* Forget state information about dead hosts or transports. This
+/* request takes effect upon the next queue scan.
+/* .IP QMGR_SCAN_ALL
+/* Ignore queue file time stamps.
+/* This flag is passed on to the qmgr_active_feed() routine.
+/* .IP QMGR_SCAN_START
+/* Start a queue scan when none is in progress, or restart the
+/* current scan upon completion.
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/* Panic: interface violations, internal consistency errors.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <scan_dir.h>
+
+/* Global library. */
+
+#include <mail_scan_dir.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+/* qmgr_scan_start - start queue scan */
+
+static void qmgr_scan_start(QMGR_SCAN *scan_info)
+{
+ char *myname = "qmgr_scan_start";
+
+ /*
+ * Sanity check.
+ */
+ if (scan_info->handle)
+ msg_panic("%s: %s queue scan in progress",
+ myname, scan_info->queue);
+
+ /*
+ * Give the poor tester a clue.
+ */
+ if (msg_verbose)
+ msg_info("%s: %sstart %s queue scan",
+ myname,
+ scan_info->nflags & QMGR_SCAN_START ? "re" : "",
+ scan_info->queue);
+
+ /*
+ * Optionally forget all dead host information.
+ */
+ if (scan_info->nflags & QMGR_FLUSH_DEAD)
+ qmgr_enable_all();
+
+ /*
+ * Start or restart the scan.
+ */
+ scan_info->flags = scan_info->nflags;
+ scan_info->nflags = 0;
+ scan_info->handle = scan_dir_open(scan_info->queue);
+}
+
+/* qmgr_scan_request - request for future scan */
+
+void qmgr_scan_request(QMGR_SCAN *scan_info, int flags)
+{
+
+ /*
+ * If a scan is in progress, just record the request.
+ */
+ scan_info->nflags |= flags;
+ if (scan_info->handle == 0 && (flags & QMGR_SCAN_START) != 0) {
+ scan_info->nflags &= ~QMGR_SCAN_START;
+ qmgr_scan_start(scan_info);
+ }
+}
+
+/* qmgr_scan_next - look for next queue file */
+
+char *qmgr_scan_next(QMGR_SCAN *scan_info)
+{
+ char *path = 0;
+
+ /*
+ * Restart the scan if we reach the end and a queue scan request has
+ * arrived in the mean time.
+ */
+ if (scan_info->handle && (path = mail_scan_dir_next(scan_info->handle)) == 0) {
+ scan_info->handle = scan_dir_close(scan_info->handle);
+ if (msg_verbose && (scan_info->nflags & QMGR_SCAN_START) == 0)
+ msg_info("done %s queue scan", scan_info->queue);
+ }
+ if (!scan_info->handle && (scan_info->nflags & QMGR_SCAN_START)) {
+ qmgr_scan_start(scan_info);
+ path = mail_scan_dir_next(scan_info->handle);
+ }
+ return (path);
+}
+
+/* qmgr_scan_create - create queue scan context */
+
+QMGR_SCAN *qmgr_scan_create(const char *queue)
+{
+ QMGR_SCAN *scan_info;
+
+ scan_info = (QMGR_SCAN *) mymalloc(sizeof(*scan_info));
+ scan_info->queue = mystrdup(queue);
+ scan_info->flags = scan_info->nflags = 0;
+ scan_info->handle = 0;
+ return (scan_info);
+}
diff --git a/gnu/dist/postfix/src/nqmgr/qmgr_transport.c b/gnu/dist/postfix/src/nqmgr/qmgr_transport.c
new file mode 100644
index 00000000000..204880962b3
--- /dev/null
+++ b/gnu/dist/postfix/src/nqmgr/qmgr_transport.c
@@ -0,0 +1,381 @@
+/*++
+/* NAME
+/* qmgr_transport 3
+/* SUMMARY
+/* per-transport data structures
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* QMGR_TRANSPORT *qmgr_transport_create(name)
+/* const char *name;
+/*
+/* QMGR_TRANSPORT *qmgr_transport_find(name)
+/* const char *name;
+/*
+/* QMGR_TRANSPORT *qmgr_transport_select()
+/*
+/* void qmgr_transport_alloc(transport, notify)
+/* QMGR_TRANSPORT *transport;
+/* void (*notify)(QMGR_TRANSPORT *transport, VSTREAM *fp);
+/*
+/* void qmgr_transport_throttle(transport, reason)
+/* QMGR_TRANSPORT *transport;
+/* const char *reason;
+/*
+/* void qmgr_transport_unthrottle(transport)
+/* QMGR_TRANSPORT *transport;
+/* DESCRIPTION
+/* This module organizes the world by message transport type.
+/* Each transport can have zero or more destination queues
+/* associated with it.
+/*
+/* qmgr_transport_create() instantiates a data structure for the
+/* named transport type.
+/*
+/* qmgr_transport_find() looks up an existing message transport
+/* data structure.
+/*
+/* qmgr_transport_select() attempts to find a transport that
+/* has messages pending delivery. This routine implements
+/* round-robin search among transports.
+/*
+/* qmgr_transport_alloc() allocates a delivery process for the
+/* specified transport type. Allocation is performed asynchronously.
+/* When a process becomes available, the application callback routine
+/* is invoked with as arguments the transport and a stream that
+/* is connected to a delivery process. It is an error to call
+/* qmgr_transport_alloc() while delivery process allocation for
+/* the same transport is in progress.
+/*
+/* qmgr_transport_throttle blocks further allocation of delivery
+/* processes for the named transport. Attempts to throttle a
+/* throttled transport are ignored.
+/*
+/* qmgr_transport_unthrottle() undoes qmgr_transport_throttle().
+/* Attempts to unthrottle a non-throttled transport are ignored.
+/* DIAGNOSTICS
+/* Panic: consistency check failure. Fatal: out of memory.
+/* 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
+/*
+/* Scheduler enhancements:
+/* Patrik Rak
+/* Modra 6
+/* 155 00, Prague, Czech Republic
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <events.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <recipient_list.h>
+#include <mail_conf.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+HTABLE *qmgr_transport_byname; /* transport by name */
+QMGR_TRANSPORT_LIST qmgr_transport_list;/* transports, round robin */
+
+ /*
+ * A local structure to remember a delivery process allocation request.
+ */
+typedef struct QMGR_TRANSPORT_ALLOC QMGR_TRANSPORT_ALLOC;
+
+struct QMGR_TRANSPORT_ALLOC {
+ QMGR_TRANSPORT *transport; /* transport context */
+ VSTREAM *stream; /* delivery service stream */
+ QMGR_TRANSPORT_ALLOC_NOTIFY notify; /* application call-back routine */
+};
+
+/* qmgr_transport_unthrottle_wrapper - in case (char *) != (struct *) */
+
+static void qmgr_transport_unthrottle_wrapper(int unused_event, char *context)
+{
+ qmgr_transport_unthrottle((QMGR_TRANSPORT *) context);
+}
+
+/* qmgr_transport_unthrottle - open the throttle */
+
+void qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
+{
+ char *myname = "qmgr_transport_unthrottle";
+
+ /*
+ * This routine runs after expiration of the timer set by
+ * qmgr_transport_throttle(), or whenever a delivery transport has been
+ * used without malfunction. In either case, we enable delivery again if
+ * the transport was blocked, otherwise the request is ignored.
+ */
+ if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
+ if (msg_verbose)
+ msg_info("%s: transport %s", myname, transport->name);
+ transport->flags &= ~QMGR_TRANSPORT_STAT_DEAD;
+ if (transport->reason == 0)
+ msg_panic("%s: transport %s: null reason", myname, transport->name);
+ myfree(transport->reason);
+ transport->reason = 0;
+ event_cancel_timer(qmgr_transport_unthrottle_wrapper,
+ (char *) transport);
+ }
+}
+
+/* qmgr_transport_throttle - disable delivery process allocation */
+
+void qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *reason)
+{
+ char *myname = "qmgr_transport_throttle";
+
+ /*
+ * We are unable to connect to a deliver process for this type of message
+ * transport. Instead of hosing the system by retrying in a tight loop,
+ * back off and disable this transport type for a while.
+ */
+ if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) == 0) {
+ if (msg_verbose)
+ msg_info("%s: transport %s: reason: %s",
+ myname, transport->name, reason);
+ transport->flags |= QMGR_TRANSPORT_STAT_DEAD;
+ if (transport->reason)
+ msg_panic("%s: transport %s: spurious reason: %s",
+ myname, transport->name, transport->reason);
+ transport->reason = mystrdup(reason);
+ event_request_timer(qmgr_transport_unthrottle_wrapper,
+ (char *) transport, var_transport_retry_time);
+ }
+}
+
+/* qmgr_transport_abort - transport connect watchdog */
+
+static void qmgr_transport_abort(int unused_event, char *context)
+{
+ QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context;
+
+ msg_fatal("timeout connecting to transport: %s", alloc->transport->name);
+}
+
+/* qmgr_transport_event - delivery process availability notice */
+
+static void qmgr_transport_event(int unused_event, char *context)
+{
+ QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context;
+
+ /*
+ * This routine notifies the application when the request given to
+ * qmgr_transport_alloc() completes.
+ */
+ if (msg_verbose)
+ msg_info("transport_event: %s", alloc->transport->name);
+
+ /*
+ * Connection request completed. Stop the watchdog timer.
+ */
+ event_cancel_timer(qmgr_transport_abort, context);
+
+ /*
+ * Disable further read events that end up calling this function.
+ */
+ event_disable_readwrite(vstream_fileno(alloc->stream));
+ alloc->transport->flags &= ~QMGR_TRANSPORT_STAT_BUSY;
+
+ /*
+ * Notify the requestor.
+ */
+ alloc->notify(alloc->transport, alloc->stream);
+ myfree((char *) alloc);
+}
+
+#ifdef UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT
+
+/* qmgr_transport_connect - handle connection request completion */
+
+static void qmgr_transport_connect(int unused_event, char *context)
+{
+ QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context;
+
+ /*
+ * This code is necessary for some versions of LINUX, where connect(2)
+ * blocks until the application performs an accept(2). Reportedly, the
+ * same can happen on Solaris 2.5.1.
+ */
+ event_disable_readwrite(vstream_fileno(alloc->stream));
+ non_blocking(vstream_fileno(alloc->stream), BLOCKING);
+ event_enable_read(vstream_fileno(alloc->stream),
+ qmgr_transport_event, (char *) alloc);
+}
+
+#endif
+
+/* qmgr_transport_select - select transport for allocation */
+
+QMGR_TRANSPORT *qmgr_transport_select(void)
+{
+ QMGR_TRANSPORT *xport;
+ QMGR_QUEUE *queue;
+
+ /*
+ * If we find a suitable transport, rotate the list of transports to
+ * effectuate round-robin selection. See similar selection code in
+ * qmgr_peer_select().
+ */
+#define STAY_AWAY (QMGR_TRANSPORT_STAT_BUSY | QMGR_TRANSPORT_STAT_DEAD)
+
+ for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next) {
+ if (xport->flags & STAY_AWAY)
+ continue;
+ for (queue = xport->queue_list.next; queue; queue = queue->peers.next) {
+ if (queue->window > queue->busy_refcount && queue->todo.next != 0) {
+ QMGR_LIST_ROTATE(qmgr_transport_list, xport, peers);
+ if (msg_verbose)
+ msg_info("qmgr_transport_select: %s", xport->name);
+ return (xport);
+ }
+ }
+ }
+ return (0);
+}
+
+/* qmgr_transport_alloc - allocate delivery process */
+
+void qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOTIFY notify)
+{
+ QMGR_TRANSPORT_ALLOC *alloc;
+ VSTREAM *stream;
+
+ /*
+ * Sanity checks.
+ */
+ if (transport->flags & QMGR_TRANSPORT_STAT_DEAD)
+ msg_panic("qmgr_transport: dead transport: %s", transport->name);
+ if (transport->flags & QMGR_TRANSPORT_STAT_BUSY)
+ msg_panic("qmgr_transport: nested allocation: %s", transport->name);
+
+ /*
+ * Connect to the well-known port for this delivery service, and wake up
+ * when a process announces its availability. In the mean time, block out
+ * other delivery process allocation attempts for this transport. In case
+ * of problems, back off. Do not hose the system when it is in trouble
+ * already.
+ */
+#ifdef UNIX_DOMAIN_CONNECT_BLOCKS_FOR_ACCEPT
+#define BLOCK_MODE NON_BLOCKING
+#define ENABLE_EVENTS event_enable_write
+#define EVENT_HANDLER qmgr_transport_connect
+#else
+#define BLOCK_MODE BLOCKING
+#define ENABLE_EVENTS event_enable_read
+#define EVENT_HANDLER qmgr_transport_event
+#endif
+
+ if ((stream = mail_connect(MAIL_CLASS_PRIVATE, transport->name, BLOCK_MODE)) == 0) {
+ msg_warn("connect to transport %s: %m", transport->name);
+ qmgr_transport_throttle(transport, "transport is unavailable");
+ return;
+ }
+ alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc));
+ alloc->stream = stream;
+ alloc->transport = transport;
+ alloc->notify = notify;
+ transport->flags |= QMGR_TRANSPORT_STAT_BUSY;
+ ENABLE_EVENTS(vstream_fileno(alloc->stream), EVENT_HANDLER, (char *) alloc);
+
+ /*
+ * Guard against broken systems.
+ */
+ event_request_timer(qmgr_transport_abort, (char *) alloc,
+ var_daemon_timeout);
+}
+
+/* qmgr_transport_create - create transport instance */
+
+QMGR_TRANSPORT *qmgr_transport_create(const char *name)
+{
+ QMGR_TRANSPORT *transport;
+
+ if (htable_find(qmgr_transport_byname, name) != 0)
+ msg_panic("qmgr_transport_create: transport exists: %s", name);
+ transport = (QMGR_TRANSPORT *) mymalloc(sizeof(QMGR_TRANSPORT));
+ transport->flags = 0;
+ transport->name = mystrdup(name);
+
+ /*
+ * Use global configuration settings or transport-specific settings.
+ */
+ transport->dest_concurrency_limit =
+ get_mail_conf_int2(name, _DEST_CON_LIMIT,
+ var_dest_con_limit, 0, 0);
+ transport->recipient_limit =
+ get_mail_conf_int2(name, _DEST_RCPT_LIMIT,
+ var_dest_rcpt_limit, 0, 0);
+
+ if (transport->dest_concurrency_limit == 0
+ || transport->dest_concurrency_limit >= var_init_dest_concurrency)
+ transport->init_dest_concurrency = var_init_dest_concurrency;
+ else
+ transport->init_dest_concurrency = transport->dest_concurrency_limit;
+
+ transport->slot_cost = get_mail_conf_int2(name, _DELIVERY_SLOT_COST,
+ var_delivery_slot_cost, 0, 0);
+ transport->slot_loan = get_mail_conf_int2(name, _DELIVERY_SLOT_LOAN,
+ var_delivery_slot_loan, 0, 0);
+ transport->slot_loan_factor =
+ 100 - get_mail_conf_int2(name, _DELIVERY_SLOT_DISCOUNT,
+ var_delivery_slot_discount, 0, 100);
+ transport->min_slots = get_mail_conf_int2(name, _MIN_DELIVERY_SLOTS,
+ var_min_delivery_slots, 0, 0);
+ transport->rcpt_unused = get_mail_conf_int2(name, _XPORT_RCPT_LIMIT,
+ var_xport_rcpt_limit, 0, 0);
+ transport->rcpt_per_stack = get_mail_conf_int2(name, _STACK_RCPT_LIMIT,
+ var_stack_rcpt_limit, 0, 0);
+
+ transport->queue_byname = htable_create(0);
+ QMGR_LIST_INIT(transport->queue_list);
+ transport->job_byname = htable_create(0);
+ QMGR_LIST_INIT(transport->job_list);
+ QMGR_LIST_INIT(transport->job_bytime);
+ transport->job_current = 0;
+ transport->job_next_unread = 0;
+ transport->candidate_cache = 0;
+ transport->candidate_cache_current = 0;
+ transport->candidate_cache_time = (time_t) 0;
+ transport->blocker_tag = 1;
+ transport->reason = 0;
+ if (qmgr_transport_byname == 0)
+ qmgr_transport_byname = htable_create(10);
+ htable_enter(qmgr_transport_byname, name, (char *) transport);
+ QMGR_LIST_PREPEND(qmgr_transport_list, transport, peers);
+ if (msg_verbose)
+ msg_info("qmgr_transport_create: %s concurrency %d recipients %d",
+ transport->name, transport->dest_concurrency_limit,
+ transport->recipient_limit);
+ return (transport);
+}
+
+/* qmgr_transport_find - find transport instance */
+
+QMGR_TRANSPORT *qmgr_transport_find(const char *name)
+{
+ return ((QMGR_TRANSPORT *) htable_find(qmgr_transport_byname, name));
+}
diff --git a/gnu/dist/postfix/src/pickup/Makefile.in b/gnu/dist/postfix/src/pickup/Makefile.in
index 19f5688fc74..a9006d5edd3 100644
--- a/gnu/dist/postfix/src/pickup/Makefile.in
+++ b/gnu/dist/postfix/src/pickup/Makefile.in
@@ -60,13 +60,14 @@ pickup.o: ../../include/scan_dir.h
pickup.o: ../../include/vstring.h
pickup.o: ../../include/vbuf.h
pickup.o: ../../include/vstream.h
-pickup.o: ../../include/open_as.h
-pickup.o: ../../include/set_eugid.h
+pickup.o: ../../include/set_ugid.h
+pickup.o: ../../include/safe_open.h
pickup.o: ../../include/mail_queue.h
pickup.o: ../../include/mail_open_ok.h
pickup.o: ../../include/mymalloc.h
pickup.o: ../../include/mail_proto.h
pickup.o: ../../include/iostuff.h
+pickup.o: ../../include/attr.h
pickup.o: ../../include/cleanup_user.h
pickup.o: ../../include/mail_date.h
pickup.o: ../../include/mail_params.h
diff --git a/gnu/dist/postfix/src/pickup/pickup.c b/gnu/dist/postfix/src/pickup/pickup.c
index 38d798e39cc..994a6328f58 100644
--- a/gnu/dist/postfix/src/pickup/pickup.c
+++ b/gnu/dist/postfix/src/pickup/pickup.c
@@ -7,8 +7,8 @@
/* \fBpickup\fR [generic Postfix daemon options]
/* DESCRIPTION
/* The \fBpickup\fR daemon waits for hints that new mail has been
-/* dropped into the world-writable \fBmaildrop\fR directory, and
-/* feeds it into the \fBcleanup\fR(8) daemon.
+/* dropped into the \fBmaildrop\fR directory, and feeds it into the
+/* \fBcleanup\fR(8) daemon.
/* Ill-formatted files are deleted without notifying the originator.
/* This program expects to be run from the \fBmaster\fR(8) process
/* manager.
@@ -58,6 +58,7 @@
/* SEE ALSO
/* cleanup(8) message canonicalization
/* master(8) process manager
+/* sendmail(1), postdrop(8) mail posting agent
/* syslogd(8) system logging
/* LICENSE
/* .ad
@@ -89,8 +90,8 @@
#include <scan_dir.h>
#include <vstring.h>
#include <vstream.h>
-#include <open_as.h>
-#include <set_eugid.h>
+#include <set_ugid.h>
+#include <safe_open.h>
/* Global library. */
@@ -295,7 +296,9 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup,
* bounce, the cleanup service can report only soft errors here.
*/
rec_fputs(cleanup, REC_TYPE_END, "");
- if (mail_scan(cleanup, "%d", &status) != 1)
+ if (attr_scan(cleanup, ATTR_FLAG_MISSING,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) != 1)
return (cleanup_service_error(info, CLEANUP_STAT_WRITE));
/*
@@ -315,8 +318,7 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup,
static int pickup_file(PICKUP_INFO *info)
{
- struct stat st;
- VSTRING *buf;
+ VSTRING *buf = vstring_alloc(100);
int status;
VSTREAM *qfile;
VSTREAM *cleanup;
@@ -329,29 +331,16 @@ static int pickup_file(PICKUP_INFO *info)
* Perhaps we should save "bad" files elsewhere for further inspection.
* XXX How can we delete a file when open() fails with ENOENT?
*/
- fd = open_as(info->path, O_RDONLY | O_NONBLOCK, 0,
- info->st.st_uid, info->st.st_gid);
- if (fd < 0) {
+ qfile = safe_open(info->path, O_RDONLY | O_NONBLOCK, 0,
+ (struct stat *) 0, -1, -1, buf);
+ if (qfile == 0) {
if (errno != ENOENT)
- msg_fatal("open input file %s: %m", info->path);
- msg_warn("open input file %s: %m", info->path);
- return (REMOVE_MESSAGE_FILE);
+ msg_warn("open input file %s: %s", info->path, vstring_str(buf));
+ vstring_free(buf);
+ return (errno == EACCES ? KEEP_MESSAGE_FILE : REMOVE_MESSAGE_FILE);
}
/*
- * Like safe_open(pat, O_RDONLY, 0), but without any link count checks.
- */
- if (fstat(fd, &st) < 0)
- msg_fatal("fstat: %m");
- if (st.st_dev != info->st.st_dev
- || st.st_ino != info->st.st_ino
- || st.st_mode != info->st.st_mode) {
- msg_warn("%s: uid %ld: file has changed", info->path, (long) st.st_uid);
- return (REMOVE_MESSAGE_FILE);
- }
- qfile = vstream_fdopen(fd, O_RDONLY);
-
- /*
* Contact the cleanup service and read the queue ID that it has
* allocated. In case of trouble, request that the cleanup service
* bounces its copy of the message. because the original input file is
@@ -363,10 +352,13 @@ static int pickup_file(PICKUP_INFO *info)
*/
#define PICKUP_CLEANUP_FLAGS (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_FILTER)
- buf = vstring_alloc(100);
- cleanup = mail_connect_wait(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP);
- if (mail_scan(cleanup, "%s", buf) != 1
- || mail_print(cleanup, "%d", PICKUP_CLEANUP_FLAGS) != 0) {
+ cleanup = mail_connect_wait(MAIL_CLASS_PUBLIC, MAIL_SERVICE_CLEANUP);
+ if (attr_scan(cleanup, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, buf,
+ ATTR_TYPE_END) != 1
+ || attr_print(cleanup, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, PICKUP_CLEANUP_FLAGS,
+ ATTR_TYPE_END) != 0) {
status = KEEP_MESSAGE_FILE;
} else {
info->id = mystrdup(vstring_str(buf));
@@ -445,11 +437,16 @@ static void pickup_service(char *unused_buf, int unused_len,
} while (file_count);
}
-/* drop_privileges - drop privileges most of the time */
+/* drop_privileges - drop privileges */
static void drop_privileges(char *unused_name, char **unused_argv)
{
- set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * In case master.cf was not updated for unprivileged service.
+ */
+ if (getuid() != var_owner_uid)
+ set_ugid(var_owner_uid, var_owner_gid);
}
/* main - pass control to the multi-threaded server skeleton */
diff --git a/gnu/dist/postfix/src/pipe/Makefile.in b/gnu/dist/postfix/src/pipe/Makefile.in
index 8575f30fbd4..c46d22ca03d 100644
--- a/gnu/dist/postfix/src/pipe/Makefile.in
+++ b/gnu/dist/postfix/src/pipe/Makefile.in
@@ -82,4 +82,5 @@ pipe.o: ../../include/mail_addr.h
pipe.o: ../../include/canon_addr.h
pipe.o: ../../include/split_addr.h
pipe.o: ../../include/off_cvt.h
+pipe.o: ../../include/quote_822_local.h
pipe.o: ../../include/mail_server.h
diff --git a/gnu/dist/postfix/src/pipe/pipe.c b/gnu/dist/postfix/src/pipe/pipe.c
index 2b1552c60c1..9794c697cfc 100644
--- a/gnu/dist/postfix/src/pipe/pipe.c
+++ b/gnu/dist/postfix/src/pipe/pipe.c
@@ -11,6 +11,10 @@
/* This program expects to be run from the \fBmaster\fR(8) process
/* manager.
/*
+/* Message attributes such as sender address, recipient address and
+/* next-hop host name can be specified as command-line macros that are
+/* expanded before the external command is executed.
+/*
/* The \fBpipe\fR daemon updates queue files and marks recipients
/* as finished, or it informs the queue manager that delivery should
/* be tried again at a later time. Delivery problem reports are sent
@@ -23,10 +27,10 @@
/* fax machines, and so on.
/*
/* To prevent Postfix from sending multiple recipients per delivery
-/* request, specify
+/* request, specify
/*
/* .ti +4
-/* \fItransport\fB_destination_recipient_limit = 1\fR
+/* \fItransport\fB_destination_recipient_limit = 1\fR
/*
/* in the Postfix \fBmain.cf\fR file, where \fItransport\fR
/* is the name in the first column of the Postfix \fBmaster.cf\fR
@@ -36,7 +40,7 @@
/* .fi
/* The external command attributes are given in the \fBmaster.cf\fR
/* file at the end of a service definition. The syntax is as follows:
-/* .IP "\fBflags=BFR.>\fR (optional)"
+/* .IP "\fBflags=BFRhqu.>\fR (optional)"
/* Optional message processing flags. By default, a message is
/* copied unchanged.
/* .RS
@@ -51,6 +55,27 @@
/* .IP \fBR\fR
/* Prepend a \fBReturn-Path:\fR message header with the envelope sender
/* address.
+/* .IP \fBh\fR
+/* Fold the command-line \fB$recipient\fR domain name and \fB$nexthop\fR
+/* host name to lower case.
+/* This is recommended for delivery via \fBUUCP\fR.
+/* .IP \fBq\fR
+/* Quote white space and other special characters in the command-line
+/* \fB$sender\fR and \fB$recipient\fR address localparts (text to the
+/* left of the right-most \fB@\fR character), according to an 8-bit
+/* transparent version of RFC 822.
+/* This is recommended for delivery via \fBUUCP\fR or \fBBSMTP\fR.
+/* .sp
+/* The result is compatible with the address parsing of command-line
+/* recipients by the Postfix \fBsendmail\fR mail submission command.
+/* .sp
+/* The \fBq\fR flag affects only entire addresses, not the partial
+/* address information from the \fB$user\fR, \fB$extension\fR or
+/* \fB$mailbox\fR command-line macros.
+/* .IP \fBu\fR
+/* Fold the command-line \fB$recipient\fR address localpart (text to
+/* the left of the right-most \fB@\fR character) to lower case.
+/* This is recommended for delivery via \fBUUCP\fR.
/* .IP \fB.\fR
/* Prepend \fB.\fR to lines starting with "\fB.\fR". This is needed
/* by, for example, \fBBSMTP\fR software.
@@ -91,6 +116,8 @@
/* .sp
/* A command-line argument that contains \fB${\fBextension\fR}\fR expands
/* into as many command-line arguments as there are recipients.
+/* .sp
+/* This information is modified by the \fBu\fR flag for case folding.
/* .IP \fB${\fBmailbox\fR}\fR
/* This macro expands to the complete local part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the mailbox is
@@ -98,15 +125,24 @@
/* .sp
/* A command-line argument that contains \fB${\fBmailbox\fR}\fR
/* expands into as many command-line arguments as there are recipients.
+/* .sp
+/* This information is modified by the \fBu\fR flag for case folding.
/* .IP \fB${\fBnexthop\fR}\fR
/* This macro expands to the next-hop hostname.
+/* .sp
+/* This information is modified by the \fBh\fR flag for case folding.
/* .IP \fB${\fBrecipient\fR}\fR
/* This macro expands to the complete recipient address.
/* .sp
/* A command-line argument that contains \fB${\fBrecipient\fR}\fR
/* expands into as many command-line arguments as there are recipients.
+/* .sp
+/* This information is modified by the \fBhqu\fR flags for quoting
+/* and case folding.
/* .IP \fB${\fBsender\fR}\fR
/* This macro expands to the envelope sender address.
+/* .sp
+/* This information is modified by the \fBq\fR flag for quoting.
/* .IP \fB${\fBsize\fR}\fR
/* This macro expands to Postfix's idea of the message size, which
/* is an approximation of the size of the message as delivered.
@@ -117,6 +153,8 @@
/* .sp
/* A command-line argument that contains \fB${\fBuser\fR}\fR expands
/* into as many command-line arguments as there are recipients.
+/* .sp
+/* This information is modified by the \fBu\fR flag for case folding.
/* .RE
/* .PP
/* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
@@ -195,6 +233,7 @@
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
+#include <ctype.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
@@ -231,6 +270,7 @@
#include <canon_addr.h>
#include <split_addr.h>
#include <off_cvt.h>
+#include <quote_822_local.h>
/* Single server skeleton. */
@@ -262,6 +302,16 @@
#define PIPE_FLAG_SIZE (1<<4)
/*
+ * Additional flags. These are colocated with mail_copy() flags. Allow some
+ * space for extension of the mail_copy() interface.
+ */
+#define PIPE_OPT_FOLD_USER (1<<16)
+#define PIPE_OPT_FOLD_HOST (1<<17)
+#define PIPE_OPT_QUOTE_LOCAL (1<<18)
+
+#define PIPE_OPT_FOLD_FLAGS (PIPE_OPT_FOLD_USER | PIPE_OPT_FOLD_HOST)
+
+ /*
* Tunable parameters. Values are taken from the config file, after
* prepending the service name to _name, and so on.
*/
@@ -291,6 +341,11 @@ typedef struct {
off_t size_limit; /* max size in bytes we will accept */
} PIPE_ATTR;
+ /*
+ * Silly little macros.
+ */
+#define STR vstring_str
+
/* parse_callback - callback for mac_parse() */
static int parse_callback(int type, VSTRING *buf, char *context)
@@ -313,9 +368,44 @@ static int parse_callback(int type, VSTRING *buf, char *context)
return (0);
}
+/* morph_recipient - morph a recipient address */
+
+static void morph_recipient(VSTRING *buf, const char *address, int flags)
+{
+ char *cp;
+
+ /*
+ * Quote the recipient address as appropriate.
+ */
+ if (flags & PIPE_OPT_QUOTE_LOCAL)
+ quote_822_local(buf, address);
+ else
+ vstring_strcpy(buf, address);
+
+ /*
+ * Fold the recipient address as appropriate.
+ */
+ switch (flags & PIPE_OPT_FOLD_FLAGS) {
+ case PIPE_OPT_FOLD_HOST:
+ if ((cp = strrchr(STR(buf), '@')) != 0)
+ lowercase(cp + 1);
+ break;
+ case PIPE_OPT_FOLD_USER:
+ if ((cp = strrchr(STR(buf), '@')) != 0) {
+ *cp = 0;
+ lowercase(STR(buf));
+ *cp = '@';
+ break;
+ }
+ case PIPE_OPT_FOLD_USER | PIPE_OPT_FOLD_HOST:
+ lowercase(STR(buf));
+ break;
+ }
+}
+
/* expand_argv - expand macros in the argument vector */
-static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
+static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list, int flags)
{
VSTRING *buf = vstring_alloc(100);
ARGV *result;
@@ -338,7 +428,6 @@ static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
* would screw up mail addresses that contain $ characters.
*/
#define NO 0
-#define STR vstring_str
result = argv_alloc(1);
for (cpp = argv; *cpp; cpp++) {
@@ -353,8 +442,8 @@ static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
* This argument contains $recipient.
*/
if (expand_flag & PIPE_FLAG_RCPT) {
- dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT,
- rcpt_list->info[i].address);
+ morph_recipient(buf, rcpt_list->info[i].address, flags);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, STR(buf));
}
/*
@@ -370,7 +459,8 @@ static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
* expansions of this specific command-line argument.
*/
if (expand_flag & PIPE_FLAG_USER) {
- vstring_strcpy(buf, rcpt_list->info[i].address);
+ morph_recipient(buf, rcpt_list->info[i].address,
+ flags & PIPE_OPT_FOLD_FLAGS);
if (split_at_right(STR(buf), '@') == 0)
msg_warn("no @ in recipient address: %s",
rcpt_list->info[i].address);
@@ -378,7 +468,6 @@ static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
split_addr(STR(buf), *var_rcpt_delim);
if (*STR(buf) == 0)
continue;
- lowercase(STR(buf));
dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
}
@@ -388,15 +477,14 @@ static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
* delimiter and the rightmost @. The extension may be blank.
*/
if (expand_flag & PIPE_FLAG_EXTENSION) {
- vstring_strcpy(buf, rcpt_list->info[i].address);
+ morph_recipient(buf, rcpt_list->info[i].address,
+ flags & PIPE_OPT_FOLD_FLAGS);
if (split_at_right(STR(buf), '@') == 0)
msg_warn("no @ in recipient address: %s",
rcpt_list->info[i].address);
if (*var_rcpt_delim == 0
|| (ext = split_addr(STR(buf), *var_rcpt_delim)) == 0)
ext = ""; /* insert null arg */
- else
- lowercase(ext);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext);
}
@@ -405,11 +493,11 @@ static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list)
* anything to the left of the rightmost @.
*/
if (expand_flag & PIPE_FLAG_MAILBOX) {
- vstring_strcpy(buf, rcpt_list->info[i].address);
+ morph_recipient(buf, rcpt_list->info[i].address,
+ flags & PIPE_OPT_FOLD_FLAGS);
if (split_at_right(STR(buf), '@') == 0)
msg_warn("no @ in recipient address: %s",
rcpt_list->info[i].address);
- lowercase(STR(buf));
dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
}
@@ -492,6 +580,15 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv)
case 'R':
attr->flags |= MAIL_COPY_RETURN_PATH;
break;
+ case 'h':
+ attr->flags |= PIPE_OPT_FOLD_HOST;
+ break;
+ case 'q':
+ attr->flags |= PIPE_OPT_QUOTE_LOCAL;
+ break;
+ case 'u':
+ attr->flags |= PIPE_OPT_FOLD_USER;
+ break;
default:
msg_fatal("unknown flag: %c (ignored)", *cp);
break;
@@ -620,6 +717,9 @@ static int eval_command_status(int command_status, char *service,
service, request->arrival_time, "%s", why);
}
break;
+ case PIPE_STAT_CORRUPT:
+ result |= DEL_STAT_DEFER;
+ break;
default:
msg_panic("eval_command_status: bad status %d", command_status);
/* NOTREACHED */
@@ -704,13 +804,22 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0)
msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp));
- dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender);
- dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
buf = vstring_alloc(10);
+ if (attr.flags & PIPE_OPT_QUOTE_LOCAL) {
+ quote_822_local(buf, request->sender);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf));
+ } else
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender);
+ if (attr.flags & PIPE_OPT_FOLD_HOST) {
+ vstring_strcpy(buf, request->nexthop);
+ lowercase(STR(buf));
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, STR(buf));
+ } else
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
vstring_sprintf(buf, "%ld", (long) request->data_size);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
vstring_free(buf);
- expanded_argv = expand_argv(attr.command, rcpt_list);
+ expanded_argv = expand_argv(attr.command, rcpt_list, attr.flags);
export_env = argv_split(var_export_environ, ", \t\r\n");
command_status = pipe_command(request->fp, why,
diff --git a/gnu/dist/postfix/src/postalias/Makefile.in b/gnu/dist/postfix/src/postalias/Makefile.in
index 1fbc2a67d54..b18759656dd 100644
--- a/gnu/dist/postfix/src/postalias/Makefile.in
+++ b/gnu/dist/postfix/src/postalias/Makefile.in
@@ -21,10 +21,36 @@ $(PROG): $(OBJS) $(LIBS)
Makefile: Makefile.in
(set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
-test: $(TESTPROG)
-
update: ../../bin/$(PROG)
+test: test1 test2
+
+test1: $(PROG) map.in map-abc1.ref map-ghi1.ref map-ABC1.ref
+ ./$(PROG) map.in
+ for key in abc ghi; \
+ do \
+ ./$(PROG) -q $${key} map.in | diff map-$${key}1.ref -; \
+ done
+ ./$(PROG) -f map.in
+ for key in ABC; \
+ do \
+ ./$(PROG) -fq $${key} map.in | diff map-$${key}1.ref -; \
+ done
+ rm -f map.in.db
+
+test2: $(PROG) map.in map-abc2.ref map-ghi2.ref map-ABC2.ref
+ ./$(PROG) map.in
+ for key in abc ghi; \
+ do \
+ echo $${key} | ./$(PROG) -q - map.in | diff map-$${key}2.ref -; \
+ done
+ ./$(PROG) -f map.in
+ for key in ABC; \
+ do \
+ echo $${key} | ./$(PROG) -fq - map.in | diff map-$${key}2.ref -; \
+ done
+ rm -f map.in.db
+
../../bin/$(PROG): $(PROG)
cp $(PROG) ../../bin
@@ -65,6 +91,7 @@ postalias.o: ../../include/readlline.h
postalias.o: ../../include/stringops.h
postalias.o: ../../include/split_at.h
postalias.o: ../../include/get_hostname.h
+postalias.o: ../../include/vstring_vstream.h
postalias.o: ../../include/tok822.h
postalias.o: ../../include/resolve_clnt.h
postalias.o: ../../include/mail_conf.h
diff --git a/gnu/dist/postfix/src/postalias/map-ABC1.ref b/gnu/dist/postfix/src/postalias/map-ABC1.ref
new file mode 100644
index 00000000000..142195f36ae
--- /dev/null
+++ b/gnu/dist/postfix/src/postalias/map-ABC1.ref
@@ -0,0 +1 @@
+DEF
diff --git a/gnu/dist/postfix/src/postalias/map-ABC2.ref b/gnu/dist/postfix/src/postalias/map-ABC2.ref
new file mode 100644
index 00000000000..929916bcb4d
--- /dev/null
+++ b/gnu/dist/postfix/src/postalias/map-ABC2.ref
@@ -0,0 +1 @@
+ABC: DEF
diff --git a/gnu/dist/postfix/src/postalias/map-abc1.ref b/gnu/dist/postfix/src/postalias/map-abc1.ref
new file mode 100644
index 00000000000..142195f36ae
--- /dev/null
+++ b/gnu/dist/postfix/src/postalias/map-abc1.ref
@@ -0,0 +1 @@
+DEF
diff --git a/gnu/dist/postfix/src/postalias/map-abc2.ref b/gnu/dist/postfix/src/postalias/map-abc2.ref
new file mode 100644
index 00000000000..dfbdab6f18e
--- /dev/null
+++ b/gnu/dist/postfix/src/postalias/map-abc2.ref
@@ -0,0 +1 @@
+abc: DEF
diff --git a/gnu/dist/postfix/src/postalias/map-ghi1.ref b/gnu/dist/postfix/src/postalias/map-ghi1.ref
new file mode 100644
index 00000000000..7beb1db20dc
--- /dev/null
+++ b/gnu/dist/postfix/src/postalias/map-ghi1.ref
@@ -0,0 +1 @@
+jkl
diff --git a/gnu/dist/postfix/src/postalias/map-ghi2.ref b/gnu/dist/postfix/src/postalias/map-ghi2.ref
new file mode 100644
index 00000000000..e2ca310ca9d
--- /dev/null
+++ b/gnu/dist/postfix/src/postalias/map-ghi2.ref
@@ -0,0 +1 @@
+ghi: jkl
diff --git a/gnu/dist/postfix/src/postalias/map.in b/gnu/dist/postfix/src/postalias/map.in
new file mode 100644
index 00000000000..203fa0cd34a
--- /dev/null
+++ b/gnu/dist/postfix/src/postalias/map.in
@@ -0,0 +1,2 @@
+ABC: DEF
+ghi: jkl
diff --git a/gnu/dist/postfix/src/postalias/postalias.c b/gnu/dist/postfix/src/postalias/postalias.c
index 0fe46f910f5..6daf0fff6d4 100644
--- a/gnu/dist/postfix/src/postalias/postalias.c
+++ b/gnu/dist/postfix/src/postalias/postalias.c
@@ -5,7 +5,7 @@
/* Postfix alias database maintenance
/* SYNOPSIS
/* .fi
-/* \fBpostalias\fR [\fB-Ninrvw\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostalias\fR [\fB-Nfinrvw\fR] [\fB-c \fIconfig_dir\fR]
/* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
/* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION
@@ -29,7 +29,14 @@
/* instead of the default configuration directory.
/* .IP "\fB-d \fIkey\fR"
/* Search the specified maps for \fIkey\fR and remove one entry per map.
-/* The exit status is non-zero if the requested information was not found.
+/* The exit status is zero when the requested information was found.
+/*
+/* If a key value of \fB-\fR is specified, the program reads key
+/* values from the standard input stream. The exit status is zero
+/* when at least one of the requested keys was found.
+/* .IP \fB-f\fR
+/* Do not fold the lookup key to lower case while creating or querying
+/* a map.
/* .IP \fB-i\fR
/* Incremental mode. Read entries from standard input and do not
/* truncate an existing database. By default, \fBpostalias\fR creates
@@ -40,8 +47,13 @@
/* the host operating system.
/* .IP "\fB-q \fIkey\fR"
/* Search the specified maps for \fIkey\fR and print the first value
-/* found on the standard output stream. The exit status is non-zero
-/* if the requested information was not found.
+/* found on the standard output stream. The exit status is zero
+/* when the requested information was found.
+/*
+/* If a key value of \fB-\fR is specified, the program reads key
+/* values from the standard input stream and prints one line of
+/* \fIkey: value\fR output for each key that was found. The exit
+/* status is zero when at least one of the requested keys was found.
/* .IP \fB-r\fR
/* When updating a table, do not warn about duplicate entries; silently
/* replace them.
@@ -81,9 +93,6 @@
/* \fBpostalias\fR terminates with zero exit status in case of success
/* (including successful \fBpostmap -q\fR lookup) and terminates
/* with non-zero exit status in case of failure.
-/* BUGS
-/* The "delete key" support is limited to one delete operation
-/* per command invocation.
/* ENVIRONMENT
/* .ad
/* .fi
@@ -137,6 +146,7 @@
#include <stringops.h>
#include <split_at.h>
#include <get_hostname.h>
+#include <vstring_vstream.h>
/* Global library. */
@@ -188,23 +198,7 @@ static void postalias(char *map_type, char *path_name,
* Add records to the database.
*/
lineno = 0;
- while (readlline(line_buffer, source_fp, &lineno, READLL_STRIPNL)) {
-
- /*
- * Skip comments.
- */
- if (*STR(line_buffer) == '#')
- continue;
-
- /*
- * Weird stuff. Normally, a line that begins with whitespace is a
- * continuation of the previous line.
- */
- if (ISSPACE(*STR(line_buffer))) {
- msg_warn("%s, line %d: malformed line",
- VSTREAM_PATH(source_fp), lineno);
- continue;
- }
+ while (readlline(line_buffer, source_fp, &lineno)) {
/*
* Tokenize the input, so that we do the right thing when a quoted
@@ -263,7 +257,8 @@ static void postalias(char *map_type, char *path_name,
/*
* Store the value under a case-insensitive key.
*/
- lowercase(STR(key_buffer));
+ if (dict_flags & DICT_FLAG_FOLD_KEY)
+ lowercase(STR(key_buffer));
mkmap_append(mkmap, STR(key_buffer), STR(value_buffer));
}
@@ -306,6 +301,65 @@ static void postalias(char *map_type, char *path_name,
vstream_fclose(source_fp);
}
+/* postalias_queries - apply multiple requests from stdin */
+
+static int postalias_queries(VSTREAM *in, char **maps, const int map_count,
+ const int dict_flags)
+{
+ int found = 0;
+ VSTRING *keybuf = vstring_alloc(100);
+ DICT **dicts;
+ const char *map_name;
+ const char *value;
+ int n;
+
+ /*
+ * Sanity check.
+ */
+ if (map_count <= 0)
+ msg_panic("postalias_queries: bad map count");
+
+ /*
+ * Prepare to open maps lazily.
+ */
+ dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
+ for (n = 0; n < map_count; n++)
+ dicts[n] = 0;
+
+ /*
+ * Perform all queries. Open maps on the fly, to avoid opening unecessary
+ * maps.
+ */
+ while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF) {
+ if (dict_flags & DICT_FLAG_FOLD_KEY)
+ lowercase(STR(keybuf));
+ for (n = 0; n < map_count; n++) {
+ if (dicts[n] == 0)
+ dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
+ dict_open3(maps[n], map_name, O_RDONLY, DICT_FLAG_LOCK) :
+ dict_open3(var_db_type, maps[n], O_RDONLY, DICT_FLAG_LOCK));
+ if ((value = dict_get(dicts[n], STR(keybuf))) != 0) {
+ vstream_printf("%s: %s\n", STR(keybuf), value);
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found)
+ vstream_fflush(VSTREAM_OUT);
+
+ /*
+ * Cleanup.
+ */
+ for (n = 0; n < map_count; n++)
+ if (dicts[n])
+ dict_close(dicts[n]);
+ myfree((char *) dicts);
+ vstring_free(keybuf);
+
+ return (found);
+}
+
/* postalias_query - query a map and print the result to stdout */
static int postalias_query(const char *map_type, const char *map_name,
@@ -323,6 +377,50 @@ static int postalias_query(const char *map_type, const char *map_name,
return (value != 0);
}
+/* postalias_deletes - apply multiple requests from stdin */
+
+static int postalias_deletes(VSTREAM *in, char **maps, const int map_count)
+{
+ int found = 0;
+ VSTRING *keybuf = vstring_alloc(100);
+ DICT **dicts;
+ const char *map_name;
+ int n;
+
+ /*
+ * Sanity check.
+ */
+ if (map_count <= 0)
+ msg_panic("postalias_deletes: bad map count");
+
+ /*
+ * Open maps ahead of time.
+ */
+ dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
+ for (n = 0; n < map_count; n++)
+ dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
+ dict_open3(maps[n], map_name, O_RDWR, DICT_FLAG_LOCK) :
+ dict_open3(var_db_type, maps[n], O_RDWR, DICT_FLAG_LOCK));
+
+ /*
+ * Perform all requests.
+ */
+ while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF)
+ for (n = 0; n < map_count; n++)
+ found |= (dict_del(dicts[n], STR(keybuf)) == 0);
+
+ /*
+ * Cleanup.
+ */
+ for (n = 0; n < map_count; n++)
+ if (dicts[n])
+ dict_close(dicts[n]);
+ myfree((char *) dicts);
+ vstring_free(keybuf);
+
+ return (found);
+}
+
/* postalias_delete - delete a key value pair from a map */
static int postalias_delete(const char *map_type, const char *map_name,
@@ -331,10 +429,6 @@ static int postalias_delete(const char *map_type, const char *map_name,
DICT *dict;
int status;
- /*
- * XXX This must be generalized to multi-key (read from stdin) and
- * multi-map (given on command line) updates.
- */
dict = dict_open3(map_type, map_name, O_RDWR, DICT_FLAG_LOCK);
status = dict_del(dict, key);
dict_close(dict);
@@ -345,7 +439,7 @@ static int postalias_delete(const char *map_type, const char *map_name,
static NORETURN usage(char *myname)
{
- msg_fatal("usage: %s [-Ninrvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
+ msg_fatal("usage: %s [-Nfinrvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
myname);
}
@@ -357,7 +451,7 @@ int main(int argc, char **argv)
char *slash;
struct stat st;
int open_flags = O_RDWR | O_CREAT | O_TRUNC;
- int dict_flags = DICT_FLAG_DUP_WARN;
+ int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_KEY;
char *query = 0;
char *delkey = 0;
int found;
@@ -395,7 +489,7 @@ int main(int argc, char **argv)
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "Nc:d:inq:rvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "Nc:d:finq:rvw")) > 0) {
switch (ch) {
default:
usage(argv[0]);
@@ -413,6 +507,9 @@ int main(int argc, char **argv)
msg_fatal("specify only one of -q or -d");
delkey = optarg;
break;
+ case 'f':
+ dict_flags &= ~DICT_FLAG_FOLD_KEY;
+ break;
case 'i':
open_flags &= ~O_TRUNC;
break;
@@ -447,6 +544,8 @@ int main(int argc, char **argv)
if (delkey) { /* remove entry */
if (optind + 1 > argc)
usage(argv[0]);
+ if (strcmp(delkey, "-") == 0)
+ exit(postalias_deletes(VSTREAM_IN, argv + optind, argc - optind) == 0);
found = 0;
while (optind < argc) {
if ((path_name = split_at(argv[optind], ':')) != 0) {
@@ -460,6 +559,11 @@ int main(int argc, char **argv)
} else if (query) { /* query map(s) */
if (optind + 1 > argc)
usage(argv[0]);
+ if (strcmp(query, "-") == 0)
+ exit(postalias_queries(VSTREAM_IN, argv + optind, argc - optind,
+ dict_flags) == 0);
+ if (dict_flags & DICT_FLAG_FOLD_KEY)
+ lowercase(query);
while (optind < argc) {
if ((path_name = split_at(argv[optind], ':')) != 0) {
found = postalias_query(argv[optind], path_name, query);
diff --git a/gnu/dist/postfix/src/postconf/Makefile.in b/gnu/dist/postfix/src/postconf/Makefile.in
index 1cf46772319..35434d4b751 100644
--- a/gnu/dist/postfix/src/postconf/Makefile.in
+++ b/gnu/dist/postfix/src/postconf/Makefile.in
@@ -23,7 +23,10 @@ $(PROG): $(OBJS) $(LIBS)
../../conf/main.cf.default: $(PROG) Makefile
rm -f $@
- ./$(PROG) -d |egrep -v '^(myhostname|mydomain|mynetworks) ' >$@
+ (echo "# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE"; \
+ echo "# TEXT HERE JUST SHOWS DEFAULT SETTINGS BUILT INTO POSTFIX."; \
+ echo "#"; \
+ ./$(PROG) -d) |egrep -v '^(myhostname|mydomain|mynetworks) ' >$@
Makefile: Makefile.in
(set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
@@ -35,7 +38,7 @@ update: ../../bin/$(PROG) $(SAMPLES)
../../bin/$(PROG): $(PROG)
cp $(PROG) ../../bin
-$(MAKES): $(INC_DIR)/mail_params.h
+$(MAKES): $(INC_DIR)/mail_params.h ../global/mail_params.c
$(AWK) -f extract.awk ../*/*.c
printfck: $(OBJS) $(PROG)
@@ -79,12 +82,13 @@ postconf.o: ../../include/argv.h
postconf.o: ../../include/safe.h
postconf.o: ../../include/mymalloc.h
postconf.o: ../../include/split_at.h
-postconf.o: ../../include/readlline.h
+postconf.o: ../../include/vstring_vstream.h
postconf.o: ../../include/myflock.h
postconf.o: ../../include/mynetworks.h
postconf.o: ../../include/mail_conf.h
postconf.o: ../../include/mail_proto.h
postconf.o: ../../include/iostuff.h
+postconf.o: ../../include/attr.h
postconf.o: ../../include/mail_version.h
postconf.o: ../../include/mail_params.h
postconf.o: ../../include/mail_addr.h
@@ -95,9 +99,11 @@ postconf.o: int_vars.h
postconf.o: str_vars.h
postconf.o: raw_vars.h
postconf.o: smtp_vars.h
+postconf.o: install_vars.h
postconf.o: time_table.h
postconf.o: bool_table.h
postconf.o: int_table.h
postconf.o: str_table.h
postconf.o: smtp_table.h
+postconf.o: install_table.h
postconf.o: raw_table.h
diff --git a/gnu/dist/postfix/src/postconf/install_table.h b/gnu/dist/postfix/src/postconf/install_table.h
new file mode 100644
index 00000000000..a7dff6f3f18
--- /dev/null
+++ b/gnu/dist/postfix/src/postconf/install_table.h
@@ -0,0 +1 @@
+ VAR_CONFIG_DIR, DEF_CONFIG_DIR, &var_config_dir, 1, 0,
diff --git a/gnu/dist/postfix/src/postconf/install_vars.h b/gnu/dist/postfix/src/postconf/install_vars.h
new file mode 100644
index 00000000000..c54581ee8ee
--- /dev/null
+++ b/gnu/dist/postfix/src/postconf/install_vars.h
@@ -0,0 +1 @@
+char *var_config_dir;
diff --git a/gnu/dist/postfix/src/postconf/postconf.c b/gnu/dist/postfix/src/postconf/postconf.c
index da7d5625edd..ffcc7fbd86c 100644
--- a/gnu/dist/postfix/src/postconf/postconf.c
+++ b/gnu/dist/postfix/src/postconf/postconf.c
@@ -84,7 +84,7 @@
#include <mymalloc.h>
#include <argv.h>
#include <split_at.h>
-#include <readlline.h>
+#include <vstring_vstream.h>
#include <myflock.h>
/* Global library. */
@@ -131,6 +131,7 @@ DICT *text_table;
* Manually extracted.
*/
#include "smtp_vars.h"
+#include "install_vars.h"
/*
* Lookup tables generated by scanning actual C source files.
@@ -153,6 +154,7 @@ static CONFIG_INT_TABLE int_table[] = {
static CONFIG_STR_TABLE str_table[] = {
#include "str_table.h"
#include "smtp_table.h" /* XXX */
+#include "install_table.h"
0,
};
@@ -225,7 +227,7 @@ static void get_myhostname(void)
{
const char *name;
- if ((name = dict_lookup(CONFIG_DICT, VAR_MYHOSTNAME)) == 0)
+ if ((name = mail_conf_lookup_eval(VAR_MYHOSTNAME)) == 0)
name = check_myhostname();
var_myhostname = mystrdup(name);
}
@@ -255,13 +257,13 @@ static const char *check_mynetworks(void)
if (var_inet_interfaces == 0) {
if ((mode & SHOW_DEFS)
- || !(junk = mail_conf_lookup(VAR_INET_INTERFACES)))
+ || !(junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)))
junk = DEF_INET_INTERFACES;
var_inet_interfaces = mystrdup(junk);
}
if (var_mynetworks_style == 0) {
if ((mode & SHOW_DEFS)
- || !(junk = mail_conf_lookup(VAR_MYNETWORKS_STYLE)))
+ || !(junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)))
junk = DEF_MYNETWORKS_STYLE;
var_mynetworks_style = mystrdup(junk);
}
@@ -280,9 +282,9 @@ static void edit_parameters(int argc, char **argv)
VSTRING *buf = vstring_alloc(100);
VSTRING *key = vstring_alloc(10);
char *cp;
- char *value;
+ char *edit_key;
+ char *edit_val;
HTABLE *table;
- int first = 1;
struct cvalue {
char *value;
int found;
@@ -290,21 +292,26 @@ static void edit_parameters(int argc, char **argv)
struct cvalue *cvalue;
HTABLE_INFO **ht_info;
HTABLE_INFO **ht;
+ int interesting;
+ const char *err;
/*
* 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");
- while (*value && ISSPACE(*value))
- value++;
+ if (strchr(cp, '\n') != 0)
+ msg_fatal("edit accepts no multi-line input");
+ while (ISSPACE(*cp))
+ cp++;
+ if (*cp == '#')
+ msg_fatal("edit accepts no comment input");
+ if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
+ msg_fatal("%s: \"%s\"", err, cp);
cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
- cvalue->value = value;
+ cvalue->value = edit_val;
cvalue->found = 0;
- htable_enter(table, mystrtok(&cp, " \t\r\n"), (char *) cvalue);
+ htable_enter(table, edit_key, (char *) cvalue);
}
/*
@@ -342,26 +349,30 @@ static void edit_parameters(int argc, char **argv)
*/
#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 == '#') {
+ interesting = 0;
+ while (vstring_get(buf, src) != VSTREAM_EOF) {
+ for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
+ /* void */ ;
+ /* Copy comment, all-whitespace, or empty line. */
+ if (*cp == '#' || *cp == 0) {
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);
+ /* Copy or skip continued text. */
+ else if (cp > STR(buf)) {
+ if (interesting == 0)
+ vstream_fputs(STR(buf), dst);
+ }
+ /* Copy or replace start of logical line. */
+ else {
+ vstring_strncpy(key, cp, strcspn(cp, " \t\r\n="));
+ cvalue = (struct cvalue *) htable_find(table, STR(key));
+ if ((interesting = !!cvalue) != 0) {
+ if (cvalue->found++ == 1)
+ msg_warn("%s: multiple entries for \"%s\"", path, STR(key));
+ vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value);
+ } else {
+ vstream_fputs(STR(buf), dst);
+ }
}
}
diff --git a/gnu/dist/postfix/src/postdrop/Makefile.in b/gnu/dist/postfix/src/postdrop/Makefile.in
index 2d893c3ac61..a58a1b581ce 100644
--- a/gnu/dist/postfix/src/postdrop/Makefile.in
+++ b/gnu/dist/postfix/src/postdrop/Makefile.in
@@ -63,8 +63,9 @@ postdrop.o: ../../include/vstring.h
postdrop.o: ../../include/msg_vstream.h
postdrop.o: ../../include/msg_syslog.h
postdrop.o: ../../include/argv.h
-postdrop.o: ../../include/mail_proto.h
postdrop.o: ../../include/iostuff.h
+postdrop.o: ../../include/mail_proto.h
+postdrop.o: ../../include/attr.h
postdrop.o: ../../include/mail_queue.h
postdrop.o: ../../include/mail_params.h
postdrop.o: ../../include/mail_conf.h
diff --git a/gnu/dist/postfix/src/postdrop/postdrop.c b/gnu/dist/postfix/src/postdrop/postdrop.c
index 4a8c16d3c48..dacd70fdee5 100644
--- a/gnu/dist/postfix/src/postdrop/postdrop.c
+++ b/gnu/dist/postfix/src/postdrop/postdrop.c
@@ -2,29 +2,31 @@
/* NAME
/* postdrop 1
/* SUMMARY
-/* Postfix mail posting agent
+/* Postfix mail posting utility
/* SYNOPSIS
-/* \fBpostdrop\fR [\fIoption ...\fR]
+/* \fBpostdrop\fR [\fB-rv\fR] [\fB-c \fIconfig_dir\fR]
/* DESCRIPTION
/* The \fBpostdrop\fR command creates a file in the \fBmaildrop\fR
/* directory and copies its standard input to the file.
/*
-/* The command is designed to run with set-gid privileges, and with
-/* group write permission to the \fBmaildrop\fR queue directory.
-/*
-/* The \fBpostdrop\fR command is automatically invoked by the
-/* \fBsendmail\fR(1) mail posting agent when the \fBmaildrop\fR
-/* queue directory is not world-writable.
-/*
/* Options:
+/* .IP \fB-c \fIconfig_dir\fR
+/* The \fBmain.cf\fR configuration file is in the named directory
+/* instead of the default configuration directory. See also the
+/* MAIL_CONFIG environment setting below.
+/* .IP \fB-r\fR
+/* Use a Postfix-internal protocol for reading the message from
+/* standard input, and for reporting status information on standard
+/* output. This is currently the only supported method.
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose.
/* SECURITY
/* .ad
/* .fi
-/* This program is designed so that it can run with set-user (or
-/* group) id privileges.
+/* The command is designed to run with set-group ID privileges, so
+/* that it can write to the \fBmaildrop\fR queue directory and so that
+/* it can connect to Postfix daemon processes.
/* DIAGNOSTICS
/* Fatal errors: malformed input, I/O error, out of memory. Problems
/* are logged to \fBsyslogd\fR(8) and to the standard error stream.
@@ -33,8 +35,16 @@
/* ENVIRONMENT
/* .ad
/* .fi
-/* The program deletes all environment information, because the C
-/* library can't be trusted.
+/* .IP MAIL_CONFIG
+/* Directory with the \fBmain.cf\fR file. In order to avoid exploitation
+/* of set-group ID privileges, it is not possible to specify arbitrary
+/* directory names.
+/*
+/* A non-standard directory is allowed only if the name is listed in the
+/* standard \fBmain.cf\fR file, in the \fBalternate_config_directories\fR
+/* configuration parameter value.
+/*
+/* Only the super-user is allowed to specify arbitrary directory names.
/* FILES
/* /var/spool/postfix, mail queue
/* /etc/postfix, configuration files
@@ -75,6 +85,7 @@
#include <stdlib.h>
#include <signal.h>
#include <syslog.h>
+#include <errno.h>
/* Utility library. */
@@ -85,6 +96,7 @@
#include <msg_vstream.h>
#include <msg_syslog.h>
#include <argv.h>
+#include <iostuff.h>
/* Global library. */
@@ -104,10 +116,14 @@
/*
* WARNING WARNING WARNING
*
- * This software is designed to run set-gid on systems that cannot afford a
- * world-writable spool directory. In order to make this restriction work,
- * this software should not run any external commands, nor should it take
- * any configuration information from the user.
+ * This software is designed to run set-gid. In order to avoid exploitation of
+ * privilege, this software should not run any external commands, nor should
+ * it take any information from the user unless that information can be
+ * properly sanitized. To get an idea of how much information a process can
+ * inherit from a potentially hostile user, examine all the members of the
+ * process structure (typically, in /usr/include/sys/proc.h): the current
+ * directory, open files, timers, signals, environment, command line, umask,
+ * and so on.
*/
/*
@@ -137,8 +153,17 @@ static void postdrop_cleanup(void)
static void postdrop_sig(int sig)
{
- postdrop_cleanup();
- exit(sig);
+
+ /*
+ * Assume atomic signal() updates, even when emulated with sigaction().
+ */
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN
+ && signal(SIGINT, SIG_IGN) != SIG_IGN
+ && signal(SIGQUIT, SIG_IGN) != SIG_IGN
+ && signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+ postdrop_cleanup();
+ exit(sig);
+ }
}
/* main - the main program */
@@ -183,6 +208,28 @@ int main(int argc, char **argv)
set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
/*
+ * Parse JCL. This program is set-gid and must sanitize all command-line
+ * arguments. The configuration directory argument is validated by the
+ * mail configuration read routine. Don't do complex things until we have
+ * completed initializations.
+ */
+ while ((c = GETOPT(argc, argv, "c:rv")) > 0) {
+ switch (c) {
+ case 'c':
+ if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
+ msg_fatal("out of memory");
+ break;
+ case 'r': /* forward compatibility */
+ break;
+ case 'v':
+ msg_verbose++;
+ break;
+ default:
+ msg_fatal("usage: %s [-c config_dir] [-v]", argv[0]);
+ }
+ }
+
+ /*
* Read the global configuration file and extract configuration
* information. Some claim that the user should supply the working
* directory instead. That might be OK, given that this command needs
@@ -194,6 +241,13 @@ int main(int argc, char **argv)
mail_conf_read();
/*
+ * Stop run-away process accidents by limiting the queue file size. This
+ * is not a defense against DOS attack.
+ */
+ if (var_message_limit > 0 && get_file_limit() > var_message_limit)
+ set_file_limit((off_t) var_message_limit);
+
+ /*
* Strip the environment so we don't have to trust the C library.
*/
import_env = argv_split(var_import_environ, ", \t\r\n");
@@ -210,6 +264,7 @@ int main(int argc, char **argv)
* clean up incomplete output.
*/
signal(SIGPIPE, SIG_IGN);
+ signal(SIGXFSZ, SIG_IGN);
signal(SIGHUP, postdrop_sig);
signal(SIGINT, postdrop_sig);
@@ -217,18 +272,7 @@ int main(int argc, char **argv)
signal(SIGTERM, postdrop_sig);
msg_cleanup(postdrop_cleanup);
- /*
- * Parse JCL.
- */
- while ((c = GETOPT(argc, argv, "v")) > 0) {
- switch (c) {
- case 'v':
- msg_verbose++;
- break;
- default:
- msg_fatal("usage: %s [-v]", argv[0]);
- }
- }
+ /* End of initializations. */
/*
* Create queue file. mail_stream_file() never fails. Send the queue ID
@@ -236,8 +280,10 @@ int main(int argc, char **argv)
* clean up in case of a fatal error or an interrupt.
*/
dst = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC,
- MAIL_SERVICE_PICKUP);
- mail_print(VSTREAM_OUT, "%s", dst->id);
+ MAIL_SERVICE_PICKUP, 0444);
+ attr_print(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, dst->id,
+ ATTR_TYPE_END);
vstream_fflush(VSTREAM_OUT);
postdrop_path = mystrdup(VSTREAM_PATH(dst->stream));
@@ -248,6 +294,9 @@ int main(int argc, char **argv)
* file descriptor to the cleanup daemon. These are by no means all
* sanity checks - the cleanup service and queue manager services will
* reject messages that lack required information.
+ *
+ * If something goes wrong, slurp up the input before responding to the
+ * client, otherwise the client will give up after detecting SIGPIPE.
*/
vstream_control(VSTREAM_IN, VSTREAM_CTL_PATH, "stdin", VSTREAM_CTL_END);
buf = vstring_alloc(100);
@@ -273,8 +322,12 @@ int main(int argc, char **argv)
msg_fatal("uid=%ld: unexpected record type: %d", (long) uid, rec_type);
if (rec_type == **expected)
expected++;
- if (REC_PUT_BUF(dst->stream, rec_type, buf) < 0)
- msg_fatal("uid=%ld: queue file write error", (long) uid);
+ if (REC_PUT_BUF(dst->stream, rec_type, buf) < 0) {
+ while ((rec_type = rec_get(VSTREAM_IN, buf, var_line_limit)) > 0
+ && rec_type != REC_TYPE_END)
+ /* void */ ;
+ break;
+ }
if (rec_type == REC_TYPE_END)
break;
}
@@ -283,8 +336,10 @@ int main(int argc, char **argv)
/*
* Finish the file.
*/
- if ((status = mail_stream_finish(dst)) != 0)
- msg_fatal("uid=%ld: %s", (long) uid, cleanup_strerror(status));
+ if ((status = mail_stream_finish(dst, (VSTRING *) 0)) != 0) {
+ postdrop_cleanup();
+ msg_warn("uid=%ld: %m", (long) uid);
+ }
/*
* Disable deletion on fatal error before reporting success, so the file
@@ -298,7 +353,10 @@ int main(int argc, char **argv)
/*
* Send the completion status to the caller and terminate.
*/
- mail_print(VSTREAM_OUT, "%d", status);
+ attr_print(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, "",
+ ATTR_TYPE_END);
vstream_fflush(VSTREAM_OUT);
exit(status);
}
diff --git a/gnu/dist/postfix/src/postfix/Makefile.in b/gnu/dist/postfix/src/postfix/Makefile.in
index 3b651dbecc3..45e05f7f978 100644
--- a/gnu/dist/postfix/src/postfix/Makefile.in
+++ b/gnu/dist/postfix/src/postfix/Makefile.in
@@ -66,5 +66,8 @@ postfix.o: ../../include/msg_vstream.h
postfix.o: ../../include/msg_syslog.h
postfix.o: ../../include/stringops.h
postfix.o: ../../include/vstring.h
+postfix.o: ../../include/clean_env.h
+postfix.o: ../../include/argv.h
+postfix.o: ../../include/safe.h
postfix.o: ../../include/mail_conf.h
postfix.o: ../../include/mail_params.h
diff --git a/gnu/dist/postfix/src/postfix/postfix.c b/gnu/dist/postfix/src/postfix/postfix.c
index 466118504bb..2db1dc24fb1 100644
--- a/gnu/dist/postfix/src/postfix/postfix.c
+++ b/gnu/dist/postfix/src/postfix/postfix.c
@@ -8,11 +8,15 @@
/* \fBpostfix\fR [\fB-c \fIconfig_dir\fR] [\fB-D\fR] [\fB-v\fR]
/* \fIcommand\fR
/* DESCRIPTION
+/* This command is reserved for the super-user. To submit mail,
+/* use the Postfix \fBsendmail\fR command.
+/*
/* The \fBpostfix\fR command controls the operation of the Postfix
/* mail system: start or stop the \fBmaster\fR daemon, do a health
-/* check, and other maintenance. The command sets up a standardized
-/* environment and runs the \fBpostfix-script\fR shell script to
-/* do the actual work.
+/* check, and other maintenance.
+/*
+/* The \fBpostfix\fR command sets up a standardized environment and
+/* runs the \fBpostfix-script\fR shell script to do the actual work.
/*
/* The following commands are implemented:
/* .IP \fBcheck\fR
@@ -63,25 +67,48 @@
/* This is set when the -v command-line option is present.
/* .IP \fBMAIL_DEBUG\fR
/* This is set when the -D command-line option is present.
-/* .PP
-/* The following configuration parameters are made available
-/* as process environment variables with the same names:
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* The following \fBmain.cf\fR configuration parameters are made
+/* available as process environment variables with the same names:
/* .IP \fBcommand_directory\fR
-/* Directory with Postfix support commands (default:
-/* \fB$program_directory\fR).
+/* Directory with Postfix administrative commands.
/* .IP \fBdaemon_directory\fR
-/* Directory with Postfix daemon programs (default:
-/* \fB$program_directory\fR).
+/* Directory with Postfix daemon programs.
/* .IP \fBconfig_directory\fR
/* Directory with Postfix configuration files and with administrative
/* shell scripts.
/* .IP \fBqueue_directory\fR
-/* The directory with the Postfix queue directory (and with some
-/* files needed for programs running in a chrooted environment).
+/* The directory with Postfix queue files, with local inter-process
+/* communication endpoints, and with files needed for daemon programs
+/* that run in the optional chrooted environment.
/* .IP \fBmail_owner\fR
-/* The owner of the Postfix queue and of most Postfix processes.
+/* The owner of Postfix queue files and of most Postfix processes.
+/* .IP \fBsetgid_group\fR
+/* The group for mail submission and queue management commands.
+/* .IP \fBsendmail_path
+/* The full pathname for the Postfix sendmail command.
+/* .IP \fBnewaliases_path
+/* The full pathname for the Postfix newaliases command.
+/* .IP \fBmailq_path
+/* The full pathname for the Postfix mailq command.
+/* .IP \fBmanpage_directory
+/* The directory for the Postfix on-line manual pages.
+/* .IP \fBsample_directory
+/* The directory for the Postfix sample configuration files.
+/* .IP \fBreadme_directory
+/* The directory for the Postfix README files.
+/* .SH Other configuration parameters
+/* .ad
+/* .fi
+/* .IP \fBimport_environment\fR
+/* List of names of environment parameters that can be imported
+/* from non-Postfix processes.
/* FILES
/* $\fBconfig_directory/postfix-script\fR, administrative commands
+/* $\fBconfig_directory/main.cf\fR, configuration parameters
+/* $\fBconfig_directory/master.cf\fR, Postfix daemon processes
/* SEE ALSO
/* master(8) Postfix master program
/* LICENSE
@@ -115,12 +142,24 @@
#include <msg_vstream.h>
#include <msg_syslog.h>
#include <stringops.h>
+#include <clean_env.h>
+#include <argv.h>
+#include <safe.h>
/* Global library. */
#include <mail_conf.h>
#include <mail_params.h>
+/* Additional installation parameters. */
+
+static char *var_sendmail_path;
+static char *var_mailq_path;
+static char *var_newalias_path;
+static char *var_manpage_dir;
+static char *var_sample_dir;
+static char *var_readme_dir;
+
/* check_setenv - setenv() with extreme prejudice */
static void check_setenv(char *name, char *value)
@@ -137,9 +176,18 @@ int main(int argc, char **argv)
char *script;
struct stat st;
char *slash;
- int uid;
int fd;
int ch;
+ ARGV *import_env;
+ static CONFIG_STR_TABLE str_table[] = {
+ VAR_SENDMAIL_PATH, DEF_SENDMAIL_PATH, &var_sendmail_path, 1, 0,
+ VAR_MAILQ_PATH, DEF_MAILQ_PATH, &var_mailq_path, 1, 0,
+ VAR_NEWALIAS_PATH, DEF_NEWALIAS_PATH, &var_newalias_path, 1, 0,
+ VAR_MANPAGE_DIR, DEF_MANPAGE_DIR, &var_manpage_dir, 1, 0,
+ VAR_SAMPLE_DIR, DEF_SAMPLE_DIR, &var_sample_dir, 1, 0,
+ VAR_README_DIR, DEF_README_DIR, &var_readme_dir, 1, 0,
+ 0,
+ };
/*
* Be consistent with file permissions.
@@ -172,10 +220,12 @@ int main(int argc, char **argv)
* privileges for selected operations. That's right - it takes privileges
* to toss privileges.
*/
- if ((uid = getuid()) != 0) {
+ if (getuid() != 0) {
msg_error("to submit mail, use the Postfix sendmail command");
msg_fatal("the postfix command is reserved for the superuser");
}
+ if (unsafe() != 0)
+ msg_fatal("the postfix command must not run as a set-uid process");
/*
* Parse switches.
@@ -206,6 +256,17 @@ int main(int argc, char **argv)
* scripts.
*/
mail_conf_read();
+ get_mail_conf_str_table(str_table);
+
+ /*
+ * Environment import filter, to enforce consistent behavior whether this
+ * command is started by hand, or at system boot time. This is necessary
+ * because some shell scripts use environment settings to override
+ * main.cf settings.
+ */
+ import_env = argv_split(var_import_environ, ", \t\r\n");
+ clean_env(import_env->argv);
+ argv_free(import_env);
check_setenv("PATH", ROOT_PATH); /* sys_defs.h */
check_setenv(CONF_ENV_PATH, var_config_dir);/* mail_conf.h */
@@ -214,7 +275,18 @@ int main(int argc, char **argv)
check_setenv(VAR_DAEMON_DIR, var_daemon_dir); /* main.cf */
check_setenv(VAR_QUEUE_DIR, var_queue_dir); /* main.cf */
check_setenv(VAR_CONFIG_DIR, var_config_dir); /* main.cf */
+
+ /*
+ * Do we want to keep adding things here as shell scripts evolve?
+ */
check_setenv(VAR_MAIL_OWNER, var_mail_owner); /* main.cf */
+ check_setenv(VAR_SGID_GROUP, var_sgid_group); /* main.cf */
+ check_setenv(VAR_SENDMAIL_PATH, var_sendmail_path); /* main.cf */
+ check_setenv(VAR_MAILQ_PATH, var_mailq_path); /* main.cf */
+ check_setenv(VAR_NEWALIAS_PATH, var_newalias_path); /* main.cf */
+ check_setenv(VAR_MANPAGE_DIR, var_manpage_dir); /* main.cf */
+ check_setenv(VAR_SAMPLE_DIR, var_sample_dir); /* main.cf */
+ check_setenv(VAR_README_DIR, var_readme_dir); /* main.cf */
/*
* Make sure these directories exist. Run the maintenance scripts with as
diff --git a/gnu/dist/postfix/src/postkick/Makefile.in b/gnu/dist/postfix/src/postkick/Makefile.in
index b34c9673614..d4cf100f460 100644
--- a/gnu/dist/postfix/src/postkick/Makefile.in
+++ b/gnu/dist/postfix/src/postkick/Makefile.in
@@ -61,7 +61,9 @@ postkick.o: ../../include/vstream.h
postkick.o: ../../include/vbuf.h
postkick.o: ../../include/msg_vstream.h
postkick.o: ../../include/safe.h
+postkick.o: ../../include/events.h
postkick.o: ../../include/mail_proto.h
postkick.o: ../../include/iostuff.h
+postkick.o: ../../include/attr.h
postkick.o: ../../include/mail_params.h
postkick.o: ../../include/mail_conf.h
diff --git a/gnu/dist/postfix/src/postkick/postkick.c b/gnu/dist/postfix/src/postkick/postkick.c
index c756c14ae49..a4066a96aca 100644
--- a/gnu/dist/postfix/src/postkick/postkick.c
+++ b/gnu/dist/postfix/src/postkick/postkick.c
@@ -80,6 +80,7 @@
#include <vstream.h>
#include <msg_vstream.h>
#include <safe.h>
+#include <events.h>
/* Global library. */
@@ -164,6 +165,7 @@ int main(int argc, char **argv)
class, service);
exit(1);
} else {
+ event_drain();
exit(0);
}
}
diff --git a/gnu/dist/postfix/src/postmap/Makefile.in b/gnu/dist/postfix/src/postmap/Makefile.in
index b2d64c2bf1d..a96270e30f8 100644
--- a/gnu/dist/postfix/src/postmap/Makefile.in
+++ b/gnu/dist/postfix/src/postmap/Makefile.in
@@ -21,13 +21,39 @@ $(PROG): $(OBJS) $(LIBS)
Makefile: Makefile.in
(set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
-test: $(TESTPROG)
-
update: ../../bin/$(PROG)
../../bin/$(PROG): $(PROG)
cp $(PROG) ../../bin
+test: test1 test2
+
+test1: $(PROG) map.in map-abc1.ref map-ghi1.ref map-ABC1.ref
+ ./$(PROG) map.in
+ for key in abc ghi; \
+ do \
+ ./$(PROG) -q $${key} map.in | diff map-$${key}1.ref -; \
+ done
+ ./$(PROG) -f map.in
+ for key in ABC; \
+ do \
+ ./$(PROG) -fq $${key} map.in | diff map-$${key}1.ref -; \
+ done
+ rm -f map.in.db
+
+test2: $(PROG) map.in map-abc2.ref map-ghi2.ref map-ABC2.ref
+ ./$(PROG) map.in
+ for key in abc ghi; \
+ do \
+ echo $${key} | ./$(PROG) -q - map.in | diff map-$${key}2.ref -; \
+ done
+ ./$(PROG) -f map.in
+ for key in ABC; \
+ do \
+ echo $${key} | ./$(PROG) -fq - map.in | diff map-$${key}2.ref -; \
+ done
+ rm -f map.in.db
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
@@ -39,7 +65,7 @@ lint:
lint $(DEFS) $(SRCS) $(LINTFIX)
clean:
- rm -f *.o *core $(PROG) $(TESTPROG) junk
+ rm -f *.o *core $(PROG) $(TESTPROG) junk map.in.db
rm -rf printfck
tidy: clean
@@ -64,6 +90,7 @@ postmap.o: ../../include/msg_vstream.h
postmap.o: ../../include/readlline.h
postmap.o: ../../include/stringops.h
postmap.o: ../../include/split_at.h
+postmap.o: ../../include/vstring_vstream.h
postmap.o: ../../include/mail_conf.h
postmap.o: ../../include/mail_params.h
postmap.o: ../../include/mkmap.h
diff --git a/gnu/dist/postfix/src/postmap/map-ABC1.ref b/gnu/dist/postfix/src/postmap/map-ABC1.ref
new file mode 100644
index 00000000000..142195f36ae
--- /dev/null
+++ b/gnu/dist/postfix/src/postmap/map-ABC1.ref
@@ -0,0 +1 @@
+DEF
diff --git a/gnu/dist/postfix/src/postmap/map-ABC2.ref b/gnu/dist/postfix/src/postmap/map-ABC2.ref
new file mode 100644
index 00000000000..cc3ea4e3c40
--- /dev/null
+++ b/gnu/dist/postfix/src/postmap/map-ABC2.ref
@@ -0,0 +1 @@
+ABC DEF
diff --git a/gnu/dist/postfix/src/postmap/map-abc1.ref b/gnu/dist/postfix/src/postmap/map-abc1.ref
new file mode 100644
index 00000000000..142195f36ae
--- /dev/null
+++ b/gnu/dist/postfix/src/postmap/map-abc1.ref
@@ -0,0 +1 @@
+DEF
diff --git a/gnu/dist/postfix/src/postmap/map-abc2.ref b/gnu/dist/postfix/src/postmap/map-abc2.ref
new file mode 100644
index 00000000000..2ee5fab43d9
--- /dev/null
+++ b/gnu/dist/postfix/src/postmap/map-abc2.ref
@@ -0,0 +1 @@
+abc DEF
diff --git a/gnu/dist/postfix/src/postmap/map-ghi1.ref b/gnu/dist/postfix/src/postmap/map-ghi1.ref
new file mode 100644
index 00000000000..7beb1db20dc
--- /dev/null
+++ b/gnu/dist/postfix/src/postmap/map-ghi1.ref
@@ -0,0 +1 @@
+jkl
diff --git a/gnu/dist/postfix/src/postmap/map-ghi2.ref b/gnu/dist/postfix/src/postmap/map-ghi2.ref
new file mode 100644
index 00000000000..5926a187ff9
--- /dev/null
+++ b/gnu/dist/postfix/src/postmap/map-ghi2.ref
@@ -0,0 +1 @@
+ghi jkl
diff --git a/gnu/dist/postfix/src/postmap/map.in b/gnu/dist/postfix/src/postmap/map.in
new file mode 100644
index 00000000000..f1805549ec2
--- /dev/null
+++ b/gnu/dist/postfix/src/postmap/map.in
@@ -0,0 +1,2 @@
+ABC DEF
+ghi jkl
diff --git a/gnu/dist/postfix/src/postmap/postmap.c b/gnu/dist/postfix/src/postmap/postmap.c
index f82edfb1443..40579938e7a 100644
--- a/gnu/dist/postfix/src/postmap/postmap.c
+++ b/gnu/dist/postfix/src/postmap/postmap.c
@@ -5,8 +5,9 @@
/* Postfix lookup table management
/* SYNOPSIS
/* .fi
-/* \fBpostmap\fR [\fB-Ninrvw\fR] [\fB-c \fIconfig_dir\fR] [\fB-d \fIkey\fR]
-/* [\fB-q \fIkey\fR] [\fIfile_type\fR:]\fIfile_name\fR ...
+/* \fBpostmap\fR [\fB-Nfinrvw\fR] [\fB-c \fIconfig_dir\fR]
+/* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
+/* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION
/* The \fBpostmap\fR command creates or queries one or more Postfix
/* lookup tables, or updates an existing one. The input and output
@@ -15,6 +16,9 @@
/* .ti +4
/* \fBmakemap \fIfile_type\fR \fIfile_name\fR < \fIfile_name\fR
/*
+/* If the result files do not exist they will be created with the
+/* same group and other read permissions as the source file.
+/*
/* While the table update is in progress, signal delivery is
/* postponed, and an exclusive, advisory, lock is placed on the
/* entire table, in order to avoid surprises in spectator
@@ -22,14 +26,16 @@
/*
/* The format of a lookup table input file is as follows:
/* .IP \(bu
-/* Blank lines are ignored. So are lines beginning with `#'.
-/* .IP \(bu
/* A table entry has the form
/* .sp
/* .ti +5
/* \fIkey\fR whitespace \fIvalue\fR
/* .IP \(bu
-/* A line that starts with whitespace continues the preceding line.
+/* Empty lines and whitespace-only lines are ignored, as
+/* are lines whose first non-whitespace character is a `#'.
+/* .IP \(bu
+/* A logical line starts with non-whitespace text. A line that
+/* starts with whitespace continues a logical line.
/* .PP
/* The \fIkey\fR and \fIvalue\fR are processed as is, except that
/* surrounding white space is stripped off. Unlike with Postfix alias
@@ -47,7 +53,14 @@
/* instead of the default configuration directory.
/* .IP "\fB-d \fIkey\fR"
/* Search the specified maps for \fIkey\fR and remove one entry per map.
-/* The exit status is non-zero if the requested information was not found.
+/* The exit status is zero when the requested information was found.
+/*
+/* If a key value of \fB-\fR is specified, the program reads key
+/* values from the standard input stream. The exit status is zero
+/* when at least one of the requested keys was found.
+/* .IP \fB-f\fR
+/* Do not fold the lookup key to lower case while creating or querying
+/* a map.
/* .IP \fB-i\fR
/* Incremental mode. Read entries from standard input and do not
/* truncate an existing database. By default, \fBpostmap\fR creates
@@ -58,8 +71,13 @@
/* the host operating system.
/* .IP "\fB-q \fIkey\fR"
/* Search the specified maps for \fIkey\fR and print the first value
-/* found on the standard output stream. The exit status is non-zero
-/* if the requested information was not found.
+/* found on the standard output stream. The exit status is zero
+/* when the requested information was found.
+/*
+/* If a key value of \fB-\fR is specified, the program reads key
+/* values from the standard input stream and prints one line of
+/* \fIkey value\fR output for each key that was found. The exit
+/* status is zero when at least one of the requested keys was found.
/* .IP \fB-r\fR
/* When updating a table, do not warn about duplicate entries; silently
/* replace them.
@@ -98,9 +116,6 @@
/* \fBpostmap\fR terminates with zero exit status in case of success
/* (including successful \fBpostmap -q\fR lookup) and terminates
/* with non-zero exit status in case of failure.
-/* BUGS
-/* The "delete key" support is limited to one delete operation
-/* per command invocation.
/* ENVIRONMENT
/* .ad
/* .fi
@@ -146,6 +161,7 @@
#include <readlline.h>
#include <stringops.h>
#include <split_at.h>
+#include <vstring_vstream.h>
/* Global library. */
@@ -168,6 +184,8 @@ static void postmap(char *map_type, char *path_name,
int lineno;
char *key;
char *value;
+ struct stat st;
+ mode_t saved_mask;
/*
* Initialize.
@@ -179,6 +197,14 @@ static void postmap(char *map_type, char *path_name,
} else if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) {
msg_fatal("open %s: %m", path_name);
}
+ if (fstat(vstream_fileno(source_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", path_name);
+
+ /*
+ * Turn off group/other read permissions as indicated in the source file.
+ */
+ if (S_ISREG(st.st_mode))
+ saved_mask = umask(022 | (~st.st_mode & 077));
/*
* Open the database, optionally create it when it does not exist,
@@ -188,39 +214,31 @@ static void postmap(char *map_type, char *path_name,
mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags);
/*
+ * And restore the umask, in case it matters.
+ */
+ if (S_ISREG(st.st_mode))
+ umask(saved_mask);
+
+ /*
* Add records to the database.
*/
lineno = 0;
- while (readlline(line_buffer, source_fp, &lineno, READLL_STRIPNL)) {
-
- /*
- * Skip comments.
- */
- if (*STR(line_buffer) == '#')
- continue;
+ while (readlline(line_buffer, source_fp, &lineno)) {
/*
* Split on the first whitespace character, then trim leading and
* trailing whitespace from key and value.
*/
key = STR(line_buffer);
- value = STR(line_buffer) + strcspn(STR(line_buffer), " \t\r\n");
+ value = key + strcspn(key, " \t\r\n");
if (*value)
*value++ = 0;
- while (ISSPACE(*key))
- key++;
while (ISSPACE(*value))
value++;
trimblanks(key, 0)[0] = 0;
trimblanks(value, 0)[0] = 0;
/*
- * Skip empty lines, or lines with whitespace characters only.
- */
- if (*key == 0 && *value == 0)
- continue;
-
- /*
* Enforce the "key whitespace value" format. Disallow missing keys
* or missing values.
*/
@@ -236,7 +254,8 @@ static void postmap(char *map_type, char *path_name,
/*
* Store the value under a case-insensitive key.
*/
- lowercase(key);
+ if (dict_flags & DICT_FLAG_FOLD_KEY)
+ lowercase(key);
mkmap_append(mkmap, key, value);
}
@@ -253,6 +272,65 @@ static void postmap(char *map_type, char *path_name,
vstream_fclose(source_fp);
}
+/* postmap_queries - apply multiple requests from stdin */
+
+static int postmap_queries(VSTREAM *in, char **maps, const int map_count,
+ const int dict_flags)
+{
+ int found = 0;
+ VSTRING *keybuf = vstring_alloc(100);
+ DICT **dicts;
+ const char *map_name;
+ const char *value;
+ int n;
+
+ /*
+ * Sanity check.
+ */
+ if (map_count <= 0)
+ msg_panic("postmap_queries: bad map count");
+
+ /*
+ * Prepare to open maps lazily.
+ */
+ dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
+ for (n = 0; n < map_count; n++)
+ dicts[n] = 0;
+
+ /*
+ * Perform all queries. Open maps on the fly, to avoid opening unecessary
+ * maps.
+ */
+ while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF) {
+ if (dict_flags & DICT_FLAG_FOLD_KEY)
+ lowercase(STR(keybuf));
+ for (n = 0; n < map_count; n++) {
+ if (dicts[n] == 0)
+ dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
+ dict_open3(maps[n], map_name, O_RDONLY, DICT_FLAG_LOCK) :
+ dict_open3(var_db_type, maps[n], O_RDONLY, DICT_FLAG_LOCK));
+ if ((value = dict_get(dicts[n], STR(keybuf))) != 0) {
+ vstream_printf("%s %s\n", STR(keybuf), value);
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found)
+ vstream_fflush(VSTREAM_OUT);
+
+ /*
+ * Cleanup.
+ */
+ for (n = 0; n < map_count; n++)
+ if (dicts[n])
+ dict_close(dicts[n]);
+ myfree((char *) dicts);
+ vstring_free(keybuf);
+
+ return (found);
+}
+
/* postmap_query - query a map and print the result to stdout */
static int postmap_query(const char *map_type, const char *map_name,
@@ -270,6 +348,50 @@ static int postmap_query(const char *map_type, const char *map_name,
return (value != 0);
}
+/* postmap_deletes - apply multiple requests from stdin */
+
+static int postmap_deletes(VSTREAM *in, char **maps, const int map_count)
+{
+ int found = 0;
+ VSTRING *keybuf = vstring_alloc(100);
+ DICT **dicts;
+ const char *map_name;
+ int n;
+
+ /*
+ * Sanity check.
+ */
+ if (map_count <= 0)
+ msg_panic("postmap_deletes: bad map count");
+
+ /*
+ * Open maps ahead of time.
+ */
+ dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
+ for (n = 0; n < map_count; n++)
+ dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
+ dict_open3(maps[n], map_name, O_RDWR, DICT_FLAG_LOCK) :
+ dict_open3(var_db_type, maps[n], O_RDWR, DICT_FLAG_LOCK));
+
+ /*
+ * Perform all requests.
+ */
+ while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF)
+ for (n = 0; n < map_count; n++)
+ found |= (dict_del(dicts[n], STR(keybuf)) == 0);
+
+ /*
+ * Cleanup.
+ */
+ for (n = 0; n < map_count; n++)
+ if (dicts[n])
+ dict_close(dicts[n]);
+ myfree((char *) dicts);
+ vstring_free(keybuf);
+
+ return (found);
+}
+
/* postmap_delete - delete a (key, value) pair from a map */
static int postmap_delete(const char *map_type, const char *map_name,
@@ -278,10 +400,6 @@ static int postmap_delete(const char *map_type, const char *map_name,
DICT *dict;
int status;
- /*
- * XXX This must be generalized to multi-key (read from stdin) and
- * multi-map (given on command line) updates.
- */
dict = dict_open3(map_type, map_name, O_RDWR, DICT_FLAG_LOCK);
status = dict_del(dict, key);
dict_close(dict);
@@ -292,7 +410,7 @@ static int postmap_delete(const char *map_type, const char *map_name,
static NORETURN usage(char *myname)
{
- msg_fatal("usage: %s [-Ninrvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
+ msg_fatal("usage: %s [-Nfinrvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
myname);
}
@@ -304,7 +422,7 @@ int main(int argc, char **argv)
char *slash;
struct stat st;
int open_flags = O_RDWR | O_CREAT | O_TRUNC;
- int dict_flags = DICT_FLAG_DUP_WARN;
+ int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_KEY;
char *query = 0;
char *delkey = 0;
int found;
@@ -342,7 +460,7 @@ int main(int argc, char **argv)
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "Nc:d:inq:rvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "Nc:d:finq:rvw")) > 0) {
switch (ch) {
default:
usage(argv[0]);
@@ -360,6 +478,9 @@ int main(int argc, char **argv)
msg_fatal("specify only one of -q or -d");
delkey = optarg;
break;
+ case 'f':
+ dict_flags &= ~DICT_FLAG_FOLD_KEY;
+ break;
case 'i':
open_flags &= ~O_TRUNC;
break;
@@ -394,6 +515,8 @@ int main(int argc, char **argv)
if (delkey) { /* remove entry */
if (optind + 1 > argc)
usage(argv[0]);
+ if (strcmp(delkey, "-") == 0)
+ exit(postmap_deletes(VSTREAM_IN, argv + optind, argc - optind) == 0);
found = 0;
while (optind < argc) {
if ((path_name = split_at(argv[optind], ':')) != 0) {
@@ -407,6 +530,11 @@ int main(int argc, char **argv)
} else if (query) { /* query map(s) */
if (optind + 1 > argc)
usage(argv[0]);
+ if (strcmp(query, "-") == 0)
+ exit(postmap_queries(VSTREAM_IN, argv + optind, argc - optind,
+ dict_flags) == 0);
+ if (dict_flags & DICT_FLAG_FOLD_KEY)
+ lowercase(query);
while (optind < argc) {
if ((path_name = split_at(argv[optind], ':')) != 0) {
found = postmap_query(argv[optind], path_name, query);
diff --git a/gnu/dist/postfix/src/postqueue/Makefile.in b/gnu/dist/postfix/src/postqueue/Makefile.in
new file mode 100644
index 00000000000..f9470cc9a47
--- /dev/null
+++ b/gnu/dist/postfix/src/postqueue/Makefile.in
@@ -0,0 +1,80 @@
+SHELL = /bin/sh
+SRCS = postqueue.c
+OBJS = postqueue.o
+HDRS =
+TESTSRC =
+WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
+ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
+ -Wunused
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG=
+PROG = postqueue
+INC_DIR = ../../include
+LIBS = ../../lib/libglobal.a ../../lib/libutil.a
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+Makefile: Makefile.in
+ (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
+
+test: $(TESTPROG)
+
+update: ../../bin/$(PROG)
+
+../../bin/$(PROG): $(PROG)
+ cp $(PROG) ../../bin
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
+ done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+postqueue.o: postqueue.c
+postqueue.o: ../../include/sys_defs.h
+postqueue.o: ../../include/msg.h
+postqueue.o: ../../include/mymalloc.h
+postqueue.o: ../../include/clean_env.h
+postqueue.o: ../../include/vstream.h
+postqueue.o: ../../include/vbuf.h
+postqueue.o: ../../include/msg_vstream.h
+postqueue.o: ../../include/msg_syslog.h
+postqueue.o: ../../include/argv.h
+postqueue.o: ../../include/safe.h
+postqueue.o: ../../include/connect.h
+postqueue.o: ../../include/iostuff.h
+postqueue.o: ../../include/valid_hostname.h
+postqueue.o: ../../include/mail_proto.h
+postqueue.o: ../../include/attr.h
+postqueue.o: ../../include/mail_params.h
+postqueue.o: ../../include/mail_conf.h
+postqueue.o: ../../include/mail_task.h
+postqueue.o: ../../include/debug_process.h
+postqueue.o: ../../include/mail_run.h
+postqueue.o: ../../include/mail_flush.h
+postqueue.o: ../../include/flush_clnt.h
+postqueue.o: ../../include/smtp_stream.h
+postqueue.o: ../../include/vstring.h
diff --git a/gnu/dist/postfix/src/postqueue/postqueue.c b/gnu/dist/postfix/src/postqueue/postqueue.c
new file mode 100644
index 00000000000..7fba3c61d5b
--- /dev/null
+++ b/gnu/dist/postfix/src/postqueue/postqueue.c
@@ -0,0 +1,396 @@
+/*++
+/* NAME
+/* postqueue 1
+/* SUMMARY
+/* Postfix queue control
+/* SYNOPSIS
+/* \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-f\fR
+/* .br
+/* \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-p\fR
+/* .br
+/* \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
+/* DESCRIPTION
+/* The \fBpostqueue\fR program implements the Postfix user interface
+/* for queue management. It implements all the operations that are
+/* traditionally available via the \fBsendmail\fR(1) command.
+/*
+/* The following options are recognized:
+/* .IP "\fB-c \fIconfig_dir\fR"
+/* The \fBmain.cf\fR configuration file is in the named directory
+/* instead of the default configuration directory. See also the
+/* MAIL_CONFIG environment setting below.
+/* .IP \fB-f\fR
+/* Flush the queue: attempt to deliver all queued mail.
+/*
+/* This option implements the traditional \fBsendmail -q\fR command,
+/* by contacting the Postfix \fBqmgr\fR(8) daemon.
+/* .IP \fB-p\fR
+/* Produce a traditional sendmail-style queue listing.
+/*
+/* This option implements the traditional \fBmailq\fR command,
+/* by contacting the Postfix \fBshowq\fR(8) daemon.
+/* .IP "\fB-s \fIsite\fR"
+/* Schedule immediate delivery of all mail that is queued for the named
+/* \fIsite\fR. The site must be eligible for the "fast flush" service.
+/* See \fBflush\fR(8) for more information about the "fast flush"
+/* service.
+/*
+/* This option implements the traditional \fBsendmail -qR\fIsite\fR
+/* command, by contacting the Postfix \fBflush\fR(8) daemon.
+/* .IP \fB-v\fR
+/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
+/* options make the software increasingly verbose.
+/* SECURITY
+/* .ad
+/* .fi
+/* This program is designed to run with set-group ID privileges, so
+/* that it can connect to Postfix daemon processes.
+/* DIAGNOSTICS
+/* Problems are logged to \fBsyslogd\fR(8) and to the standard error
+/* stream.
+/* ENVIRONMENT
+/* .ad
+/* .fi
+/* .IP MAIL_CONFIG
+/* Directory with the \fBmain.cf\fR file.
+/*
+/* In order to avoid exploitation of set-group ID privileges, it is not
+/* possible to specify arbitrary directory names.
+/*
+/* A non-standard directory is allowed only if the name is listed in the
+/* standard \fBmain.cf\fR file, in the \fBalternate_config_directories\fR
+/* configuration parameter value.
+/*
+/* Only the super-user is allowed to specify arbitrary directory names.
+/* FILES
+/* /var/spool/postfix, mail queue
+/* /etc/postfix, configuration files
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* .IP \fBimport_environment\fR
+/* List of names of environment parameters that can be imported
+/* from non-Postfix processes.
+/* .IP \fBqueue_directory\fR
+/* Top-level directory of the Postfix queue. This is also the root
+/* directory of Postfix daemons that run chrooted.
+/* .IP \fBfast_flush_domains\fR
+/* List of domains that will receive "fast flush" service (default: all
+/* domains that this system is willing to relay mail to). This list
+/* specifies the domains that Postfix accepts in the SMTP \fBETRN\fR
+/* request and in the \fBsendmail -qR\fR command.
+/* SEE ALSO
+/* sendmail(8) sendmail-compatible user interface
+/* qmgr(8) queue manager
+/* showq(8) list mail queue
+/* flush(8) fast flush service
+/* 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 <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sysexits.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <clean_env.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <msg_syslog.h>
+#include <argv.h>
+#include <safe.h>
+#include <connect.h>
+#include <valid_hostname.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <mail_conf.h>
+#include <mail_task.h>
+#include <debug_process.h>
+#include <mail_run.h>
+#include <mail_flush.h>
+#include <flush_clnt.h>
+#include <smtp_stream.h>
+
+/* Application-specific. */
+
+ /*
+ * WARNING WARNING WARNING
+ *
+ * This software is designed to run set-gid. In order to avoid exploitation of
+ * privilege, this software should not run any external commands, nor should
+ * it take any information from the user, unless that information can be
+ * properly sanitized. To get an idea of how much information a process can
+ * inherit from a potentially hostile user, examine all the members of the
+ * process structure (typically, in /usr/include/sys/proc.h): the current
+ * directory, open files, timers, signals, environment, command line, umask,
+ * and so on.
+ */
+
+ /*
+ * Modes of operation.
+ */
+#define PQ_MODE_DEFAULT 0 /* noop */
+#define PQ_MODE_MAILQ_LIST 1 /* list mail queue */
+#define PQ_MODE_FLUSH_QUEUE 2 /* flush queue */
+#define PQ_MODE_FLUSH_SITE 3 /* flush site */
+
+ /*
+ * Silly little macros (SLMs).
+ */
+#define STR vstring_str
+
+/* show_queue - show queue status */
+
+static void show_queue(void)
+{
+ char buf[VSTREAM_BUFSIZE];
+ VSTREAM *showq;
+ int n;
+
+ /*
+ * Connect to the show queue service. Terminate silently when piping into
+ * a program that terminates early.
+ */
+ if ((showq = mail_connect(MAIL_CLASS_PUBLIC, MAIL_SERVICE_SHOWQ, BLOCKING)) != 0) {
+ while ((n = vstream_fread(showq, buf, sizeof(buf))) > 0)
+ if (vstream_fwrite(VSTREAM_OUT, buf, n) != n
+ || vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ if (vstream_fclose(showq))
+ msg_warn("close: %m");
+ }
+
+ /*
+ * When the mail system is down, the superuser can still access the queue
+ * directly. Just run the showq program in stand-alone mode.
+ */
+ else if (geteuid() == 0) {
+ ARGV *argv;
+ int stat;
+
+ msg_warn("Mail system is down -- accessing queue directly");
+ argv = argv_alloc(6);
+ argv_add(argv, MAIL_SERVICE_SHOWQ, "-u", "-S", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(argv, "-v", (char *) 0);
+ argv_terminate(argv);
+ stat = mail_run_foreground(var_daemon_dir, argv->argv);
+ argv_free(argv);
+ }
+
+ /*
+ * When the mail system is down, unprivileged users are stuck, because by
+ * design the mail system contains no set_uid programs. The only way for
+ * an unprivileged user to cross protection boundaries is to talk to the
+ * showq daemon.
+ */
+ else {
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Queue report unavailable - mail system is down");
+ }
+}
+
+/* flush_queue - force delivery */
+
+static void flush_queue(void)
+{
+
+ /*
+ * Trigger the flush queue service.
+ */
+ if (mail_flush_deferred() < 0)
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Cannot flush mail queue - mail system is down");
+}
+
+/* flush_site - flush mail for site */
+
+static void flush_site(const char *site)
+{
+ int status;
+
+ switch (status = flush_send(site)) {
+ case FLUSH_STAT_OK:
+ exit(0);
+ case FLUSH_STAT_BAD:
+ msg_fatal_status(EX_USAGE, "Invalid request: \"%s\"", site);
+ case FLUSH_STAT_FAIL:
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Cannot flush mail queue - mail system is down");
+ case FLUSH_STAT_DENY:
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Flush service is not configured for destination \"%s\"",
+ site);
+ default:
+ msg_fatal_status(EX_SOFTWARE,
+ "Unknown flush server reply status %d", status);
+ }
+}
+
+/* usage - scream and die */
+
+static NORETURN usage(void)
+{
+ msg_fatal_status(EX_USAGE, "usage: specify one of -f, -p, or -s");
+}
+
+/* main - the main program */
+
+int main(int argc, char **argv)
+{
+ struct stat st;
+ char *slash;
+ int c;
+ int fd;
+ int mode = PQ_MODE_DEFAULT;
+ char *site_to_flush = 0;
+ ARGV *import_env;
+ char *last;
+
+ /*
+ * Be consistent with file permissions.
+ */
+ umask(022);
+
+ /*
+ * To minimize confusion, make sure that the standard file descriptors
+ * are open before opening anything else. XXX Work around for 44BSD where
+ * fstat can return EBADF on an open file descriptor.
+ */
+ for (fd = 0; fd < 3; fd++)
+ if (fstat(fd, &st) == -1
+ && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
+ msg_fatal_status(EX_UNAVAILABLE, "open /dev/null: %m");
+
+ /*
+ * Initialize. Set up logging, read the global configuration file and
+ * extract configuration information. Set up signal handlers so that we
+ * can clean up incomplete output.
+ */
+ if ((slash = strrchr(argv[0], '/')) != 0)
+ argv[0] = slash + 1;
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+ msg_syslog_init(mail_task("postqueue"), LOG_PID, LOG_FACILITY);
+ set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
+
+ /*
+ * Parse JCL. This program is set-gid and must sanitize all command-line
+ * parameters. The configuration directory argument is validated by the
+ * mail configuration read routine. Don't do complex things until we have
+ * completed initializations.
+ */
+ while ((c = GETOPT(argc, argv, "c:fps:v")) > 0) {
+ switch (c) {
+ case 'c': /* non-default configuration */
+ if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
+ msg_fatal_status(EX_UNAVAILABLE, "out of memory");
+ break;
+ case 'f': /* flush queue */
+ if (mode != PQ_MODE_DEFAULT)
+ usage();
+ mode = PQ_MODE_FLUSH_QUEUE;
+ break;
+ case 'p': /* traditional mailq */
+ if (mode != PQ_MODE_DEFAULT)
+ usage();
+ mode = PQ_MODE_MAILQ_LIST;
+ break;
+ break;
+ case 's': /* flush site */
+ if (mode != PQ_MODE_DEFAULT)
+ usage();
+ mode = PQ_MODE_FLUSH_SITE;
+ site_to_flush = optarg;
+ break;
+ case 'v':
+ msg_verbose++;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /*
+ * Further initialization...
+ */
+ mail_conf_read();
+
+ /*
+ * Strip the environment so we don't have to trust the C library.
+ */
+ import_env = argv_split(var_import_environ, ", \t\r\n");
+ clean_env(import_env->argv);
+ argv_free(import_env);
+
+ if (chdir(var_queue_dir))
+ msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);
+
+ signal(SIGPIPE, SIG_IGN);
+
+ /* End of initializations. */
+
+ /*
+ * Further input validation.
+ */
+ if (site_to_flush != 0) {
+ if (*site_to_flush == '['
+ && *(last = site_to_flush + strlen(site_to_flush) - 1) == ']') {
+ *last = 0;
+ if (!valid_hostaddr(site_to_flush + 1, DONT_GRIPE))
+ site_to_flush = 0;
+ *last = ']';
+ } else {
+ if (!valid_hostname(site_to_flush, DONT_GRIPE)
+ && !valid_hostaddr(site_to_flush, DONT_GRIPE))
+ site_to_flush = 0;
+ }
+ if (site_to_flush == 0)
+ msg_fatal_status(EX_USAGE,
+ "Cannot flush mail queue - invalid destination: \"%.100s%s\"",
+ optarg, strlen(optarg) > 100 ? "..." : "");
+ }
+
+ /*
+ * Start processing.
+ */
+ switch (mode) {
+ default:
+ msg_panic("unknown operation mode: %d", mode);
+ /* NOTREACHED */
+ case PQ_MODE_MAILQ_LIST:
+ show_queue();
+ exit(0);
+ break;
+ case PQ_MODE_FLUSH_SITE:
+ flush_site(site_to_flush);
+ exit(0);
+ break;
+ case PQ_MODE_FLUSH_QUEUE:
+ flush_queue();
+ exit(0);
+ break;
+ case PQ_MODE_DEFAULT:
+ usage();
+ /* NOTREACHED */
+ }
+}
diff --git a/gnu/dist/postfix/src/postsuper/Makefile.in b/gnu/dist/postfix/src/postsuper/Makefile.in
index 3ff67a4d3df..4afa39a5434 100644
--- a/gnu/dist/postfix/src/postsuper/Makefile.in
+++ b/gnu/dist/postfix/src/postsuper/Makefile.in
@@ -66,7 +66,10 @@ postsuper.o: ../../include/vstring.h
postsuper.o: ../../include/safe.h
postsuper.o: ../../include/set_ugid.h
postsuper.o: ../../include/argv.h
+postsuper.o: ../../include/vstring_vstream.h
+postsuper.o: ../../include/sane_fsops.h
postsuper.o: ../../include/mail_task.h
postsuper.o: ../../include/mail_conf.h
postsuper.o: ../../include/mail_params.h
postsuper.o: ../../include/mail_queue.h
+postsuper.o: ../../include/mail_open_ok.h
diff --git a/gnu/dist/postfix/src/postsuper/postsuper.c b/gnu/dist/postfix/src/postsuper/postsuper.c
index b762d0849c7..660cf9ca718 100644
--- a/gnu/dist/postfix/src/postsuper/postsuper.c
+++ b/gnu/dist/postfix/src/postsuper/postsuper.c
@@ -2,37 +2,109 @@
/* NAME
/* postsuper 1
/* SUMMARY
-/* Postfix super intendent
+/* Postfix superintendent
/* SYNOPSIS
/* .fi
-/* \fBpostsuper\fR [\fB-p\fR] [\fB-s\fR] [\fB-v\fR] [\fIdirectory ...\fR]
+/* \fBpostsuper\fR [\fB-psv\fR] [\fB-d \fIqueue_id\fR]
+/* [\fB-r \fIqueue_id\fR] [\fIdirectory ...\fR]
/* DESCRIPTION
-/* The \fBpostsuper\fR command does small maintenance jobs on the named
-/* Postfix queue directories (default: all).
-/* Directory names are relative to the Postfix top-level queue directory.
+/* The \fBpostsuper\fR command does maintenance jobs on the Postfix
+/* queue. Use of the command is restricted to the super-user.
/*
/* By default, \fBpostsuper\fR performs the operations requested with the
-/* \fB-s\fR and \fB-p\fR command-line options.
-/* \fBpostsuper\fR always tries to remove objects that are neither files
-/* nor directories. Use of this command is restricted to the super-user.
+/* \fB-s\fR and \fB-p\fR command-line options on all Postfix queue
+/* directories - this includes the \fBincoming\fR, \fBactive\fR and
+/* \fBdeferred\fR directories with mail files and the \fBbounce\fR,
+/* \fBdefer\fR and \fBflush\fR directories with log files.
/*
/* Options:
-/* .IP \fB-s\fR
-/* Structure check. Move queue files that are in the wrong place
-/* in the file system hierarchy and remove subdirectories that are
-/* no longer needed. File rearrangements are necessary after a change
-/* in the \fBhash_queue_names\fR and/or \fBhash_queue_depth\fR
-/* configuration parameters. It is highly recommended to run this
-/* check once before Postfix startup.
+/* .IP "\fB-d \fIqueue_id\fR"
+/* Delete one message with the named queue ID from the named
+/* mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+/* \fBdeferred\fR).
+/* If a \fIqueue_id\fR of \fB-\fR is specified, the program reads
+/* queue IDs from standard input.
+/* .sp
+/* Specify \fB-d ALL\fR to remove all messages; for example, specify
+/* \fB-d ALL deferred\fR to delete mail in the \fBdeferred\fR queue.
+/* As a safety measure, the word \fBALL\fR must be specified in upper
+/* case.
+/* .sp
+/* .ft B
+/* Postfix queue IDs are reused.
+/* There is a very small possibility that postsuper deletes the
+/* wrong message file when it is executed while the Postfix mail
+/* system is running.
+/* .ft R
+/* .sp
+/* The scenario is as follows:
+/* .RS
+/* .IP 1)
+/* The Postfix queue manager deletes the message that \fBpostsuper\fR
+/* is supposed to delete, because Postfix is finished with the
+/* message.
+/* .IP 2)
+/* New mail arrives, and the new message is given the same queue ID
+/* as the message that \fBpostsuper\fR is supposed to delete.
+/* The probability for reusing a deleted queue ID is about 1 in 2**15
+/* (the number of different microsecond values that the system clock
+/* can distinguish within a second).
+/* .IP 3)
+/* \fBpostsuper\fR deletes the new message, instead of the old
+/* message that it should have deleted.
+/* .RE
/* .IP \fB-p\fR
-/* Purge stale files (files that are left over after system or
-/* software crashes).
+/* Purge old temporary files that are left over after system or
+/* software crashes.
+/* .IP "\fB-r \fIqueue_id\fR"
+/* Requeue the message with the named queue ID from the named
+/* mail queue(s) (default: \fBincoming\fR, \fBactive\fR and
+/* \fBdeferred\fR).
+/* To requeue multiple messages, specify multiple \fB-r\fR
+/* command-line options.
+/* Alternatively, if a \fIqueue_id\fR of \fB-\fR is specified,
+/* the program reads queue IDs from standard input.
+/* .sp
+/* Specify \fB-r ALL\fR to requeue all messages. As a safety
+/* measure, the word \fBALL\fR must be specified in upper case.
+/* .sp
+/* A requeued message is moved to the \fBmaildrop\fR queue, from
+/* where it is copied by the pickup daemon to a new file whose name
+/* is guaranteed to match the new queue file inode number. The
+/* new queue file is subjected again to mail address rewriting and
+/* substitution. This is useful when rewriting rules or virtual
+/* mappings have changed.
+/* .sp
+/* Postfix queue IDs are reused.
+/* There is a very small possibility that \fBpostsuper\fR requeues
+/* the wrong message file when it is executed while the Postfix mail
+/* system is running, but no harm should be done.
+/* .IP \fB-s\fR
+/* Structure check and structure repair. It is highly recommended
+/* to perform this operation once before Postfix startup.
+/* .RS
+/* .IP \(bu
+/* Rename files whose name does not match the message file inode
+/* number. This operation is necessary after restoring a mail queue
+/* from a different machine, or from backup media.
+/* .IP \(bu
+/* Move queue files that are in the wrong place in the file system
+/* hierarchy and remove subdirectories that are no longer needed.
+/* File position rearrangements are necessary after a change in the
+/* \fBhash_queue_names\fR and/or \fBhash_queue_depth\fR
+/* configuration parameters.
+/* .RE
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose.
/* DIAGNOSTICS
/* Problems are reported to the standard error stream and to
/* \fBsyslogd\fR.
+/*
+/* \fBpostsuper\fR reports the number of messages deleted with \fB-d\fR,
+/* the number of messages requeued with \fB-r\fR, and the number of
+/* messages whose queue file name was fixed with \fB-s\fR. The report
+/* is written to the standard error stream and to \fBsyslogd\fR.
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
@@ -62,6 +134,7 @@
#include <stdlib.h>
#include <errno.h>
#include <string.h>
+#include <signal.h>
#include <stdio.h> /* remove() */
/* Utility library. */
@@ -76,6 +149,8 @@
#include <safe.h>
#include <set_ugid.h>
#include <argv.h>
+#include <vstring_vstream.h>
+#include <sane_fsops.h>
/* Global library. */
@@ -83,6 +158,7 @@
#include <mail_conf.h>
#include <mail_params.h>
#include <mail_queue.h>
+#include <mail_open_ok.h>
/* Application-specific. */
@@ -91,6 +167,10 @@
#define ACTION_STRUCT (1<<0) /* fix file organization */
#define ACTION_PURGE (1<<1) /* purge old temp files */
+#define ACTION_DELETE_ONE (1<<2) /* delete named queue file(s) */
+#define ACTION_DELETE_ALL (1<<3) /* delete all queue file(s) */
+#define ACTION_REQUEUE_ONE (1<<4) /* requeue named queue file(s) */
+#define ACTION_REQUEUE_ALL (1<<5) /* requeue all queue file(s) */
#define ACTION_DEFAULT (ACTION_STRUCT | ACTION_PURGE)
@@ -121,21 +201,276 @@ static struct queue_info queue_info[] = {
0,
};
-/* super - check queue file location and clean up */
+ /*
+ * Directories with per-message meta files.
+ */
+const char *log_queue_names[] = {
+ MAIL_QUEUE_BOUNCE,
+ MAIL_QUEUE_DEFER,
+ 0,
+};
+
+ /*
+ * Cruft that we append to a file name when a queue ID is named after the
+ * message file inode number. This cruft must not pass mail_queue_id_ok() so
+ * that the queue manager will ignore it, should people be so unwise as to
+ * run this operation on a live mail system.
+ */
+#define SUFFIX "#FIX"
+#define SUFFIX_LEN 4
+
+ /*
+ * Grr. These counters are global, because C only has clumsy ways to return
+ * multiple results from a function.
+ */
+static int message_requeued = 0; /* requeued messages */
+static int message_deleted = 0; /* deleted messages */
+static int inode_fixed = 0; /* queue id matched to inode number */
+static int inode_mismatch = 0; /* queue id inode mismatch */
+static int position_mismatch = 0; /* file position mismatch */
+
+ /*
+ * Silly little macros. These translate arcane expressions into something
+ * more at a conceptual level.
+ */
+#define MESSAGE_QUEUE(qp) ((qp)->perms == MAIL_QUEUE_STAT_READY)
+#define READY_MESSAGE(st) (((st).st_mode & S_IRWXU) == MAIL_QUEUE_STAT_READY)
+
+/* find_queue_info - look up expected permissions field by queue name */
+
+static struct queue_info *find_queue_info(const char *queue_name)
+{
+ struct queue_info *qp;
+
+ for (qp = queue_info; qp->name; qp++)
+ if (strcmp(queue_name, qp->name) == 0)
+ return (qp);
+ msg_fatal("invalid directory name: %s", queue_name);
+}
+
+/* postremove - remove file with extreme prejudice */
+
+static int postremove(const char *path)
+{
+ int ret;
+
+ if ((ret = remove(path)) < 0) {
+ if (errno != ENOENT)
+ msg_fatal("remove file %s: %m", path);
+ } else {
+ if (msg_verbose)
+ msg_info("removed file %s", path);
+ }
+ return (ret);
+}
+
+/* postrename - rename file with extreme prejudice */
+
+static int postrename(const char *old, const char *new)
+{
+ int ret;
+
+ if ((ret = sane_rename(old, new)) < 0) {
+ if (errno != ENOENT
+ || mail_queue_mkdirs(new) < 0
+ || sane_rename(old, new) < 0)
+ if (errno != ENOENT)
+ msg_fatal("rename file %s as %s: %m", old, new);
+ } else {
+ if (msg_verbose)
+ msg_info("renamed file %s as %s", old, new);
+ }
+ return (ret);
+}
+
+/* postrmdir - remove directory with extreme prejudice */
+
+static int postrmdir(const char *path)
+{
+ int ret;
+
+ if ((ret = rmdir(path)) < 0) {
+ if (errno != ENOENT)
+ msg_fatal("remove directory %s: %m", path);
+ } else {
+ if (msg_verbose)
+ msg_info("remove directory %s", path);
+ }
+ return (ret);
+}
+
+/* delete_one - delete one message instance and all its associated files */
+
+static int delete_one(const char **queue_names, const char *queue_id)
+{
+ struct stat st;
+ const char **msg_qpp;
+ const char **log_qpp;
+ const char *msg_path;
+ VSTRING *log_path_buf;
+ int found;
+ int tries;
+
+ /*
+ * Sanity check. No early returns beyond this point.
+ */
+ if (!mail_queue_id_ok(queue_id)) {
+ msg_warn("invalid mail queue id: %s", queue_id);
+ return (0);
+ }
+ log_path_buf = vstring_alloc(100);
+
+ /*
+ * Skip meta file directories. Delete defer or bounce logfiles before
+ * deleting the corresponding message file, and only if the message file
+ * exists. This minimizes but does not eliminate a race condition with
+ * queue ID reuse which results in deleting the wrong files.
+ */
+ for (found = 0, tries = 0; found == 0 && tries < 2; tries++) {
+ for (msg_qpp = queue_names; *msg_qpp != 0; msg_qpp++) {
+ if (!MESSAGE_QUEUE(find_queue_info(*msg_qpp)))
+ continue;
+ if (mail_open_ok(*msg_qpp, queue_id, &st, &msg_path) != MAIL_OPEN_YES)
+ continue;
+ for (log_qpp = log_queue_names; *log_qpp != 0; log_qpp++)
+ postremove(mail_queue_path(log_path_buf, *log_qpp, queue_id));
+ if (postremove(msg_path) == 0) {
+ found = 1;
+ msg_info("%s: removed", queue_id);
+ break;
+ } /* else: maybe lost a race */
+ }
+ }
+ vstring_free(log_path_buf);
+ return (found);
+}
+
+/* requeue_one - requeue one message instance and delete its logfiles */
+
+static int requeue_one(const char **queue_names, const char *queue_id)
+{
+ struct stat st;
+ const char **msg_qpp;
+ const char *old_path;
+ VSTRING *new_path_buf;
+ int found;
+ int tries;
+
+ /*
+ * Sanity check. No early returns beyond this point.
+ */
+ if (!mail_queue_id_ok(queue_id)) {
+ msg_warn("invalid mail queue id: %s", queue_id);
+ return (0);
+ }
+ new_path_buf = vstring_alloc(100);
+
+ /*
+ * Skip meta file directories. Like the mass requeue operation, we not
+ * delete defer or bounce logfiles, to avoid losing a race where the
+ * queue manager decides to bounce mail after all recipients have been
+ * tried.
+ */
+ for (found = 0, tries = 0; found == 0 && tries < 2; tries++) {
+ for (msg_qpp = queue_names; *msg_qpp != 0; msg_qpp++) {
+ if (strcmp(*msg_qpp, MAIL_QUEUE_MAILDROP) == 0)
+ continue;
+ if (!MESSAGE_QUEUE(find_queue_info(*msg_qpp)))
+ continue;
+ if (mail_open_ok(*msg_qpp, queue_id, &st, &old_path) != MAIL_OPEN_YES)
+ continue;
+ (void) mail_queue_path(new_path_buf, MAIL_QUEUE_MAILDROP, queue_id);
+ if (postrename(old_path, STR(new_path_buf)) == 0) {
+ msg_info("%s: requeued", queue_id);
+ found = 1;
+ break;
+ } /* else: maybe lost a race */
+ }
+ }
+ vstring_free(new_path_buf);
+ return (found);
+}
+
+/* operate_stream - operate on queue IDs given on stream */
+
+static int operate_stream(VSTREAM *fp,
+ int (*operator) (const char **, const char *),
+ const char **queues)
+{
+ VSTRING *buf = vstring_alloc(20);
+ int found = 0;
+
+ while (vstring_get_nonl(buf, fp) != VSTREAM_EOF)
+ found += operator(queues, STR(buf));
+
+ vstring_free(buf);
+ return (found);
+}
+
+/* fix_queue_id - make message queue ID match inode number */
+
+static int fix_queue_id(const char *actual_path, const char *actual_queue,
+ const char *actual_id, ino_t inum)
+{
+ VSTRING *old_path = vstring_alloc(10);
+ VSTRING *new_path = vstring_alloc(10);
+ VSTRING *new_id = vstring_alloc(10);
+ const char **log_qpp;
+ int ret;
+
+ /*
+ * Create the new queue ID from the existing time digits and from the new
+ * inode number. Since we are renaming multiple files, the new name must
+ * be deterministic so that we can recover even when the renaming
+ * operation is interrupted in the middle.
+ */
+ vstring_sprintf(new_id, "%.5s%lX", actual_id, (unsigned long) inum);
+
+ /*
+ * Rename logfiles before renaming the message file, so that we can
+ * recover when a previous attempt was interrupted.
+ */
+ for (log_qpp = log_queue_names; *log_qpp; log_qpp++) {
+ mail_queue_path(old_path, *log_qpp, actual_id);
+ mail_queue_path(new_path, *log_qpp, STR(new_id));
+ vstring_strcat(new_path, SUFFIX);
+ postrename(STR(old_path), STR(new_path));
+ }
+
+ /*
+ * Rename the message file last, so that we know that we are done with
+ * this message and with all its logfiles.
+ */
+ mail_queue_path(new_path, actual_queue, STR(new_id));
+ vstring_strcat(new_path, SUFFIX);
+ ret = postrename(actual_path, STR(new_path));
+
+ /*
+ * Clean up.
+ */
+ vstring_free(old_path);
+ vstring_free(new_path);
+ vstring_free(new_id);
+
+ return (ret);
+}
+
+/* super - check queue structure, clean up, do wild-card operations */
-static void super(char **queues, int action)
+static void super(const char **queues, int action)
{
ARGV *hash_queue_names = argv_split(var_hash_queue_names, " \t\r\n,");
VSTRING *actual_path = vstring_alloc(10);
VSTRING *wanted_path = vstring_alloc(10);
struct stat st;
- char *queue_name;
+ const char *queue_name;
SCAN_DIR *info;
char *path;
int actual_depth;
int wanted_depth;
char **cpp;
struct queue_info *qp;
+ unsigned long inum;
/*
* Make sure every file is in the right place, clean out stale files, and
@@ -151,12 +486,7 @@ static void super(char **queues, int action)
* file permissions to look for, and whether or not it is desirable
* to step into subdirectories.
*/
- for (qp = queue_info; /* void */ ; qp++) {
- if (qp->name == 0)
- msg_fatal("unknown queue name: %s", queue_name);
- if (strcmp(qp->name, queue_name) == 0)
- break;
- }
+ qp = find_queue_info(queue_name);
for (cpp = hash_queue_names->argv; /* void */ ; cpp++) {
if (*cpp == 0) {
wanted_depth = 0;
@@ -189,12 +519,8 @@ static void super(char **queues, int action)
if ((path = scan_dir_next(info)) == 0) {
if (actual_depth == 0)
break;
- if (actual_depth > wanted_depth) {
- if (rmdir(scan_dir_path(info)) < 0 && errno != ENOENT)
- msg_warn("remove %s: %m", scan_dir_path(info));
- else if (msg_verbose)
- msg_info("remove %s", scan_dir_path(info));
- }
+ if (actual_depth > wanted_depth)
+ postrmdir(scan_dir_path(info));
scan_dir_pop(info);
actual_depth--;
continue;
@@ -212,66 +538,175 @@ static void super(char **queues, int action)
}
/*
- * See if this is a stale file or some non-file object. Be
- * careful not to delete bounce or defer logs just because they
- * are more than a couple days old.
+ * From here on we need to keep track of operations that
+ * invalidate or revalidate the actual_path and path variables,
+ * otherwise we can hit the wrong files.
*/
vstring_sprintf(actual_path, "%s/%s", scan_dir_path(info), path);
if (stat(STR(actual_path), &st) < 0)
continue;
+
+ /*
+ * Remove alien directories. If maildrop is world writable, then
+ * we cannot abort just because we cannot remove someone's
+ * directory.
+ */
if (S_ISDIR(st.st_mode)) {
- if (rmdir(STR(actual_path)) < 0 && errno != ENOENT)
- msg_warn("remove subdirectory %s: %m", STR(actual_path));
- else if (msg_verbose)
- msg_info("remove subdirectory %s", STR(actual_path));
+ if (rmdir(STR(actual_path)) < 0) {
+ if (errno != ENOENT)
+ msg_warn("remove subdirectory %s: %m", STR(actual_path));
+ } else {
+ if (msg_verbose)
+ msg_info("remove subdirectory %s", STR(actual_path));
+ }
+ /* No further work on this object is possible. */
+ continue;
+ }
+
+ /*
+ * Mass deletion. We count the deletion of mail that this system
+ * has taken responsibility for. XXX This option does not use the
+ * mail_queue(3) API, so that it can be run on a queue that does
+ * not have files in the "right" place. It would be terribly
+ * inefficient to first have to rename files into place before
+ * deleting them.
+ */
+ if (action & ACTION_DELETE_ALL) {
+ if (postremove(STR(actual_path)) == 0)
+ if (MESSAGE_QUEUE(qp) && READY_MESSAGE(st))
+ message_deleted++;
+ /* No further work on this object is possible. */
continue;
}
+
+ /*
+ * Remove non-file objects and old temporary files. Be careful
+ * not to delete bounce or defer logs just because they are more
+ * than a couple days old.
+ */
if (!S_ISREG(st.st_mode)
- || ((action & ACTION_PURGE) != 0 &&
- (st.st_mode & S_IRWXU) != qp->perms
+ || ((action & ACTION_PURGE) != 0
+ && MESSAGE_QUEUE(qp)
+ && !READY_MESSAGE(st)
&& time((time_t *) 0) > st.st_mtime + MAX_TEMP_AGE)) {
- if (remove(STR(actual_path)) < 0 && errno != ENOENT)
- msg_warn("remove %s: %m", STR(actual_path));
- else if (msg_verbose)
- msg_info("remove %s", STR(actual_path));
+ (void) postremove(STR(actual_path));
+ /* No further work on this object is possible. */
continue;
}
/*
+ * Skip temporary message files that aren't old enough.
+ */
+ if (MESSAGE_QUEUE(qp) && !READY_MESSAGE(st))
+ /* No further work on this object is possible. */
+ continue;
+
+ /*
+ * Fix queueid#FIX names that were left from a previous pass over
+ * the queue where message queue file names were matched to their
+ * inode number. This requires that the file is already in the
+ * proper directory so that we only have to strip the suffix.
+ * Make sure that the name minus suffix is well formed and that
+ * the name matches the file inode number.
+ */
+ if (strcmp(path + (strlen(path) - SUFFIX_LEN), SUFFIX) == 0) {
+ path[strlen(path) - SUFFIX_LEN] = 0; /* XXX */
+ if (!mail_queue_id_ok(path)) {
+ msg_warn("bogus file name: %s", STR(actual_path));
+ continue;
+ }
+ if (MESSAGE_QUEUE(qp)) {
+ if (sscanf(path + 5, "%lx", &inum) != 1) {
+ msg_warn("bogus file name: %s", STR(actual_path));
+ continue;
+ }
+ if (inum != (unsigned long) st.st_ino) {
+ msg_warn("name/inode mismatch: %s", STR(actual_path));
+ continue;
+ }
+ }
+ vstring_strncpy(wanted_path, STR(actual_path),
+ strlen(STR(actual_path)) - SUFFIX_LEN);
+ if (postrename(STR(actual_path), STR(wanted_path)) < 0) {
+ /* No further work on this object is possible. */
+ continue;
+ } else {
+ if (MESSAGE_QUEUE(qp))
+ inode_fixed++;
+ vstring_strcpy(actual_path, STR(wanted_path));
+ /* At this point, path and actual_path are revalidated. */
+ }
+ }
+
+ /*
* Skip over files with illegal names. The library routines
* refuse to operate on them.
*/
- if (mail_queue_id_ok(path) == 0)
+ if (!mail_queue_id_ok(path)) {
+ msg_warn("bogus file name: %s", STR(actual_path));
continue;
+ }
/*
- * Skip temporary files that aren't old enough.
+ * Mass requeuing. The pickup daemon will copy requeued mail to a
+ * new queue file, so that address rewriting is applied again.
+ * XXX This option does not use the mail_queue(3) API, so that it
+ * can be run on a queue that does not have the files in the
+ * "right" place. It would be terribly inefficient to first have
+ * to rename files into place before requeuing them. Like the
+ * requeue_one() routine, this code does not touch logfiles.
*/
- if (qp->perms == MAIL_QUEUE_STAT_READY
- && (st.st_mode & S_IRWXU) != qp->perms)
+ if ((action & ACTION_REQUEUE_ALL)
+ && MESSAGE_QUEUE(qp)
+ && strcmp(queue_name, MAIL_QUEUE_MAILDROP) != 0) {
+ (void) mail_queue_path(wanted_path, MAIL_QUEUE_MAILDROP, path);
+ if (postrename(STR(actual_path), STR(wanted_path)) == 0)
+ message_requeued++;
+ /* At this point, path and actual_path are invalidated. */
continue;
+ }
+
+ /*
+ * See if the file name matches the file inode number. Skip meta
+ * file directories. This option requires that meta files be put
+ * into their proper place before queue files, so that we can
+ * rename queue files and meta files at the same time. Mis-named
+ * files are renamed to newqueueid#FIX on the first pass, and
+ * upon the second pass the #FIX is stripped off the name. Of
+ * course we have to be prepared that the program is interrupted
+ * before it completes, so any left-over newqueueid#FIX files
+ * have to be handled properly. XXX This option does not use the
+ * mail_queue(3) API for message queue files, so that it can be
+ * run on a queue that does not have message queue files in the
+ * "right" place. It would be terribly inefficient to first have
+ * to rename files into place before fixing the file names.
+ */
+ if ((action & ACTION_STRUCT) != 0 && MESSAGE_QUEUE(qp)) {
+ if (sscanf(path + 5, "%lx", &inum) != 1) {
+ msg_warn("bogus file name: %s", STR(actual_path));
+ continue;
+ }
+ if (inum != (unsigned long) st.st_ino) {
+ inode_mismatch++; /* before we fix */
+ fix_queue_id(STR(actual_path), queue_name, path, st.st_ino);
+ /* At this point, path and actual_path are invalidated. */
+ continue;
+ }
+ }
/*
* See if this file sits in the right place in the file system
* hierarchy. Its place may be wrong after a change to the
- * hash_queue_{names,depth} parameter settings. The implied
- * mkdir() operation is the main reason for this command to run
- * with postfix privilege. The mail_queue_mkdirs() routine could
- * be fixed to use the "right" privilege, but it is a good idea
- * to do everything with the postfix owner privileges regardless,
- * in order to limit the amount of damage that we can do.
+ * hash_queue_{names,depth} parameter settings.
*/
- (void) mail_queue_path(wanted_path, queue_name, path);
- if (strcmp(STR(actual_path), STR(wanted_path)) != 0) {
- if (rename(STR(actual_path), STR(wanted_path)) < 0)
- if (errno != ENOENT
- || mail_queue_mkdirs(STR(wanted_path)) < 0
- || rename(STR(actual_path), STR(wanted_path)) < 0)
- msg_fatal("rename %s to %s: %m", STR(actual_path),
- STR(wanted_path));
- if (msg_verbose)
- msg_info("rename %s to %s", STR(actual_path),
- STR(wanted_path));
+ if (action & ACTION_STRUCT) {
+ (void) mail_queue_path(wanted_path, queue_name, path);
+ if (strcmp(STR(actual_path), STR(wanted_path)) != 0) {
+ position_mismatch++; /* before we fix */
+ (void) postrename(STR(actual_path), STR(wanted_path));
+ /* At this point, path and actual_path are invalidated. */
+ continue;
+ }
}
}
scan_dir_close(info);
@@ -285,26 +720,43 @@ static void super(char **queues, int action)
argv_free(hash_queue_names);
}
+/* fatal_exit - print warning if queue fix is incomplete */
+
+static void fatal_exit(void)
+{
+ if (inode_mismatch > 0 || inode_fixed > 0 || position_mismatch > 0)
+ msg_fatal("OPERATION INCOMPLETE -- RERUN COMMAND TO FIX THE QUEUE FIRST");
+}
+
+/* interrupted - signal handler */
+
+static void interrupted(int unused_sig)
+{
+ fatal_exit();
+}
+
int main(int argc, char **argv)
{
int fd;
struct stat st;
char *slash;
- int debug_me = 0;
int action = 0;
- char **queues;
+ const char **queues;
int c;
+ ARGV *requeue_names = 0;
+ ARGV *delete_names = 0;
+ char **cpp;
/*
* Defaults.
*/
static char *default_queues[] = {
+ MAIL_QUEUE_DEFER, /* before message directories */
+ MAIL_QUEUE_BOUNCE, /* before message directories */
MAIL_QUEUE_MAILDROP,
MAIL_QUEUE_INCOMING,
MAIL_QUEUE_ACTIVE,
MAIL_QUEUE_DEFERRED,
- MAIL_QUEUE_DEFER,
- MAIL_QUEUE_BOUNCE,
MAIL_QUEUE_FLUSH,
0,
};
@@ -331,8 +783,6 @@ int main(int argc, char **argv)
*/
if (safe_getenv(CONF_ENV_VERB))
msg_verbose = 1;
- if (safe_getenv(CONF_ENV_DEBUG))
- debug_me = 1;
/*
* Initialize. Set up logging, read the global configuration file and
@@ -349,6 +799,17 @@ int main(int argc, char **argv)
msg_fatal("chdir %s: %m", var_queue_dir);
/*
+ * Be sure to log a warning if we do not finish structural repair. Maybe
+ * we should have an fsck-style "clean" flag so Postfix will not start
+ * with a broken queue.
+ */
+ signal(SIGHUP, interrupted);
+ signal(SIGINT, interrupted);
+ signal(SIGQUIT, interrupted);
+ signal(SIGTERM, interrupted);
+ msg_cleanup(fatal_exit);
+
+ /*
* All file/directory updates must be done as the mail system owner. This
* is because Postfix daemons manipulate the queue with those same
* privileges, so directories must be created with the right ownership.
@@ -359,22 +820,38 @@ int main(int argc, char **argv)
* a non-root user limits the damage to the already compromised mail
* owner.
*/
+ if (getuid())
+ msg_fatal("use of this command is reserved for the super-user");
set_ugid(var_owner_uid, var_owner_gid);
/*
* Parse JCL.
*/
- while ((c = GETOPT(argc, argv, "spv")) > 0) {
+ while ((c = GETOPT(argc, argv, "d:pr:sv")) > 0) {
switch (c) {
default:
- msg_fatal("usage: %s [-s (fix structure)] [-p (purge stale files)]",
+ msg_fatal("usage: %s [-d queue_id (delete)] [-p (purge temporary files)] [-r queue_id (requeue)] [-s (structure fix)]",
argv[0]);
- case 's':
- action |= ACTION_STRUCT;
+ case 'd':
+ if (delete_names == 0)
+ delete_names = argv_alloc(1);
+ argv_add(delete_names, optarg, (char *) 0);
+ action |= (strcmp(optarg, "ALL") == 0 ?
+ ACTION_DELETE_ALL : ACTION_DELETE_ONE);
break;
case 'p':
action |= ACTION_PURGE;
break;
+ case 'r':
+ if (requeue_names == 0)
+ requeue_names = argv_alloc(1);
+ argv_add(requeue_names, optarg, (char *) 0);
+ action |= (strcmp(optarg, "ALL") == 0 ?
+ ACTION_REQUEUE_ALL : ACTION_REQUEUE_ONE);
+ break;
+ case 's':
+ action |= ACTION_STRUCT;
+ break;
case 'v':
msg_verbose++;
break;
@@ -384,15 +861,100 @@ int main(int argc, char **argv)
/*
* Execute the explicitly specified (or default) action, on the
* explicitly specified (or default) queues.
+ *
+ * XXX Work around gcc const brain damage.
*/
if (action == 0)
action = ACTION_DEFAULT;
if (argv[optind] == 0)
- queues = default_queues;
+ queues = (const char **) default_queues;
else
- queues = argv + optind;
+ queues = (const char **) argv + optind;
+
+#define ACTIONS_BY_QUEUE_ID (ACTION_DELETE_ONE | ACTION_REQUEUE_ONE)
+
+ /*
+ * Basic queue maintenance, as well as mass deletion, mass requeuing, and
+ * mass name-to-inode fixing. This ensures that queue files are in the
+ * right place before the file-by-name operations are done.
+ */
+ if (action & ~ACTIONS_BY_QUEUE_ID)
+ super(queues, action & ~ACTIONS_BY_QUEUE_ID);
- super(queues, action);
+ /*
+ * If any file names needed changing to match the message file inode
+ * number, those files were named newqeueid#FIX. We need a second pass to
+ * strip the suffix from the new queue ID.
+ */
+ if (inode_mismatch > 0)
+ super(queues, 0);
+
+ /*
+ * Delete queue files by name. This must not be done when queue file
+ * names have changed names as a result of inode number mismatches,
+ * because we could be deleting the wrong message.
+ */
+ if (action & ACTION_DELETE_ONE) {
+ if (inode_mismatch > 0 || inode_fixed > 0) {
+ msg_error("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
+ msg_fatal("CHECK YOUR QUEUE IDS AND RE-ISSUE THE COMMAND");
+ }
+ argv_terminate(delete_names);
+ for (cpp = delete_names->argv; *cpp; cpp++) {
+ if (strcmp(*cpp, "ALL") == 0)
+ continue;
+ if (strcmp(*cpp, "-") == 0)
+ message_deleted +=
+ operate_stream(VSTREAM_IN, delete_one, queues);
+ else
+ message_deleted += delete_one(queues, *cpp);
+ }
+ }
+
+ /*
+ * Requeue queue files by name. This must not be done when queue file
+ * names have changed names as a result of inode number mismatches,
+ * because we could be requeuing the wrong message.
+ */
+ if (action & ACTION_REQUEUE_ONE) {
+ if (inode_mismatch > 0 || inode_fixed > 0) {
+ msg_error("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
+ msg_fatal("CHECK YOUR QUEUE IDS AND RE-ISSUE THE COMMAND");
+ }
+ argv_terminate(requeue_names);
+ for (cpp = requeue_names->argv; *cpp; cpp++) {
+ if (strcmp(*cpp, "ALL") == 0)
+ continue;
+ if (strcmp(*cpp, "-") == 0)
+ message_requeued +=
+ operate_stream(VSTREAM_IN, requeue_one, queues);
+ else
+ message_requeued += requeue_one(queues, *cpp);
+ }
+ }
+
+ /*
+ * Report.
+ */
+ if (message_requeued > 0)
+ msg_info("Requeued: %d message%s", message_requeued,
+ message_requeued > 1 ? "s" : "");
+ if (message_deleted > 0)
+ msg_info("Deleted: %d message%s", message_deleted,
+ message_deleted > 1 ? "s" : "");
+ if (inode_fixed > 0)
+ msg_info("Renamed to match inode number: %d message%s", inode_fixed,
+ inode_fixed > 1 ? "s" : "");
+ if (inode_mismatch > 0 || inode_fixed > 0)
+ msg_warn("QUEUE FILE NAMES WERE CHANGED TO MATCH INODE NUMBERS");
+
+ /*
+ * Clean up.
+ */
+ if (requeue_names)
+ argv_free(requeue_names);
+ if (delete_names)
+ argv_free(delete_names);
exit(0);
}
diff --git a/gnu/dist/postfix/src/qmgr/Makefile.in b/gnu/dist/postfix/src/qmgr/Makefile.in
index c37f1d78fd0..19f24d7e67e 100644
--- a/gnu/dist/postfix/src/qmgr/Makefile.in
+++ b/gnu/dist/postfix/src/qmgr/Makefile.in
@@ -73,6 +73,8 @@ qmgr.o: ../../include/mail_conf.h
qmgr.o: ../../include/mail_params.h
qmgr.o: ../../include/mail_proto.h
qmgr.o: ../../include/iostuff.h
+qmgr.o: ../../include/attr.h
+qmgr.o: ../../include/mail_flow.h
qmgr.o: ../../include/master_proto.h
qmgr.o: ../../include/mail_server.h
qmgr.o: qmgr.h
@@ -133,9 +135,11 @@ qmgr_deliver.o: ../../include/events.h
qmgr_deliver.o: ../../include/iostuff.h
qmgr_deliver.o: ../../include/mail_queue.h
qmgr_deliver.o: ../../include/mail_proto.h
+qmgr_deliver.o: ../../include/attr.h
qmgr_deliver.o: ../../include/recipient_list.h
qmgr_deliver.o: ../../include/mail_params.h
qmgr_deliver.o: ../../include/deliver_request.h
+qmgr_deliver.o: ../../include/verp_sender.h
qmgr_deliver.o: qmgr.h
qmgr_deliver.o: ../../include/scan_dir.h
qmgr_deliver.o: ../../include/maps.h
@@ -188,6 +192,7 @@ qmgr_message.o: ../../include/mail_addr_find.h
qmgr_message.o: ../../include/maps.h
qmgr_message.o: ../../include/opened.h
qmgr_message.o: ../../include/resolve_local.h
+qmgr_message.o: ../../include/verp_sender.h
qmgr_message.o: ../../include/resolve_clnt.h
qmgr_message.o: qmgr.h
qmgr_message.o: ../../include/scan_dir.h
@@ -252,6 +257,7 @@ qmgr_transport.o: ../../include/vstream.h
qmgr_transport.o: ../../include/vbuf.h
qmgr_transport.o: ../../include/iostuff.h
qmgr_transport.o: ../../include/mail_proto.h
+qmgr_transport.o: ../../include/attr.h
qmgr_transport.o: ../../include/recipient_list.h
qmgr_transport.o: ../../include/mail_conf.h
qmgr_transport.o: ../../include/mail_params.h
diff --git a/gnu/dist/postfix/src/qmgr/qmgr.c b/gnu/dist/postfix/src/qmgr/qmgr.c
index 7513aa368e9..146b2646933 100644
--- a/gnu/dist/postfix/src/qmgr/qmgr.c
+++ b/gnu/dist/postfix/src/qmgr/qmgr.c
@@ -262,6 +262,7 @@
#include <mail_conf.h>
#include <mail_params.h>
#include <mail_proto.h> /* QMGR_SCAN constants */
+#include <mail_flow.h>
/* Master process interface */
@@ -293,6 +294,8 @@ int var_qmgr_fudge;
int var_qmgr_hog;
int var_local_rcpt_lim; /* XXX */
int var_local_con_lim; /* XXX */
+int var_proc_limit;
+bool var_verp_bounce_off;
static QMGR_SCAN *qmgr_incoming;
static QMGR_SCAN *qmgr_deferred;
@@ -378,6 +381,8 @@ static int qmgr_loop(char *unused_name, char **unused_argv)
{
char *in_path = 0;
char *df_path = 0;
+ int token_count;
+ int in_feed = 0;
/*
* This routine runs as part of the event handling loop, after the event
@@ -405,11 +410,27 @@ static int qmgr_loop(char *unused_name, char **unused_argv)
if (qmgr_message_count < var_qmgr_active_limit
&& qmgr_recipient_count < var_qmgr_rcpt_limit)
if ((in_path = qmgr_scan_next(qmgr_incoming)) != 0)
- qmgr_active_feed(qmgr_incoming, in_path);
+ in_feed = qmgr_active_feed(qmgr_incoming, in_path);
if (qmgr_message_count < var_qmgr_active_limit
&& qmgr_recipient_count < var_qmgr_rcpt_limit)
if ((df_path = qmgr_scan_next(qmgr_deferred)) != 0)
qmgr_active_feed(qmgr_deferred, df_path);
+
+ /*
+ * Global flow control. If enabled, slow down receiving processes that
+ * get ahead of the queue manager, but don't block them completely.
+ */
+ if (var_in_flow_delay > 0) {
+ token_count = mail_flow_count();
+ if (token_count < var_proc_limit) {
+ if (in_feed != 0)
+ mail_flow_put(1);
+ else if (qmgr_incoming->handle == 0)
+ mail_flow_put(var_proc_limit - token_count);
+ } else if (token_count > var_proc_limit) {
+ mail_flow_get(token_count - var_proc_limit);
+ }
+ }
if (in_path || df_path)
return (DONT_WAIT);
return (WAIT_FOR_EVENT);
@@ -491,10 +512,12 @@ int main(int argc, char **argv)
VAR_QMGR_HOG, DEF_QMGR_HOG, &var_qmgr_hog, 10, 100,
VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0,
VAR_LOCAL_CON_LIMIT, DEF_LOCAL_CON_LIMIT, &var_local_con_lim, 0, 0,
+ VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0,
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
VAR_ALLOW_MIN_USER, DEF_ALLOW_MIN_USER, &var_allow_min_user,
+ VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off,
0,
};
diff --git a/gnu/dist/postfix/src/qmgr/qmgr.h b/gnu/dist/postfix/src/qmgr/qmgr.h
index 45ceac8e99b..9839c07c209 100644
--- a/gnu/dist/postfix/src/qmgr/qmgr.h
+++ b/gnu/dist/postfix/src/qmgr/qmgr.h
@@ -227,6 +227,7 @@ struct QMGR_MESSAGE {
char *queue_name; /* queue name */
char *queue_id; /* queue file */
char *sender; /* complete address */
+ char *verp_delims; /* VERP delimiters */
char *errors_to; /* error report address */
char *return_receipt; /* confirm receipt address */
char *filter_xport; /* filtering transport */
@@ -269,7 +270,7 @@ extern void qmgr_deliver(QMGR_TRANSPORT *, VSTREAM *);
/*
* qmgr_active.c
*/
-extern void qmgr_active_feed(QMGR_SCAN *, const char *);
+extern int qmgr_active_feed(QMGR_SCAN *, const char *);
extern void qmgr_active_drain(void);
extern void qmgr_active_done(QMGR_MESSAGE *);
diff --git a/gnu/dist/postfix/src/qmgr/qmgr_active.c b/gnu/dist/postfix/src/qmgr/qmgr_active.c
index 404c4eda986..722d810739c 100644
--- a/gnu/dist/postfix/src/qmgr/qmgr_active.c
+++ b/gnu/dist/postfix/src/qmgr/qmgr_active.c
@@ -131,7 +131,8 @@ static void qmgr_active_corrupt(const char *queue_id)
msg_warn("%s: save corrupt file queue %s id %s: %m",
myname, MAIL_QUEUE_ACTIVE, queue_id);
} else {
- msg_warn("corrupt file queue %s id %s", MAIL_QUEUE_ACTIVE, queue_id);
+ msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"",
+ queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
}
}
@@ -164,7 +165,7 @@ static void qmgr_active_defer(const char *queue_name, const char *queue_id,
/* qmgr_active_feed - feed one message into active queue */
-void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
+int qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
{
char *myname = "qmgr_active_feed";
QMGR_MESSAGE *message;
@@ -180,7 +181,7 @@ void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
* Make sure this is something we are willing to open.
*/
if (mail_open_ok(scan_info->queue, queue_id, &st, &path) == MAIL_OPEN_NO)
- return;
+ return (0);
if (msg_verbose)
msg_info("%s: %s", myname, path);
@@ -194,7 +195,7 @@ void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
if (msg_verbose)
msg_info("%s: skip %s (%ld seconds)", myname, queue_id,
(long) (st.st_mtime - event_time()));
- return;
+ return (0);
}
/*
@@ -206,7 +207,7 @@ void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
msg_warn("%s: %s: rename from %s to %s: %m", myname,
queue_id, scan_info->queue, MAIL_QUEUE_ACTIVE);
- return;
+ return (0);
}
/*
@@ -226,8 +227,10 @@ void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
if ((message = qmgr_message_alloc(MAIL_QUEUE_ACTIVE, queue_id,
scan_info->flags)) == 0) {
qmgr_active_corrupt(queue_id);
+ return (0);
} else if (message == QMGR_MESSAGE_LOCKED) {
qmgr_active_defer(MAIL_QUEUE_ACTIVE, queue_id, MAIL_QUEUE_INCOMING, 60);
+ return (0);
} else {
/*
@@ -236,6 +239,7 @@ void qmgr_active_feed(QMGR_SCAN *scan_info, const char *queue_id)
*/
if (message->refcount == 0)
qmgr_active_done(message);
+ return (1);
}
}
@@ -275,12 +279,21 @@ void qmgr_active_done(QMGR_MESSAGE *message)
} else {
if (msg_verbose)
msg_info("%s: bounce %s", myname, message->queue_id);
- abounce_flush(BOUNCE_FLAG_KEEP,
- message->queue_name,
- message->queue_id,
- message->errors_to,
- qmgr_active_done_2_bounce_flush,
- (char *) message);
+ if (message->verp_delims == 0 || var_verp_bounce_off)
+ abounce_flush(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ qmgr_active_done_2_bounce_flush,
+ (char *) message);
+ else
+ abounce_flush_verp(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ message->verp_delims,
+ qmgr_active_done_2_bounce_flush,
+ (char *) message);
return;
}
}
@@ -317,7 +330,7 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
* attributes, and by pretending that delivery was deferred.
*/
if (message->flags
- && !mail_open_ok(MAIL_QUEUE_ACTIVE, message->queue_id, &st, &path)) {
+ && mail_open_ok(MAIL_QUEUE_ACTIVE, message->queue_id, &st, &path) == MAIL_OPEN_NO) {
qmgr_active_corrupt(message->queue_id);
qmgr_message_free(message);
return;
@@ -351,14 +364,23 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
*/
if (message->flags) {
if (event_time() > message->arrival_time + var_max_queue_time) {
- if (msg_verbose)
- msg_info("%s: too old, bouncing %s", myname, message->queue_id);
- adefer_flush(BOUNCE_FLAG_KEEP,
- message->queue_name,
- message->queue_id,
- message->errors_to,
- qmgr_active_done_3_defer_flush,
- (char *) message);
+ msg_info("%s: from=<%s>, status=expired, returned to sender",
+ message->queue_id, message->sender);
+ if (message->verp_delims == 0 || var_verp_bounce_off)
+ adefer_flush(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ qmgr_active_done_3_defer_flush,
+ (char *) message);
+ else
+ adefer_flush_verp(BOUNCE_FLAG_KEEP,
+ message->queue_name,
+ message->queue_id,
+ message->errors_to,
+ message->verp_delims,
+ qmgr_active_done_3_defer_flush,
+ (char *) message);
return;
} else if (message->warn_time > 0
&& event_time() > message->warn_time) {
diff --git a/gnu/dist/postfix/src/qmgr/qmgr_deliver.c b/gnu/dist/postfix/src/qmgr/qmgr_deliver.c
index 1a605dd1f05..45c0a6cdde0 100644
--- a/gnu/dist/postfix/src/qmgr/qmgr_deliver.c
+++ b/gnu/dist/postfix/src/qmgr/qmgr_deliver.c
@@ -63,6 +63,7 @@
#include <recipient_list.h>
#include <mail_params.h>
#include <deliver_request.h>
+#include <verp_sender.h>
/* Application-specific. */
@@ -86,7 +87,9 @@ static int qmgr_deliver_initial_reply(VSTREAM *stream)
if (peekfd(vstream_fileno(stream)) < 0) {
msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
return (DELIVER_STAT_CRASH);
- } else if (mail_scan(stream, "%d", &stat) != 1) {
+ } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+ ATTR_TYPE_END) != 1) {
msg_warn("%s: malformed response", VSTREAM_PATH(stream));
return (DELIVER_STAT_CRASH);
} else {
@@ -103,7 +106,10 @@ static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
if (peekfd(vstream_fileno(stream)) < 0) {
msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
return (DELIVER_STAT_CRASH);
- } else if (mail_scan(stream, "%s %d", reason, &stat) != 2) {
+ } else if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+ ATTR_TYPE_END) != 2) {
msg_warn("%s: malformed response", VSTREAM_PATH(stream));
return (DELIVER_STAT_CRASH);
} else {
@@ -119,6 +125,24 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
QMGR_RCPT *recipient;
QMGR_MESSAGE *message = entry->message;
char *cp;
+ VSTRING *sender_buf = 0;
+ char *sender;
+ int flags;
+ char *nexthop;
+
+ /*
+ * If variable envelope return path is requested, change prefix+@origin
+ * into prefix+user=domain@origin. Note that with VERP there is only one
+ * recipient per delivery.
+ */
+ if (message->verp_delims == 0) {
+ sender = message->sender;
+ } else {
+ sender_buf = vstring_alloc(100);
+ verp_sender(sender_buf, message->verp_delims,
+ message->sender, list.info->address);
+ sender = vstring_str(sender_buf);
+ }
/*
* With mail transports that accept only one recipient per delivery, the
@@ -126,17 +150,32 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
* concurrency limits. However, the delivery agent protocol expects
* nexthop only, so we must strip off the recipient local part.
*/
- mail_print(stream, "%d %s %s %ld %ld %s %s %s %s %ld",
- message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT,
- message->queue_name, message->queue_id,
- message->data_offset, message->data_size,
- (cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ? cp + 1 :
- entry->queue->name, message->sender,
- message->errors_to, message->return_receipt,
- message->arrival_time);
+ flags = message->inspect_xport ?
+ DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT;
+ nexthop = (cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ?
+ cp + 1 : entry->queue->name;
+ attr_print(stream, ATTR_FLAG_MORE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
+ ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, message->data_offset,
+ ATTR_TYPE_LONG, MAIL_ATTR_SIZE, message->data_size,
+ ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
+ ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
+ ATTR_TYPE_STR, MAIL_ATTR_RRCPT, message->return_receipt,
+ ATTR_TYPE_LONG, MAIL_ATTR_TIME, message->arrival_time,
+ ATTR_TYPE_END);
+ if (sender_buf != 0)
+ vstring_free(sender_buf);
for (recipient = list.info; recipient < list.info + list.len; recipient++)
- mail_print(stream, "%ld %s", recipient->offset, recipient->address);
- mail_print(stream, "%s", "0");
+ attr_print(stream, ATTR_FLAG_MORE,
+ ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
+ ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
+ ATTR_TYPE_END);
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
+ ATTR_TYPE_END);
if (vstream_fflush(stream) != 0) {
msg_warn("write to process (%s): %m", entry->queue->transport->name);
return (-1);
@@ -197,6 +236,8 @@ static void qmgr_deliver_update(int unused_event, char *context)
if (status == DELIVER_STAT_CRASH) {
message->flags |= DELIVER_STAT_DEFER;
qmgr_transport_throttle(transport, "unknown mail transport error");
+ msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
+ transport->name);
qmgr_defer_transport(transport, transport->reason);
}
diff --git a/gnu/dist/postfix/src/qmgr/qmgr_move.c b/gnu/dist/postfix/src/qmgr/qmgr_move.c
index 7f86197ec01..a4e55f0585f 100644
--- a/gnu/dist/postfix/src/qmgr/qmgr_move.c
+++ b/gnu/dist/postfix/src/qmgr/qmgr_move.c
@@ -35,6 +35,7 @@
#include <sys/stat.h>
#include <string.h>
#include <utime.h>
+#include <errno.h>
/* Utility library. */
@@ -73,12 +74,21 @@ void qmgr_move(const char *src_queue, const char *dst_queue,
if (time_stamp > 0) {
tbuf.actime = tbuf.modtime = time_stamp;
path = mail_queue_path((VSTRING *) 0, src_queue, queue_id);
- if (utime(path, &tbuf) < 0)
- msg_fatal("%s: update %s time stamps: %m", myname, path);
+ if (utime(path, &tbuf) < 0) {
+ if (errno != ENOENT)
+ msg_fatal("%s: update %s time stamps: %m", myname, path);
+ msg_warn("%s: update %s time stamps: %m", myname, path);
+ continue;
+ }
+ }
+ if (mail_queue_rename(queue_id, src_queue, dst_queue)) {
+ if (errno != ENOENT)
+ msg_fatal("%s: rename %s from %s to %s: %m",
+ myname, queue_id, src_queue, dst_queue);
+ msg_warn("%s: rename %s from %s to %s: %m",
+ myname, queue_id, src_queue, dst_queue);
+ continue;
}
- if (mail_queue_rename(queue_id, src_queue, dst_queue))
- msg_fatal("%s: rename %s from %s to %s: %m",
- myname, queue_id, src_queue, dst_queue);
if (msg_verbose)
msg_info("%s: moved %s from %s to %s",
myname, queue_id, src_queue, dst_queue);
diff --git a/gnu/dist/postfix/src/qmqpd/.printfck b/gnu/dist/postfix/src/qmqpd/.printfck
new file mode 100644
index 00000000000..66016ed453c
--- /dev/null
+++ b/gnu/dist/postfix/src/qmqpd/.printfck
@@ -0,0 +1,25 @@
+been_here_xt 2 0
+bounce_append 5 0
+cleanup_out_format 1 0
+defer_append 5 0
+mail_command 1 0
+mail_print 1 0
+msg_error 0 0
+msg_fatal 0 0
+msg_info 0 0
+msg_panic 0 0
+msg_warn 0 0
+opened 4 0
+post_mail_fprintf 1 0
+qmgr_message_bounce 2 0
+rec_fprintf 2 0
+sent 4 0
+smtp_cmd 1 0
+smtp_mesg_fail 2 0
+smtp_printf 1 0
+smtp_rcpt_fail 3 0
+smtp_site_fail 2 0
+udp_syslog 1 0
+vstream_fprintf 1 0
+vstream_printf 0 0
+vstring_sprintf 1 0
diff --git a/gnu/dist/postfix/src/qmqpd/Makefile.in b/gnu/dist/postfix/src/qmqpd/Makefile.in
new file mode 100644
index 00000000000..5e923c5b071
--- /dev/null
+++ b/gnu/dist/postfix/src/qmqpd/Makefile.in
@@ -0,0 +1,117 @@
+SHELL = /bin/sh
+SRCS = qmqpd.c qmqpd_state.c qmqpd_peer.c
+OBJS = qmqpd.o qmqpd_state.o qmqpd_peer.o
+HDRS =
+TESTSRC =
+WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
+ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
+ -Wunused
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG= qmqpd_token qmqpd_check
+PROG = qmqpd
+INC_DIR = ../../include
+LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libdns.a ../../lib/libutil.a
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+Makefile: Makefile.in
+ (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
+
+test: $(TESTPROG)
+
+update: ../../libexec/$(PROG)
+
+../../libexec/$(PROG): $(PROG)
+ cp $(PROG) ../../libexec
+
+SMTPD_CHECK_OBJ = qmqpd_state.o qmqpd_peer.o
+
+qmqpd_token: qmqpd_token.c $(LIBS)
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS)
+
+qmqpd_check: qmqpd_check.c $(SMTPD_CHECK_OBJ) $(LIBS)
+ mv $@.o junk
+ $(CC) $(CFLAGS) -DTEST -o $@ qmqpd_check.c $(SMTPD_CHECK_OBJ) \
+ $(LIBS) $(SYSLIBS)
+ mv junk $@.o
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ cp *.h printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk *.db *.out *.tmp
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
+ done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+tests:
+
+# do not edit below this line - it is generated by 'make depend'
+qmqpd.o: qmqpd.c
+qmqpd.o: ../../include/sys_defs.h
+qmqpd.o: ../../include/msg.h
+qmqpd.o: ../../include/mymalloc.h
+qmqpd.o: ../../include/vstring.h
+qmqpd.o: ../../include/vbuf.h
+qmqpd.o: ../../include/vstream.h
+qmqpd.o: ../../include/netstring.h
+qmqpd.o: ../../include/dict.h
+qmqpd.o: ../../include/argv.h
+qmqpd.o: ../../include/mail_params.h
+qmqpd.o: ../../include/record.h
+qmqpd.o: ../../include/rec_type.h
+qmqpd.o: ../../include/mail_proto.h
+qmqpd.o: ../../include/iostuff.h
+qmqpd.o: ../../include/attr.h
+qmqpd.o: ../../include/cleanup_user.h
+qmqpd.o: ../../include/mail_date.h
+qmqpd.o: ../../include/mail_conf.h
+qmqpd.o: ../../include/debug_peer.h
+qmqpd.o: ../../include/mail_stream.h
+qmqpd.o: ../../include/namadr_list.h
+qmqpd.o: ../../include/match_list.h
+qmqpd.o: ../../include/match_ops.h
+qmqpd.o: ../../include/quote_822_local.h
+qmqpd.o: ../../include/match_parent_style.h
+qmqpd.o: ../../include/mail_server.h
+qmqpd.o: qmqpd.h
+qmqpd_peer.o: qmqpd_peer.c
+qmqpd_peer.o: ../../include/sys_defs.h
+qmqpd_peer.o: ../../include/msg.h
+qmqpd_peer.o: ../../include/mymalloc.h
+qmqpd_peer.o: ../../include/valid_hostname.h
+qmqpd_peer.o: ../../include/stringops.h
+qmqpd_peer.o: ../../include/vstring.h
+qmqpd_peer.o: ../../include/vbuf.h
+qmqpd_peer.o: qmqpd.h
+qmqpd_peer.o: ../../include/vstream.h
+qmqpd_peer.o: ../../include/mail_stream.h
+qmqpd_state.o: qmqpd_state.c
+qmqpd_state.o: ../../include/sys_defs.h
+qmqpd_state.o: ../../include/mymalloc.h
+qmqpd_state.o: ../../include/vstream.h
+qmqpd_state.o: ../../include/vbuf.h
+qmqpd_state.o: ../../include/vstring.h
+qmqpd_state.o: ../../include/mail_stream.h
+qmqpd_state.o: ../../include/cleanup_user.h
+qmqpd_state.o: qmqpd.h
diff --git a/gnu/dist/postfix/src/qmqpd/qmqpd.c b/gnu/dist/postfix/src/qmqpd/qmqpd.c
new file mode 100644
index 00000000000..442e41433e0
--- /dev/null
+++ b/gnu/dist/postfix/src/qmqpd/qmqpd.c
@@ -0,0 +1,677 @@
+/*++
+/* NAME
+/* qmqpd 8
+/* SUMMARY
+/* Postfix QMQP server
+/* SYNOPSIS
+/* \fBqmqpd\fR [generic Postfix daemon options]
+/* DESCRIPTION
+/* The Postfix QMQP server receives one message per connection.
+/* Each message is piped through the \fBcleanup\fR(8)
+/* daemon, and is placed into the \fBincoming\fR queue as one
+/* single queue file. The program expects to be run from the
+/* \fBmaster\fR(8) process manager.
+/*
+/* The QMQP server implements one access policy: only explicitly
+/* authorized client hosts are allowed to use the service.
+/* SECURITY
+/* .ad
+/* .fi
+/* The QMQP server is moderately security-sensitive. It talks to QMQP
+/* clients and to DNS servers on the network. The QMQP server can be
+/* run chrooted at fixed low privilege.
+/* DIAGNOSTICS
+/* Problems and transactions are logged to \fBsyslogd\fR(8).
+/* BUGS
+/* The QMQP protocol provides only one server reply per message
+/* delivery. It is therefore not possible to reject individual
+/* recipients.
+/*
+/* The QMQP protocol requires the server to receive the entire
+/* message before replying. If a message is malformed, or if any
+/* netstring component is longer than acceptable, Postfix replies
+/* immediately and closes the connection. It is left up to the
+/* client to handle the situation.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* The following \fBmain.cf\fR parameters are especially relevant to
+/* this program. See the Postfix \fBmain.cf\fR file for syntax details
+/* and for default values. Use the \fBpostfix reload\fR command after
+/* a configuration change.
+/* .SH Miscellaneous
+/* .ad
+/* .fi
+/* .IP \fBalways_bcc\fR
+/* Address to send a copy of each message that enters the system.
+/* .IP \fBdebug_peer_level\fR
+/* Increment in verbose logging level when a remote host matches a
+/* pattern in the \fBdebug_peer_list\fR parameter.
+/* .IP \fBdebug_peer_list\fR
+/* List of domain or network patterns. When a remote host matches
+/* a pattern, increase the verbose logging level by the amount
+/* specified in the \fBdebug_peer_level\fR parameter.
+/* .IP \fBhopcount_limit\fR
+/* Limit the number of \fBReceived:\fR message headers.
+/* .IP \fBqmqpd_authorized_clients\fR
+/* A list of domain or network patterns that specifies what
+/* clients are allowed to use the service.
+/* .IP \fBqmqpd_timeout\fR
+/* Limit the time to send a server response and to receive a client
+/* request.
+/* .IP \fBsoft_bounce\fR
+/* Change hard (D) reject responses into soft (Z) reject responses.
+/* This can be useful for testing purposes.
+/* .SH "Content inspection controls"
+/* .IP \fBcontent_filter\fR
+/* The name of a mail delivery transport that filters mail and that
+/* either bounces mail or re-injects the result back into Postfix.
+/* This parameter uses the same syntax as the right-hand side of
+/* a Postfix transport table.
+/* .SH "Resource controls"
+/* .ad
+/* .fi
+/* .IP \fBline_length_limit\fR
+/* Limit the amount of memory in bytes used for the handling of
+/* partial input lines, and the length of sender and recipient
+/* addresses that are received from client.
+/* .IP \fBmessage_size_limit\fR
+/* Limit the total size in bytes of a message, including on-disk
+/* storage for sender and recipient address information.
+/* .SH Tarpitting
+/* .ad
+/* .fi
+/* .IP \fBqmqpd_error_sleep_time\fR
+/* Time to wait in seconds before informing the client of
+/* a problem. This slows down run-away errors.
+/* SEE ALSO
+/* http://cr.yp.to/proto/qmqp.html, QMQP protocol
+/* cleanup(8) message canonicalization
+/* master(8) process manager
+/* syslogd(8) system logging
+/* 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>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <netstring.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <record.h>
+#include <rec_type.h>
+#include <mail_proto.h>
+#include <cleanup_user.h>
+#include <mail_date.h>
+#include <mail_conf.h>
+#include <debug_peer.h>
+#include <mail_stream.h>
+#include <namadr_list.h>
+#include <quote_822_local.h>
+#include <match_parent_style.h>
+
+/* Single-threaded server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific */
+
+#include <qmqpd.h>
+
+ /*
+ * Tunable parameters. Make sure that there is some bound on the length of a
+ * netstring, so that the mail system stays in control even when a malicious
+ * client sends netstrings of unreasonable length. The recipient count limit
+ * is enforced by the message size limit.
+ */
+int var_qmqpd_timeout;
+int var_qmqpd_err_sleep;
+char *var_always_bcc;
+char *var_filter_xport;
+char *var_qmqpd_clients;
+
+ /*
+ * Silly little macros.
+ */
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+#define DO_LOG 1
+#define DONT_LOG 0
+
+ /*
+ * Access control. This service should be exposed only to explicitly
+ * authorized clients. There is no default authorization.
+ */
+static NAMADR_LIST *qmqpd_clients;
+
+/* qmqpd_open_file - open a queue file */
+
+static void qmqpd_open_file(QMQPD_STATE *state)
+{
+
+ /*
+ * Connect to the cleanup server. Log client name/address with queue ID.
+ */
+ state->dest = mail_stream_service(MAIL_CLASS_PUBLIC, MAIL_SERVICE_CLEANUP);
+ if (state->dest == 0
+ || attr_print(state->dest->stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, CLEANUP_FLAG_FILTER,
+ ATTR_TYPE_END) != 0)
+ msg_fatal("unable to connect to the %s %s service",
+ MAIL_CLASS_PUBLIC, MAIL_SERVICE_CLEANUP);
+ state->cleanup = state->dest->stream;
+ state->queue_id = mystrdup(state->dest->id);
+ msg_info("%s: client=%s", state->queue_id, state->namaddr);
+
+ /*
+ * Record the time of arrival. Optionally, enable content filtering (not
+ * bloody likely, but present for the sake of consistency with all other
+ * Postfix points of entrance).
+ */
+ rec_fprintf(state->cleanup, REC_TYPE_TIME, "%ld", state->time);
+ if (*var_filter_xport)
+ rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
+}
+
+/* qmqpd_read_content - receive message content */
+
+static void qmqpd_read_content(QMQPD_STATE *state)
+{
+ state->where = "receiving message content";
+ netstring_get(state->client, state->message, var_message_limit);
+}
+
+/* qmqpd_copy_sender - copy envelope sender */
+
+static void qmqpd_copy_sender(QMQPD_STATE *state)
+{
+ char *end_prefix;
+ char *end_origin;
+ int verp_requested;
+
+ /*
+ * If the sender address looks like prefix-@origin-@[], then request
+ * variable envelope return path delivery, with an envelope sender
+ * address of prefix@origin, and with VERP delimiters of - and =. This
+ * way, the recipients will see envelope sender addresses that look like:
+ * prefix-user=domain@origin.
+ */
+ state->where = "receiving sender address";
+ netstring_get(state->client, state->buf, var_line_limit);
+ VSTRING_TERMINATE(state->buf);
+ verp_requested = ((end_prefix = strstr(STR(state->buf), "-@")) != 0
+ && (end_origin = strstr(end_prefix + 2, "-@")) != 0
+ && strncmp(end_origin + 2, "[]", 2) == 0
+ && vstring_end(state->buf) == end_origin + 4);
+ if (verp_requested) {
+ memcpy(end_prefix, end_prefix + 1, end_origin - end_prefix - 1);
+ vstring_truncate(state->buf, end_origin - STR(state->buf) - 1);
+ }
+ if (state->err == CLEANUP_STAT_OK
+ && REC_PUT_BUF(state->cleanup, REC_TYPE_FROM, state->buf) < 0)
+ state->err = CLEANUP_STAT_WRITE;
+ if (verp_requested)
+ if (state->err == CLEANUP_STAT_OK
+ && rec_put(state->cleanup, REC_TYPE_VERP, "-=", 2) < 0)
+ state->err = CLEANUP_STAT_WRITE;
+ state->sender = mystrndup(STR(state->buf), LEN(state->buf));
+}
+
+/* qmqpd_copy_recipients - copy message recipients */
+
+static void qmqpd_copy_recipients(QMQPD_STATE *state)
+{
+ int ch;
+
+ /*
+ * Remember the first recipient. We are done when we read the over-all
+ * netstring terminator.
+ *
+ * XXX This approach violates abstractions, but it is a heck of a lot more
+ * convenient than counting the over-all byte count down to zero, like
+ * qmail does.
+ */
+ state->where = "receiving recipient address";
+ while ((ch = VSTREAM_GETC(state->client)) != ',') {
+ vstream_ungetc(state->client, ch);
+ netstring_get(state->client, state->buf, var_line_limit);
+ if (state->err == CLEANUP_STAT_OK
+ && REC_PUT_BUF(state->cleanup, REC_TYPE_RCPT, state->buf) < 0)
+ state->err = CLEANUP_STAT_WRITE;
+ state->rcpt_count++;
+ if (state->recipient == 0)
+ state->recipient = mystrndup(STR(state->buf), LEN(state->buf));
+ }
+
+ /*
+ * Append the optional recipient who is copied on all mail.
+ */
+ if (*var_always_bcc)
+ rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc);
+}
+
+/* qmqpd_next_line - get line from buffer, return last char, newline, or -1 */
+
+static int qmqpd_next_line(VSTRING *message, char **start, int *len,
+ char **next)
+{
+ char *beyond = STR(message) + LEN(message);
+ char *enough = *next + var_line_limit;
+ char *cp;
+
+ /*
+ * Stop at newline or at some limit. Don't look beyond the end of the
+ * buffer.
+ */
+#define UCHARPTR(x) ((unsigned char *) (x))
+
+ for (cp = *start = *next; /* void */ ; cp++) {
+ if (cp >= beyond)
+ return ((*len = (*next = cp) - *start) > 0 ? UCHARPTR(cp)[-1] : -1);
+ if (*cp == '\n')
+ return ((*len = cp - *start), (*next = cp + 1), '\n');
+ if (cp >= enough)
+ return ((*len = cp - *start), (*next = cp), UCHARPTR(cp)[-1]);
+ }
+}
+
+/* qmqpd_write_content - write the message content segment */
+
+static void qmqpd_write_content(QMQPD_STATE *state)
+{
+ char *start;
+ char *next;
+ int len;
+ int rec_type;
+ int first = 1;
+ int ch;
+
+ /*
+ * Start the message content segment. Prepend our own Received: header to
+ * the message content. List the recipient only when a message has one
+ * recipient. Otherwise, don't list the recipient to avoid revealing Bcc:
+ * recipients that are supposed to be invisible.
+ */
+ rec_fputs(state->cleanup, REC_TYPE_MESG, "");
+ rec_fprintf(state->cleanup, REC_TYPE_NORM, "Received: from %s (%s [%s])",
+ state->name, state->name, state->addr);
+ if (state->rcpt_count == 1 && state->recipient) {
+ rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ "\tby %s (%s) with %s id %s",
+ var_myhostname, var_mail_name,
+ state->protocol, state->queue_id);
+ quote_822_local(state->buf, state->recipient);
+ rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ "\tfor <%s>; %s", STR(state->buf), mail_date(state->time));
+ } else {
+ rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ "\tby %s (%s) with %s",
+ var_myhostname, var_mail_name, state->protocol);
+ rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ "\tid %s; %s", state->queue_id, mail_date(state->time));
+ }
+#ifdef RECEIVED_ENVELOPE_FROM
+ quote_822_local(state->buf, state->sender);
+ rec_fprintf(state->cleanup, REC_TYPE_NORM,
+ "\t(envelope-from <%s>)", STR(state->buf));
+#endif
+
+ /*
+ * Write the message content.
+ *
+ * XXX Force an empty record when the queue file content begins with
+ * whitespace, so that it won't be considered as being part of our own
+ * Received: header. What an ugly Kluge.
+ *
+ * XXX Deal with UNIX-style From_ lines at the start of message content just
+ * in case.
+ */
+ for (next = STR(state->message); /* void */ ; /* void */ ) {
+ if ((ch = qmqpd_next_line(state->message, &start, &len, &next)) < 0)
+ break;
+ if (ch == '\n')
+ rec_type = REC_TYPE_NORM;
+ else
+ rec_type = REC_TYPE_CONT;
+ if (first) {
+ if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) {
+ rec_fprintf(state->cleanup, rec_type,
+ "Mailbox-Line: %*s", len, start);
+ continue;
+ }
+ first = 0;
+ if (len > 0 && ISSPACE(start[0]))
+ rec_put(state->cleanup, REC_TYPE_NORM, "", 0);
+ }
+ if (rec_put(state->cleanup, rec_type, start, len) < 0) {
+ state->err = CLEANUP_STAT_WRITE;
+ return;
+ }
+ }
+}
+
+/* qmqpd_close_file - close queue file */
+
+static void qmqpd_close_file(QMQPD_STATE *state)
+{
+
+ /*
+ * Send the end-of-segment markers.
+ */
+ if (state->err == CLEANUP_STAT_OK)
+ if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
+ || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
+ || vstream_fflush(state->cleanup))
+ state->err = CLEANUP_STAT_WRITE;
+
+ /*
+ * Finish the queue file or finish the cleanup conversation.
+ */
+ if (state->err == 0)
+ state->err = mail_stream_finish(state->dest, state->why_rejected);
+ else
+ mail_stream_cleanup(state->dest);
+ state->dest = 0;
+}
+
+/* qmqpd_reply - send status to client and optionally log message */
+
+static void qmqpd_reply(QMQPD_STATE *state, int log_message,
+ int status_code, const char *fmt,...)
+{
+ va_list ap;
+
+ /*
+ * Optionally change hard errors into retryable ones. Send the reply and
+ * optionally log it. Always insert a delay before reporting a problem.
+ * This slows down software run-away conditions.
+ */
+ if (status_code == QMQPD_STAT_HARD && var_soft_bounce)
+ status_code = QMQPD_STAT_RETRY;
+ VSTRING_RESET(state->buf);
+ VSTRING_ADDCH(state->buf, status_code);
+ va_start(ap, fmt);
+ vstring_vsprintf_append(state->buf, fmt, ap);
+ va_end(ap);
+ NETSTRING_PUT_BUF(state->client, state->buf);
+ if (log_message)
+ (status_code == QMQPD_STAT_OK ? msg_info : msg_warn) ("%s: %s: %s",
+ state->queue_id, state->namaddr, STR(state->buf) + 1);
+ if (status_code != QMQPD_STAT_OK)
+ sleep(var_qmqpd_err_sleep);
+ netstring_fflush(state->client);
+}
+
+/* qmqpd_send_status - send mail transaction completion status */
+
+static void qmqpd_send_status(QMQPD_STATE *state)
+{
+
+ /*
+ * One message may suffer from multiple errors, so complain only about
+ * the most severe error.
+ *
+ * See also: smtpd.c
+ */
+ state->where = "sending completion status";
+
+ if (state->err == CLEANUP_STAT_OK) {
+ qmqpd_reply(state, DONT_LOG, QMQPD_STAT_OK,
+ "Ok: queued as %s", state->queue_id);
+ } else if ((state->err & CLEANUP_STAT_BAD) != 0) {
+ qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
+ "Error: internal error %d", state->err);
+ } else if ((state->err & CLEANUP_STAT_SIZE) != 0) {
+ qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+ "Error: message too large");
+ } else if ((state->err & CLEANUP_STAT_HOPS) != 0) {
+ qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+ "Error: too many hops");
+ } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
+ qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+ "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");
+ } else if ((state->err & CLEANUP_STAT_RCPT) != 0) {
+ qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
+ "Error: no recipients specified");
+ } else {
+ qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
+ "Error: internal error %d", state->err);
+ }
+}
+
+/* qmqpd_receive - receive QMQP message+sender+recipients */
+
+static void qmqpd_receive(QMQPD_STATE *state)
+{
+
+ /*
+ * Open a queue file. This must be first so that we can simplify the
+ * error logging and always include the queue ID information.
+ */
+ qmqpd_open_file(state);
+
+ /*
+ * Read and ignore the over-all netstring length indicator.
+ */
+ state->where = "receiving QMQP packet header";
+ (void) netstring_get_length(state->client);
+
+ /*
+ * XXX Read the message content into memory, because Postfix expects to
+ * store the sender before storing the message content. Fixing that
+ * requires changes to pickup, cleanup, qmgr, and perhaps elsewhere, so
+ * that will have to happen later when I have more time. However, QMQP is
+ * used for mailing list distribution, so the bulk of the volume is
+ * expected to be not message content but recipients, and recipients are
+ * not accumulated in memory.
+ */
+ qmqpd_read_content(state);
+
+ /*
+ * Read and write the envelope sender.
+ */
+ qmqpd_copy_sender(state);
+
+ /*
+ * Read and write the envelope recipients, including the optional big
+ * brother recipient.
+ */
+ qmqpd_copy_recipients(state);
+
+ /*
+ * Start the message content segment, prepend our own Received: header,
+ * and write the message content.
+ */
+ qmqpd_write_content(state);
+
+ /*
+ * Close the queue file.
+ */
+ qmqpd_close_file(state);
+
+ /*
+ * Report the completion status to the client.
+ */
+ qmqpd_send_status(state);
+}
+
+/* qmqpd_proto - speak the QMQP "protocol" */
+
+static void qmqpd_proto(QMQPD_STATE *state)
+{
+ int status;
+
+ netstring_setup(state->client, var_qmqpd_timeout);
+
+ switch (status = vstream_setjmp(state->client)) {
+
+ default:
+ msg_panic("qmqpd_proto: unknown status %d", status);
+
+ case NETSTRING_ERR_EOF:
+ state->reason = "lost connection";
+ break;
+
+ case NETSTRING_ERR_TIME:
+ state->reason = "read/write timeout";
+ break;
+
+ case NETSTRING_ERR_FORMAT:
+ state->reason = "netstring format error";
+ if (vstream_setjmp(state->client) == 0)
+ if (state->reason && state->where)
+ qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD, "%s while %s",
+ state->reason, state->where);
+ break;
+
+ case NETSTRING_ERR_SIZE:
+ state->reason = "netstring length exceeds storage limit";
+ if (vstream_setjmp(state->client) == 0)
+ if (state->reason && state->where)
+ qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD, "%s while %s",
+ state->reason, state->where);
+ break;
+
+ case 0:
+ qmqpd_receive(state);
+ break;
+ }
+
+ /*
+ * Log abnormal session termination. Indicate the last recognized state
+ * before things went wrong.
+ */
+ if (state->reason && state->where)
+ msg_info("%s: %s: %s while %s",
+ state->queue_id, state->namaddr, state->reason, state->where);
+}
+
+/* qmqpd_service - service one client */
+
+static void qmqpd_service(VSTREAM *stream, char *unused_service, char **argv)
+{
+ QMQPD_STATE *state;
+
+ /*
+ * Sanity check. This service takes no command-line arguments.
+ */
+ if (argv[0])
+ msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+ /*
+ * This routine runs when a client has connected to our network port.
+ * Look up and sanitize the peer name and initialize some connection-
+ * specific state.
+ */
+ state = qmqpd_state_alloc(stream);
+
+ /*
+ * See if we need to turn on verbose logging for this client.
+ */
+ debug_peer_check(state->name, state->addr);
+
+ /*
+ * See if we want to talk to this client at all. In all cases, log the
+ * connection event.
+ */
+ if (namadr_list_match(qmqpd_clients, state->name, state->addr) == 0) {
+ msg_info("refused connect from %s", state->namaddr);
+ qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD,
+ "Error: %s is not authorized to use this service",
+ state->namaddr);
+ }
+
+ /*
+ * Provide the QMQP service.
+ */
+ else {
+ msg_info("connect from %s", state->namaddr);
+ qmqpd_proto(state);
+ msg_info("disconnect from %s", state->namaddr);
+ }
+
+ /*
+ * After the client has gone away, clean up whatever we have set up at
+ * connection time.
+ */
+ debug_peer_restore();
+ qmqpd_state_free(state);
+}
+
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(char *unused_name, char **unused_argv)
+{
+ if (dict_changed()) {
+ msg_info("lookup table has changed -- exiting");
+ exit(0);
+ }
+}
+
+/* pre_jail_init - pre-jail initialization */
+
+static void pre_jail_init(char *unused_name, char **unused_argv)
+{
+ debug_peer_init();
+ qmqpd_clients =
+ namadr_list_init(match_parent_style(VAR_QMQPD_CLIENTS),
+ var_qmqpd_clients);
+}
+
+/* main - the main program */
+
+int main(int argc, char **argv)
+{
+ static CONFIG_TIME_TABLE time_table[] = {
+ VAR_QMTPD_TMOUT, DEF_QMTPD_TMOUT, &var_qmqpd_timeout, 1, 0,
+ VAR_QMTPD_ERR_SLEEP, DEF_QMTPD_ERR_SLEEP, &var_qmqpd_err_sleep, 0, 0,
+ 0,
+ };
+ static CONFIG_STR_TABLE str_table[] = {
+ VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0,
+ VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
+ VAR_QMQPD_CLIENTS, DEF_QMQPD_CLIENTS, &var_qmqpd_clients, 0, 0,
+ 0,
+ };
+
+ /*
+ * Pass control to the single-threaded service skeleton.
+ */
+ single_server_main(argc, argv, qmqpd_service,
+ MAIL_SERVER_TIME_TABLE, time_table,
+ MAIL_SERVER_STR_TABLE, str_table,
+ MAIL_SERVER_PRE_INIT, pre_jail_init,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ 0);
+}
diff --git a/gnu/dist/postfix/src/qmqpd/qmqpd.h b/gnu/dist/postfix/src/qmqpd/qmqpd.h
new file mode 100644
index 00000000000..aaacb424254
--- /dev/null
+++ b/gnu/dist/postfix/src/qmqpd/qmqpd.h
@@ -0,0 +1,79 @@
+/*++
+/* NAME
+/* qmqpd 3h
+/* SUMMARY
+/* Postfix QMQP server
+/* SYNOPSIS
+/* include "qmqpd.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <time.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * Global library.
+ */
+#include <mail_stream.h>
+
+ /*
+ * Per-session state.
+ */
+typedef struct {
+ int err; /* error flags */
+ VSTREAM *client; /* client connection */
+ VSTRING *message; /* message buffer */
+ VSTRING *buf; /* line buffer */
+ time_t time; /* start of session */
+ char *name; /* client name */
+ char *addr; /* client IP address */
+ char *namaddr; /* name[addr] */
+ char *queue_id; /* queue file ID */
+ VSTREAM *cleanup; /* cleanup server */
+ MAIL_STREAM *dest; /* cleanup server */
+ int rcpt_count; /* recipient count */
+ char *reason; /* exception name */
+ char *sender; /* sender address */
+ char *recipient; /* recipient address */
+ char *protocol; /* protocol name */
+ char *where; /* protocol state */
+ VSTRING *why_rejected; /* REJECT reason */
+} QMQPD_STATE;
+
+ /*
+ * QMQP protocol status codes.
+ */
+#define QMQPD_STAT_OK 'K'
+#define QMQPD_STAT_RETRY 'Z'
+#define QMQPD_STAT_HARD 'D'
+
+ /*
+ * qmqpd_state.c
+ */
+QMQPD_STATE *qmqpd_state_alloc(VSTREAM *);
+void qmqpd_state_free(QMQPD_STATE *);
+
+ /*
+ * qmqpd_peer.c
+ */
+void qmqpd_peer_init(QMQPD_STATE *);
+void qmqpd_peer_reset(QMQPD_STATE *);
+
+/* 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
+/*--*/
diff --git a/gnu/dist/postfix/src/qmqpd/qmqpd_peer.c b/gnu/dist/postfix/src/qmqpd/qmqpd_peer.c
new file mode 100644
index 00000000000..9f0e9f2f5dd
--- /dev/null
+++ b/gnu/dist/postfix/src/qmqpd/qmqpd_peer.c
@@ -0,0 +1,183 @@
+/*++
+/* NAME
+/* qmqpd_peer 3
+/* SUMMARY
+/* look up peer name/address information
+/* SYNOPSIS
+/* #include "qmqpd.h"
+/*
+/* void qmqpd_peer_init(state)
+/* QMQPD_STATE *state;
+/*
+/* void qmqpd_peer_reset(state)
+/* QMQPD_STATE *state;
+/* DESCRIPTION
+/* The qmqpd_peer_init() routine attempts to produce a printable
+/* version of the peer name and address of the specified socket.
+/* Where information is unavailable, the name and/or address
+/* are set to "unknown".
+/*
+/* qmqpd_peer_init() updates the following fields:
+/* .IP name
+/* The client hostname. An unknown name is represented by the
+/* string "unknown".
+/* .IP addr
+/* Printable representation of the client address.
+/* .IP namaddr
+/* String of the form: "name[addr]".
+/* .PP
+/* qmqpd_peer_reset() releases memory allocate by qmqpd_peer_init().
+/* 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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h> /* strerror() */
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+
+ /*
+ * Older systems don't have h_errno. Even modern systems don't have
+ * hstrerror().
+ */
+#ifdef NO_HERRNO
+
+static int h_errno = TRY_AGAIN;
+
+#define HSTRERROR(err) "Host not found"
+
+#else
+
+#define HSTRERROR(err) (\
+ err == TRY_AGAIN ? "Host not found, try again" : \
+ err == HOST_NOT_FOUND ? "Host not found" : \
+ err == NO_DATA ? "Host name has no address" : \
+ err == NO_RECOVERY ? "Name server failure" : \
+ strerror(errno) \
+ )
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <valid_hostname.h>
+#include <stringops.h>
+
+/* Global library. */
+
+
+/* Application-specific. */
+
+#include "qmqpd.h"
+
+/* qmqpd_peer_init - initialize peer information */
+
+void qmqpd_peer_init(QMQPD_STATE *state)
+{
+ struct sockaddr_in sin;
+ SOCKADDR_SIZE len = sizeof(sin);
+ struct hostent *hp;
+ int i;
+
+ /*
+ * Look up the peer address information.
+ */
+ if (getpeername(vstream_fileno(state->client),
+ (struct sockaddr *) & sin, &len) >= 0) {
+ errno = 0;
+ }
+
+ /*
+ * If peer went away, give up.
+ */
+ if (errno == ECONNRESET || errno == ECONNABORTED) {
+ state->name = mystrdup("unknown");
+ state->addr = mystrdup("unknown");
+ }
+
+ /*
+ * Look up and "verify" the client hostname.
+ */
+ else if (errno == 0 && sin.sin_family == AF_INET) {
+ state->addr = mystrdup(inet_ntoa(sin.sin_addr));
+ hp = gethostbyaddr((char *) &(sin.sin_addr),
+ sizeof(sin.sin_addr), AF_INET);
+ if (hp == 0) {
+ state->name = mystrdup("unknown");
+ } else if (!valid_hostname(hp->h_name, DONT_GRIPE)) {
+ state->name = mystrdup("unknown");
+ } else {
+ state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */
+
+ /*
+ * Reject the hostname if it does not list the peer address.
+ */
+#define REJECT_PEER_NAME(state) { \
+ myfree(state->name); \
+ state->name = mystrdup("unknown"); \
+ }
+
+ hp = gethostbyname(state->name); /* clobbers hp->name!! */
+ if (hp == 0) {
+ msg_warn("%s: hostname %s verification failed: %s",
+ state->addr, state->name, HSTRERROR(h_errno));
+ REJECT_PEER_NAME(state);
+ } else if (hp->h_length != sizeof(sin.sin_addr)) {
+ msg_warn("%s: hostname %s verification failed: bad address size %d",
+ state->addr, state->name, hp->h_length);
+ REJECT_PEER_NAME(state);
+ } else {
+ for (i = 0; /* void */ ; i++) {
+ if (hp->h_addr_list[i] == 0) {
+ msg_warn("%s: address not listed for hostname %s",
+ state->addr, state->name);
+ REJECT_PEER_NAME(state);
+ break;
+ }
+ if (memcmp(hp->h_addr_list[i],
+ (char *) &sin.sin_addr,
+ sizeof(sin.sin_addr)) == 0)
+ break; /* keep peer name */
+ }
+ }
+ }
+ }
+
+ /*
+ * If it's not Internet, assume the client is local, and avoid using the
+ * naming service because that can hang when the machine is disconnected.
+ */
+ else {
+ state->name = mystrdup("localhost");
+ state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */
+ }
+
+ /*
+ * Do the name[addr] formatting for pretty reports.
+ */
+ state->namaddr =
+ concatenate(state->name, "[", state->addr, "]", (char *) 0);
+}
+
+/* qmqpd_peer_reset - destroy peer information */
+
+void qmqpd_peer_reset(QMQPD_STATE *state)
+{
+ myfree(state->name);
+ myfree(state->addr);
+ myfree(state->namaddr);
+}
diff --git a/gnu/dist/postfix/src/qmqpd/qmqpd_state.c b/gnu/dist/postfix/src/qmqpd/qmqpd_state.c
new file mode 100644
index 00000000000..8e5b46c9c4e
--- /dev/null
+++ b/gnu/dist/postfix/src/qmqpd/qmqpd_state.c
@@ -0,0 +1,98 @@
+/*++
+/* NAME
+/* qmqpd_state 3
+/* SUMMARY
+/* Postfix QMQP server
+/* SYNOPSIS
+/* #include "qmqpd.h"
+/*
+/* QMQPD_STATE *qmqpd_state_alloc(stream)
+/* VSTREAM *stream;
+/*
+/* void qmqpd_state_free(state)
+/* QMQPD_STATE *state;
+/* DESCRIPTION
+/* qmqpd_state_alloc() creates and initializes session context.
+/*
+/* qmqpd_state_free() destroys session context.
+/*
+/* Arguments:
+/* .IP stream
+/* Stream connected to peer. The stream is not copied.
+/* DIAGNOSTICS
+/* All errors are fatal.
+/* 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 <time.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_stream.h>
+#include <cleanup_user.h>
+
+/* Application-specific. */
+
+#include <qmqpd.h>
+
+/* qmqpd_state_alloc - allocate and initialize session state */
+
+QMQPD_STATE *qmqpd_state_alloc(VSTREAM *stream)
+{
+ QMQPD_STATE *state;
+
+ state = (QMQPD_STATE *) mymalloc(sizeof(*state));
+ state->err = CLEANUP_STAT_OK;
+ state->client = stream;
+ state->message = vstring_alloc(1000);
+ state->buf = vstring_alloc(100);
+ state->time = time((time_t *) 0);
+ qmqpd_peer_init(state);
+ state->queue_id = 0;
+ state->cleanup = 0;
+ state->dest = 0;
+ state->rcpt_count = 0;
+ state->reason = 0;
+ state->sender = 0;
+ state->recipient = 0;
+ state->protocol = "QMQP";
+ state->where = "initializing client connection";
+ state->why_rejected = vstring_alloc(10);
+ return (state);
+}
+
+/* qmqpd_state_free - destroy session state */
+
+void qmqpd_state_free(QMQPD_STATE *state)
+{
+ vstring_free(state->message);
+ vstring_free(state->buf);
+ qmqpd_peer_reset(state);
+ if (state->queue_id)
+ myfree(state->queue_id);
+ if (state->dest)
+ mail_stream_cleanup(state->dest);
+ if (state->sender)
+ myfree(state->sender);
+ if (state->recipient)
+ myfree(state->recipient);
+ vstring_free(state->why_rejected);
+ myfree((char *) state);
+}
diff --git a/gnu/dist/postfix/src/sendmail/Makefile.in b/gnu/dist/postfix/src/sendmail/Makefile.in
index 0f9f5a4786c..e037edf1fd3 100644
--- a/gnu/dist/postfix/src/sendmail/Makefile.in
+++ b/gnu/dist/postfix/src/sendmail/Makefile.in
@@ -73,6 +73,7 @@ sendmail.o: ../../include/set_ugid.h
sendmail.o: ../../include/connect.h
sendmail.o: ../../include/mail_queue.h
sendmail.o: ../../include/mail_proto.h
+sendmail.o: ../../include/attr.h
sendmail.o: ../../include/mail_params.h
sendmail.o: ../../include/record.h
sendmail.o: ../../include/rec_type.h
@@ -86,4 +87,4 @@ sendmail.o: ../../include/tok822.h
sendmail.o: ../../include/resolve_clnt.h
sendmail.o: ../../include/mail_flush.h
sendmail.o: ../../include/mail_stream.h
-sendmail.o: ../../include/smtp_stream.h
+sendmail.o: ../../include/verp_sender.h
diff --git a/gnu/dist/postfix/src/sendmail/sendmail.c b/gnu/dist/postfix/src/sendmail/sendmail.c
index a3daf6422a7..54cb2443f2d 100644
--- a/gnu/dist/postfix/src/sendmail/sendmail.c
+++ b/gnu/dist/postfix/src/sendmail/sendmail.c
@@ -19,11 +19,9 @@
/*
/* By default, \fBsendmail\fR reads a message from standard input
/* until EOF or until it reads a line with only a \fB.\fR character,
-/* and arranges for delivery. \fBsendmail\fR attempts to create
-/* a queue file in the \fBmaildrop\fR directory. If that directory
-/* is not world-writable, the message is piped through the
-/* \fBpostdrop\fR(1) command, which is expected to execute with
-/* suitable privileges.
+/* and arranges for delivery. \fBsendmail\fR relies on the
+/* \fBpostdrop\fR(1) command to create a queue file in the \fBmaildrop\fR
+/* directory.
/*
/* Specific command aliases are provided for other common modes of
/* operation:
@@ -32,7 +30,7 @@
/* size, arrival time, sender, and the recipients that still need to
/* be delivered. If mail could not be delivered upon the last attempt,
/* the reason for failure is shown. This mode of operation is implemented
-/* by connecting to the \fBshowq\fR(8) daemon.
+/* by executing the \fBpostqueue\fR(1) command.
/* .IP \fBnewaliases\fR
/* Initialize the alias database. If no input file is specified (with
/* the \fB-oA\fR option, see below), the program processes the file(s)
@@ -65,6 +63,9 @@
/* .IP \fB-I\fR
/* Initialize alias database. See the \fBnewaliases\fR
/* command above.
+/* .IP "\fB-L \fIlabel\fR (ignored)"
+/* The logging label. Use the \fBsyslog_name\fR configuration
+/* parameter instead.
/* .IP "\fB-N \fIdsn\fR (ignored)"
/* Delivery status notification control. Currently, Postfix does
/* not implement \fBDSN\fR.
@@ -76,6 +77,20 @@
/* \fBdebug_peer_level\fR configuration parameters instead.
/* .IP "\fB-U\fR (ignored)"
/* Initial user submission.
+/* .IP \fB-V\fR
+/* Variable Envelope Return Path. Given an envelope sender address
+/* of the form \fIowner-listname\fR@\fIorigin\fR, each recipient
+/* \fIuser\fR@\fIdomain\fR receives mail with a personalized envelope
+/* sender address.
+/* .sp
+/* By default, the personalized envelope sender address is
+/* \fIowner-listname\fB+\fIuser\fB=\fIdomain\fR@\fIorigin\fR. The default
+/* \fB+\fR and \fB=\fR characters are configurable with the
+/* \fBdefault_verp_delimiters\fR configuration parameter.
+/* .IP \fB-V\fIxy\fR
+/* As \fB-V\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter
+/* characters, instead of the characters specified with the
+/* \fBdefault_verp_delimiters\fR configuration parameter.
/* .IP \fB-bd\fR
/* Go into daemon mode. This mode of operation is implemented by
/* executing the \fBpostfix start\fR command.
@@ -127,17 +142,17 @@
/* delivery problems are sent to, unless the message contains an
/* \fBErrors-To:\fR message header.
/* .IP \fB-q\fR
-/* Attempt to deliver all queued mail. This is implemented by kicking the
-/* \fBqmgr\fR(8) daemon.
+/* Attempt to deliver all queued mail. This is implemented by
+/* executing the \fBpostqueue\fR(1) command.
/* .IP "\fB-q\fIinterval\fR (ignored)"
/* The interval between queue runs. Use the \fBqueue_run_delay\fR
/* configuration parameter instead.
/* .IP \fB-qR\fIsite\fR
/* Schedule immediate delivery of all mail that is queued for the named
-/* \fIsite\fR. Depending on the destination, this uses "fast flush"
-/* service, or it has the same effect as \fBsendmail -q\fR.
-/* This is implemented by connecting to the local SMTP server.
-/* See \fBsmtpd\fR(8) for more information about the "fast flush"
+/* \fIsite\fR. This option accepts only \fIsite\fR names that are
+/* eligible for the "fast flush" service, and is implemented by
+/* executing the \fBpostqueue\fR(1) command.
+/* See \fBflush\fR(8) for more information about the "fast flush"
/* service.
/* .IP \fB-qS\fIsite\fR
/* This command is not implemented. Use the slower \fBsendmail -q\fR
@@ -195,12 +210,14 @@
/* List of domain or network patterns. When a remote host matches
/* a pattern, increase the verbose logging level by the amount
/* specified in the \fBdebug_peer_level\fR parameter.
+/* .IP \fBdefault_verp_delimiters\fR
+/* The VERP delimiter characters that are used when the \fB-V\fR
+/* command line option is specified without delimiter characters.
/* .IP \fBfast_flush_domains\fR
/* List of domains that will receive "fast flush" service (default: all
-/* domains that this system is willing to relay mail to). This greatly
-/* improves the performance of the SMTP \fBETRN\fR request, and of the
-/* \fBsendmail -qR\fR command. For domains not in the list, Postfix simply
-/* attempts to deliver all queued mail.
+/* domains that this system is willing to relay mail to). This list
+/* specifies the domains that Postfix accepts in the SMTP \fBETRN\fR
+/* request and in the \fBsendmail -qR\fR command.
/* .IP \fBfork_attempts\fR
/* Number of attempts to \fBfork\fR() a process before giving up.
/* .IP \fBfork_delay\fR
@@ -220,16 +237,17 @@
/* directory of Postfix daemons that run chrooted.
/* .IP \fBqueue_run_delay\fR
/* The time between successive scans of the deferred queue.
+/* .IP \fBverp_delimiter_filter\fR
+/* The characters that Postfix accepts as VERP delimiter characters.
/* SEE ALSO
/* pickup(8) mail pickup daemon
/* postalias(1) maintain alias database
-/* postdrop(1) privileged posting agent
+/* postdrop(1) mail posting utility
/* postfix(1) mail system control
-/* postkick(1) kick a Postfix daemon
+/* postqueue(1) mail queue control
/* qmgr(8) queue manager
-/* showq(8) list mail queue
/* smtpd(8) SMTP server
-/* flushd(8) fast flush service
+/* flush(8) fast flush service
/* syslogd(8) system logging
/* LICENSE
/* .ad
@@ -257,6 +275,7 @@
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
+#include <sysexits.h>
/* Utility library. */
@@ -291,7 +310,7 @@
#include <tok822.h>
#include <mail_flush.h>
#include <mail_stream.h>
-#include <smtp_stream.h>
+#include <verp_sender.h>
/* Application-specific. */
@@ -306,13 +325,6 @@
#define SM_MODE_FLUSHQ 6 /* user (stand-alone) mode */
/*
- * Queue file name. Global, so that the cleanup routine can find it when
- * called by the run-time error handler.
- */
-static char *sendmail_path;
-static void sendmail_cleanup(void);
-
- /*
* Flag parade.
*/
#define SM_FLAG_AEOF (1<<0) /* archaic EOF */
@@ -320,6 +332,11 @@ static void sendmail_cleanup(void);
#define SM_FLAG_DEFAULT (SM_FLAG_AEOF)
/*
+ * VERP support.
+ */
+char *verp_delims;
+
+ /*
* Silly little macros (SLMs).
*/
#define STR vstring_str
@@ -345,7 +362,6 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
char *postdrop_command;
uid_t uid = getuid();
int status;
- struct stat st;
int naddr;
/*
@@ -370,31 +386,21 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
msg_warn("-f option specified malformed sender: %s", sender);
} else {
if ((sender = username()) == 0)
- msg_fatal("unable to find out your login name");
+ msg_fatal_status(EX_OSERR, "unable to find out your login name");
saved_sender = mystrdup(sender);
}
/*
- * Open the queue file. Save the queue file name, so the run-time error
- * handler can clean up in case of errors.
- *
- * If the queue is not world-writable, let the postdrop command open the
- * queue file.
+ * Let the postdrop command open the queue file for us, and sanity check
+ * the content. XXX Make postdrop a manifest constant.
*/
- if (stat(MAIL_QUEUE_MAILDROP, &st) < 0)
- msg_fatal("No maildrop directory %s: %m", MAIL_QUEUE_MAILDROP);
- if (st.st_mode & S_IWOTH) {
- handle = mail_stream_file(MAIL_QUEUE_MAILDROP,
- MAIL_CLASS_PUBLIC, MAIL_SERVICE_PICKUP);
- sendmail_path = mystrdup(VSTREAM_PATH(handle->stream));
- } else {
- postdrop_command = concatenate(var_command_dir, "/postdrop",
+ errno = 0;
+ postdrop_command = concatenate(var_command_dir, "/postdrop -r",
msg_verbose ? " -v" : (char *) 0, (char *) 0);
- if ((handle = mail_stream_command(postdrop_command)) == 0)
- msg_fatal("%s(%ld): unable to execute %s",
- saved_sender, (long) uid, postdrop_command);
- myfree(postdrop_command);
- }
+ if ((handle = mail_stream_command(postdrop_command)) == 0)
+ msg_fatal_status(EX_UNAVAILABLE, "%s(%ld): unable to execute %s: %m",
+ saved_sender, (long) uid, postdrop_command);
+ myfree(postdrop_command);
dst = handle->stream;
/*
@@ -414,6 +420,10 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
if (full_name || (full_name = fullname()) != 0)
rec_fputs(dst, REC_TYPE_FULL, full_name);
rec_fputs(dst, REC_TYPE_FROM, saved_sender);
+ if (verp_delims && *saved_sender == 0)
+ msg_fatal("-V option requires non-null sender address");
+ if (verp_delims)
+ rec_fputs(dst, REC_TYPE_VERP, verp_delims);
if (recipients) {
for (cpp = recipients; *cpp != 0; cpp++) {
tree = tok822_parse(*cpp);
@@ -421,8 +431,9 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
if (tp->type == TOK822_ADDR) {
tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
if (REC_PUT_BUF(dst, REC_TYPE_RCPT, buf) < 0)
- msg_fatal("%s(%ld): error writing queue file: %m",
- saved_sender, (long) uid);
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ saved_sender, (long) uid);
}
}
tok822_free_tree(tree);
@@ -463,8 +474,9 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
if ((flags & SM_FLAG_AEOF) && VSTRING_LEN(buf) == 1 && *STR(buf) == '.')
break;
if (REC_PUT_BUF(dst, type, buf) < 0)
- msg_fatal("%s(%ld): error writing queue file: %m",
- saved_sender, (long) uid);
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ saved_sender, (long) uid);
}
/*
@@ -485,15 +497,13 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
* handler from removing the file.
*/
if (vstream_ferror(VSTREAM_IN))
- msg_fatal("%s(%ld): error reading input: %m",
- saved_sender, (long) uid);
- if ((status = mail_stream_finish(handle)) != 0)
- msg_fatal("%s(%ld): %s", saved_sender,
- (long) uid, cleanup_strerror(status));
- if (sendmail_path) {
- myfree(sendmail_path);
- sendmail_path = 0;
- }
+ msg_fatal_status(EX_DATAERR, "%s(%ld): error reading input: %m",
+ saved_sender, (long) uid);
+ if ((status = mail_stream_finish(handle, (VSTRING *) 0)) != 0)
+ msg_fatal_status((status & CLEANUP_STAT_BAD) ? EX_SOFTWARE :
+ (status & CLEANUP_STAT_WRITE) ? EX_TEMPFAIL :
+ EX_UNAVAILABLE, "%s(%ld): %s", saved_sender,
+ (long) uid, cleanup_strerror(status));
/*
* Cleanup. Not really necessary as we're about to exit, but good for
@@ -503,168 +513,6 @@ static void enqueue(const int flags, const char *sender, const char *full_name,
myfree(saved_sender);
}
-/* show_queue - show queue status */
-
-static void show_queue(void)
-{
- char buf[VSTREAM_BUFSIZE];
- VSTREAM *showq;
- int n;
-
- /*
- * Connect to the show queue service. Terminate silently when piping into
- * a program that terminates early.
- */
- signal(SIGPIPE, SIG_DFL);
- if ((showq = mail_connect(MAIL_CLASS_PUBLIC, MAIL_SERVICE_SHOWQ, BLOCKING)) != 0) {
- while ((n = vstream_fread(showq, buf, sizeof(buf))) > 0)
- if (vstream_fwrite(VSTREAM_OUT, buf, n) != n
- || vstream_fflush(VSTREAM_OUT) != 0)
- msg_fatal("write error: %m");
-
- if (vstream_fclose(showq))
- msg_warn("close: %m");
- }
-
- /*
- * When the mail system is down, the superuser can still access the queue
- * directly. Just run the showq program in stand-alone mode.
- */
- else if (geteuid() == 0) {
- ARGV *argv;
- int stat;
-
- msg_warn("Mail system is down -- accessing queue directly");
- argv = argv_alloc(6);
- argv_add(argv, MAIL_SERVICE_SHOWQ, "-c", "-u", "-S", (char *) 0);
- for (n = 0; n < msg_verbose; n++)
- argv_add(argv, "-v", (char *) 0);
- argv_terminate(argv);
- stat = mail_run_foreground(var_daemon_dir, argv->argv);
- argv_free(argv);
- }
-
- /*
- * When the mail system is down, unprivileged users are stuck, because by
- * design the mail system contains no set_uid programs. The only way for
- * an unprivileged user to cross protection boundaries is to talk to the
- * showq daemon.
- */
- else {
- msg_fatal("Queue report unavailable - mail system is down");
- }
-}
-
-/* flush_queue - force delivery */
-
-static void flush_queue(void)
-{
-
- /*
- * Trigger the flush queue service.
- */
- if (mail_flush_deferred() < 0)
- msg_warn("Cannot flush mail queue - mail system is down");
-}
-
-/* chat - send command and examine reply */
-
-static void chat(VSTREAM *fp, VSTRING *buf, const char *fmt,...)
-{
- va_list ap;
-
- smtp_get(buf, fp, var_line_limit);
- if (STR(buf)[0] != '2')
- msg_fatal("server rejected request: %s", STR(buf));
-
- if (msg_verbose)
- msg_info("<<< %s", STR(buf));
-
- if (msg_verbose) {
- va_start(ap, fmt);
- vstring_vsprintf(buf, fmt, ap);
- va_end(ap);
- msg_info(">>> %s", STR(buf));
- }
- va_start(ap, fmt);
- smtp_vprintf(fp, fmt, ap);
- va_end(ap);
-}
-
-/* flush_site - flush mail for site */
-
-static void flush_site(const char *site)
-{
- VSTRING *buf = vstring_alloc(10);
- VSTREAM *fp;
- int sock;
- int status;
-
- /*
- * Make connection to the local SMTP server. Translate "connection
- * refused" into something less misleading.
- */
- vstring_sprintf(buf, "%s:smtp", var_myhostname);
- if ((sock = inet_connect(STR(buf), BLOCKING, 10)) < 0) {
- if (errno == ECONNREFUSED)
- msg_fatal("mail service at %s is down", var_myhostname);
- msg_fatal("connect to mail service at %s: %m", var_myhostname);
- }
- fp = vstream_fdopen(sock, O_RDWR);
-
- /*
- * Prepare for trouble.
- */
- vstream_control(fp, VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END);
- status = vstream_setjmp(fp);
- if (status != 0) {
- switch (status) {
- case SMTP_ERR_EOF:
- msg_fatal("server at %s aborted connection", var_myhostname);
- case SMTP_ERR_TIME:
- msg_fatal("timeout while talking to server at %s", var_myhostname);
- }
- }
- smtp_timeout_setup(fp, 60);
-
- /*
- * Chat with the SMTP server.
- */
- chat(fp, buf, "helo %s", var_myhostname);
- chat(fp, buf, "etrn %s", site);
- chat(fp, buf, "quit");
-
- vstream_fclose(fp);
- vstring_free(buf);
-}
-
-/* sendmail_cleanup - callback for the runtime error handler */
-
-static void sendmail_cleanup(void)
-{
-
- /*
- * We're possibly running from a signal handler, so we should not be
- * doing complicated things such as memory of buffer management, but if
- * for some reason we can't cleanup it is even worse to just die quietly.
- */
- if (sendmail_path) {
- if (remove(sendmail_path))
- msg_warn("sendmail_cleanup: remove %s: %m", sendmail_path);
- else if (msg_verbose)
- msg_info("remove %s", sendmail_path);
- sendmail_path = 0;
- }
-}
-
-/* sendmail_sig - catch signal and clean up */
-
-static void sendmail_sig(int sig)
-{
- sendmail_cleanup();
- exit(sig);
-}
-
/* main - the main program */
int main(int argc, char **argv)
@@ -697,7 +545,7 @@ int main(int argc, char **argv)
for (fd = 0; fd < 3; fd++)
if (fstat(fd, &st) == -1
&& (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
- msg_fatal("open /dev/null: %m");
+ msg_fatal_status(EX_UNAVAILABLE, "open /dev/null: %m");
/*
* The CDE desktop calendar manager leaks a parent file descriptor into
@@ -745,15 +593,16 @@ int main(int argc, char **argv)
*/
mail_conf_read();
if (chdir(var_queue_dir))
- msg_fatal("chdir %s: %m", var_queue_dir);
+ msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);
- signal(SIGPIPE, SIG_IGN);
+ /*
+ * Stop run-away process accidents by limiting the queue file size. This
+ * is not a defense against DOS attack.
+ */
+ if (var_message_limit > 0 && get_file_limit() > var_message_limit)
+ set_file_limit((off_t) var_message_limit);
- signal(SIGHUP, sendmail_sig);
- signal(SIGINT, sendmail_sig);
- signal(SIGQUIT, sendmail_sig);
- signal(SIGTERM, sendmail_sig);
- msg_cleanup(sendmail_cleanup);
+ signal(SIGPIPE, SIG_IGN);
/*
* Optionally start the debugger on ourself. This must be done after
@@ -794,7 +643,12 @@ int main(int argc, char **argv)
optind++;
continue;
}
- if ((c = GETOPT(argc, argv, "B:C:F:GIN:R:UX:b:ce:f:h:imno:p:r:q:tvx")) <= 0)
+ if (strcmp(argv[OPTIND], "-V") == 0) {
+ verp_delims = var_verp_delims;
+ optind++;
+ continue;
+ }
+ if ((c = GETOPT(argc, argv, "B:C:F:GIL:N:R:UV:X:b:ce:f:h:imno:p:r:q:tvx")) <= 0)
break;
switch (c) {
default:
@@ -802,25 +656,23 @@ int main(int argc, char **argv)
msg_info("-%c option ignored", c);
break;
case 'n':
- msg_fatal("-%c option not supported", c);
- case 'B': /* body type */
- break;
+ msg_fatal_status(EX_USAGE, "-%c option not supported", c);
case 'F': /* full name */
full_name = optarg;
break;
- case 'G': /* gateway submission */
- break;
case 'I': /* newaliases */
mode = SM_MODE_NEWALIAS;
break;
- case 'N': /* DSN */
- break;
- case 'R': /* DSN */
+ case 'V': /* VERP */
+ if (verp_delims_verify(optarg) != 0)
+ msg_fatal_status(EX_USAGE, "-V requires two characters from %s",
+ var_verp_filter);
+ verp_delims = optarg;
break;
case 'b':
switch (*optarg) {
default:
- msg_fatal("unsupported: -%c%c", c, *optarg);
+ msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg);
case 'd': /* daemon mode */
if (mode == SM_MODE_FLUSHQ)
msg_warn("ignoring -q option in daemon mode");
@@ -854,7 +706,7 @@ int main(int argc, char **argv)
break;
case 'A':
if (optarg[1] == 0)
- msg_fatal("-oA requires pathname");
+ msg_fatal_status(EX_USAGE, "-oA requires pathname");
myfree(var_alias_db_map);
var_alias_db_map = mystrdup(optarg + 1);
set_mail_conf_str(VAR_ALIAS_DB_MAP, var_alias_db_map);
@@ -881,8 +733,11 @@ int main(int argc, char **argv)
}
} else if (optarg[0] == 'R') {
site_to_flush = optarg + 1;
+ if (*site_to_flush == 0)
+ msg_fatal("specify: -qRsitename");
} else {
- msg_fatal("-q%c is not implemented", optarg[0]);
+ msg_fatal_status(EX_USAGE, "-q%c is not implemented",
+ optarg[0]);
}
break;
case 't':
@@ -892,7 +747,7 @@ int main(int argc, char **argv)
msg_verbose++;
break;
case '?':
- msg_fatal("usage: %s [options]", argv[0]);
+ msg_fatal_status(EX_USAGE, "usage: %s [options]", argv[0]);
}
}
@@ -909,29 +764,47 @@ int main(int argc, char **argv)
msg_fatal("cannot handle command-line recipients with -t");
/*
- * Start processing. Some modes are implemented internally (enqueue
- * message), or as network clients (show queue, flush queue); everything
- * else is delegated to external commands.
+ * Start processing. Everything is delegated to external commands.
*/
switch (mode) {
default:
msg_panic("unknown operation mode: %d", mode);
/* NOTREACHED */
case SM_MODE_ENQUEUE:
- if (site_to_flush)
- flush_site(site_to_flush);
- else
+ if (site_to_flush == 0) {
enqueue(flags, sender, full_name, argv + OPTIND);
- exit(0);
+ exit(0);
+ }
+ if (argv[OPTIND])
+ msg_fatal("flush site requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postqueue", "-s", site_to_flush, (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
break;
case SM_MODE_MAILQ:
- show_queue();
- exit(0);
- break;
+ if (argv[OPTIND])
+ msg_fatal("display queue mode requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postqueue", "-p", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
case SM_MODE_FLUSHQ:
- flush_queue();
- exit(0);
- break;
+ if (argv[OPTIND])
+ msg_fatal("flush queue mode requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postqueue", "-f", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
case SM_MODE_DAEMON:
if (argv[OPTIND])
msg_fatal("daemon mode requires no recipient");
@@ -955,6 +828,7 @@ int main(int argc, char **argv)
for (n = 0; n < msg_verbose; n++)
argv_add(ext_argv, "-v", (char *) 0);
argv_split_append(ext_argv, var_alias_db_map, ", \t\r\n");
+ argv_terminate(ext_argv);
mail_run_replace(var_command_dir, ext_argv->argv);
/* NOTREACHED */
case SM_MODE_USER:
diff --git a/gnu/dist/postfix/src/showq/Makefile.in b/gnu/dist/postfix/src/showq/Makefile.in
index 23d15c420d5..5c714ef1145 100644
--- a/gnu/dist/postfix/src/showq/Makefile.in
+++ b/gnu/dist/postfix/src/showq/Makefile.in
@@ -68,6 +68,7 @@ showq.o: ../../include/mail_queue.h
showq.o: ../../include/mail_open_ok.h
showq.o: ../../include/mail_proto.h
showq.o: ../../include/iostuff.h
+showq.o: ../../include/attr.h
showq.o: ../../include/mail_date.h
showq.o: ../../include/mail_params.h
showq.o: ../../include/mail_scan_dir.h
diff --git a/gnu/dist/postfix/src/showq/showq.c b/gnu/dist/postfix/src/showq/showq.c
index 55cab747a91..97147adeae8 100644
--- a/gnu/dist/postfix/src/showq/showq.c
+++ b/gnu/dist/postfix/src/showq/showq.c
@@ -92,6 +92,7 @@ int var_dup_filter_limit;
#define STRING_FORMAT "%-10s %8s %-20s %s\n"
#define DATA_FORMAT "%-10s%c%8ld %20.20s %s\n"
+#define DROP_FORMAT "%-10s%c%8ld %20.20s (maildrop queue, sender UID %u)\n"
static void showq_reasons(VSTREAM *, BOUNCE_LOG *, HTABLE *);
@@ -106,6 +107,7 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
BOUNCE_LOG *logfile;
HTABLE *dup_filter = 0;
char status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' : ' ');
+ long offset;
while (!vstream_ferror(client) && (rec_type = rec_get(qfile, buf, 0)) > 0) {
start = vstring_str(buf);
@@ -114,7 +116,8 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
arrival_time = atol(start);
break;
case REC_TYPE_SIZE:
- msg_size = atol(start);
+ if ((msg_size = atol(start)) <= 0)
+ msg_size = size;
break;
case REC_TYPE_FROM:
if (*start == 0)
@@ -132,7 +135,8 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
"", "", "", printable(start, '?'));
break;
case REC_TYPE_MESG:
- if (vstream_fseek(qfile, atol(start), SEEK_SET) < 0)
+ if ((offset = atol(start)) > 0
+ && vstream_fseek(qfile, offset, SEEK_SET) < 0)
msg_fatal("seek file %s: %m", VSTREAM_PATH(qfile));
break;
case REC_TYPE_END:
@@ -203,7 +207,6 @@ static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, HTABLE *dup_filter)
static void showq_service(VSTREAM *client, char *unused_service, char **argv)
{
- char **queue;
VSTREAM *qfile;
const char *path;
int status;
@@ -211,11 +214,17 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv)
int file_count;
unsigned long queue_size = 0;
struct stat st;
- char *queue_names[] = { /* XXX configurable */
- MAIL_QUEUE_INCOMING,
- MAIL_QUEUE_ACTIVE,
- MAIL_QUEUE_DEFERRED,
- /* No maildrop until we can disable recursive scans. */
+ struct queue_info {
+ char *name; /* queue name */
+ char *(*scan_next) (SCAN_DIR *); /* flat or recursive */
+ };
+ struct queue_info *qp;
+
+ static struct queue_info queue_info[] = {
+ MAIL_QUEUE_MAILDROP, scan_dir_next,
+ MAIL_QUEUE_INCOMING, mail_scan_dir_next,
+ MAIL_QUEUE_ACTIVE, mail_scan_dir_next,
+ MAIL_QUEUE_DEFERRED, mail_scan_dir_next,
0,
};
@@ -231,11 +240,11 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv)
* mis-configured, and force backoff by raising a fatal error.
*/
file_count = 0;
- for (queue = queue_names; *queue != 0; queue++) {
- SCAN_DIR *scan = scan_dir_open(*queue);
+ for (qp = queue_info; qp->name != 0; qp++) {
+ SCAN_DIR *scan = scan_dir_open(qp->name);
char *saved_id = 0;
- while ((id = mail_scan_dir_next(scan)) != 0) {
+ while ((id = qp->scan_next(scan)) != 0) {
/*
* XXX I have seen showq loop on the same queue id. That would be
@@ -244,13 +253,13 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv)
*/
if (saved_id) {
if (strcmp(saved_id, id) == 0) {
- msg_warn("readdir loop on queue %s id %s", *queue, id);
+ msg_warn("readdir loop on queue %s id %s", qp->name, id);
break;
}
myfree(saved_id);
}
saved_id = mystrdup(id);
- status = mail_open_ok(*queue, id, &st, &path);
+ status = mail_open_ok(qp->name, id, &st, &path);
if (status == MAIL_OPEN_YES) {
if (file_count == 0)
vstream_fprintf(client, STRING_FORMAT,
@@ -259,19 +268,19 @@ static void showq_service(VSTREAM *client, char *unused_service, char **argv)
"-Sender/Recipient-------");
else
vstream_fprintf(client, "\n");
- if ((qfile = mail_queue_open(*queue, id, O_RDONLY, 0)) != 0) {
+ if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
queue_size += st.st_size;
- showq_report(client, *queue, id, qfile, (long) st.st_size);
+ showq_report(client, qp->name, id, qfile, (long) st.st_size);
if (vstream_fclose(qfile))
- msg_warn("close file %s %s: %m", *queue, id);
- } else if (strcmp(*queue, MAIL_QUEUE_MAILDROP) == 0) {
+ msg_warn("close file %s %s: %m", qp->name, id);
+ } else if (strcmp(qp->name, MAIL_QUEUE_MAILDROP) == 0) {
queue_size += st.st_size;
- vstream_fprintf(client, DATA_FORMAT, id, ' ',
+ vstream_fprintf(client, DROP_FORMAT, id, ' ',
(long) st.st_size,
asctime(localtime(&st.st_mtime)),
- "(to be determined)");
+ (unsigned) st.st_uid);
} else if (errno != ENOENT)
- msg_fatal("open %s %s: %m", *queue, id);
+ msg_fatal("open %s %s: %m", qp->name, id);
file_count++;
vstream_fflush(client);
}
diff --git a/gnu/dist/postfix/src/smtp/Makefile.in b/gnu/dist/postfix/src/smtp/Makefile.in
index ed46099b665..035cd5acab4 100644
--- a/gnu/dist/postfix/src/smtp/Makefile.in
+++ b/gnu/dist/postfix/src/smtp/Makefile.in
@@ -80,6 +80,7 @@ smtp.o: ../../include/mail_error.h
smtp.o: ../../include/deliver_pass.h
smtp.o: ../../include/mail_proto.h
smtp.o: ../../include/iostuff.h
+smtp.o: ../../include/attr.h
smtp.o: ../../include/mail_server.h
smtp.o: smtp.h
smtp.o: smtp_sasl.h
@@ -91,6 +92,7 @@ smtp_addr.o: ../../include/vbuf.h
smtp_addr.o: ../../include/mymalloc.h
smtp_addr.o: ../../include/inet_addr_list.h
smtp_addr.o: ../../include/stringops.h
+smtp_addr.o: ../../include/myrand.h
smtp_addr.o: ../../include/mail_params.h
smtp_addr.o: ../../include/own_inet_addr.h
smtp_addr.o: ../../include/dns.h
@@ -149,6 +151,7 @@ smtp_proto.o: ../../include/vstream.h
smtp_proto.o: ../../include/vstring_vstream.h
smtp_proto.o: ../../include/stringops.h
smtp_proto.o: ../../include/mymalloc.h
+smtp_proto.o: ../../include/iostuff.h
smtp_proto.o: ../../include/mail_params.h
smtp_proto.o: ../../include/smtp_stream.h
smtp_proto.o: ../../include/mail_queue.h
@@ -177,6 +180,8 @@ smtp_sasl_glue.o: ../../include/split_at.h
smtp_sasl_glue.o: ../../include/name_mask.h
smtp_sasl_glue.o: ../../include/mail_params.h
smtp_sasl_glue.o: ../../include/string_list.h
+smtp_sasl_glue.o: ../../include/match_list.h
+smtp_sasl_glue.o: ../../include/match_ops.h
smtp_sasl_glue.o: ../../include/maps.h
smtp_sasl_glue.o: ../../include/dict.h
smtp_sasl_glue.o: ../../include/vstream.h
diff --git a/gnu/dist/postfix/src/smtp/smtp.c b/gnu/dist/postfix/src/smtp/smtp.c
index 477e33b7197..9c61d89dbba 100644
--- a/gnu/dist/postfix/src/smtp/smtp.c
+++ b/gnu/dist/postfix/src/smtp/smtp.c
@@ -41,6 +41,7 @@
/* RFC 1870 (Message Size Declaration)
/* RFC 2197 (Pipelining)
/* RFC 2554 (AUTH command)
+/* RFC 2821 (SMTP protocol)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* Corrupted message files are marked so that the queue manager can
@@ -94,14 +95,23 @@
/* Always send EHLO at the start of a connection.
/* .IP \fBsmtp_never_send_ehlo\fR
/* Never send EHLO at the start of a connection.
+/* .IP \fBsmtp_bind_address\fR
+/* Numerical source network address to bind to when making a connection.
+/* .IP \fBsmtp_break_lines\fR
+/* Break lines > \fB$line_length_limit\fR into multiple shorter lines.
+/* Some SMTP servers misbehave on long lines.
/* .IP \fBsmtp_skip_4xx_greeting\fR
/* Skip servers that greet us with a 4xx status code.
/* .IP \fBsmtp_skip_5xx_greeting\fR
/* Skip servers that greet us with a 5xx status code.
/* .IP \fBsmtp_skip_quit_response\fR
/* Do not wait for the server response after sending QUIT.
-/* .IP \fBsmtp_bind_address\fR
-/* Numerical network address to bind to when making a connection.
+/* .IP \fBsmtp_pix_workaround_delay_time\fR
+/* The time to pause before sending .<CR><LF>, while working
+/* around the CISCO PIX firewall <CR><LF>.<CR><LF> bug.
+/* .IP \fBsmtp_pix_workaround_threshold_time\fR
+/* The time a message must be queued before the CISCO PIX firewall
+/* <CR><LF>.<CR><LF> bug workaround is turned on.
/* .SH "Authentication controls"
/* .IP \fBsmtp_enable_sasl_auth\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
@@ -234,8 +244,6 @@ int var_smtp_data1_tmout;
int var_smtp_data2_tmout;
int var_smtp_quit_tmout;
char *var_inet_interfaces;
-char *var_debug_peer_list;
-int var_debug_peer_level;
char *var_notify_classes;
int var_smtp_skip_4xx_greeting;
int var_smtp_skip_5xx_greeting;
@@ -250,6 +258,10 @@ char *var_smtp_sasl_opts;
char *var_smtp_sasl_passwd;
bool var_smtp_sasl_enable;
char *var_smtp_bind_addr;
+bool var_smtp_rand_addr;
+bool var_smtp_break_lines;
+int var_smtp_pix_thresh;
+int var_smtp_pix_delay;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
@@ -394,7 +406,6 @@ static void pre_exit(void)
int main(int argc, char **argv)
{
static CONFIG_STR_TABLE str_table[] = {
- VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0,
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_FALLBACK_RELAY, DEF_FALLBACK_RELAY, &var_fallback_relay, 0, 0,
VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0,
@@ -413,10 +424,11 @@ int main(int argc, char **argv)
VAR_SMTP_DATA1_TMOUT, DEF_SMTP_DATA1_TMOUT, &var_smtp_data1_tmout, 1, 0,
VAR_SMTP_DATA2_TMOUT, DEF_SMTP_DATA2_TMOUT, &var_smtp_data2_tmout, 1, 0,
VAR_SMTP_QUIT_TMOUT, DEF_SMTP_QUIT_TMOUT, &var_smtp_quit_tmout, 1, 0,
+ VAR_SMTP_PIX_THRESH, DEF_SMTP_PIX_THRESH, &var_smtp_pix_thresh, 0, 0,
+ VAR_SMTP_PIX_DELAY, DEF_SMTP_PIX_DELAY, &var_smtp_pix_delay, 1, 0,
0,
};
static CONFIG_INT_TABLE int_table[] = {
- VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
@@ -427,6 +439,8 @@ int main(int argc, char **argv)
VAR_SMTP_ALWAYS_EHLO, DEF_SMTP_ALWAYS_EHLO, &var_smtp_always_ehlo,
VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo,
VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
+ VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr,
+ VAR_SMTP_BREAK_LINES, DEF_SMTP_BREAK_LINES, &var_smtp_break_lines,
0,
};
diff --git a/gnu/dist/postfix/src/smtp/smtp.h b/gnu/dist/postfix/src/smtp/smtp.h
index 8c6dd5b0636..81990e8a2f5 100644
--- a/gnu/dist/postfix/src/smtp/smtp.h
+++ b/gnu/dist/postfix/src/smtp/smtp.h
@@ -52,6 +52,7 @@ typedef struct SMTP_STATE {
VSTRING *sasl_decoded; /* decoding buffer */
sasl_callback_t *sasl_callbacks; /* stateful callbacks */
#endif
+ off_t size_limit; /* server limit or unknown */
} SMTP_STATE;
#define SMTP_FEATURE_ESMTP (1<<0)
@@ -60,6 +61,7 @@ typedef struct SMTP_STATE {
#define SMTP_FEATURE_SIZE (1<<3)
#define SMTP_FEATURE_STARTTLS (1<<4)
#define SMTP_FEATURE_AUTH (1<<5)
+#define SMTP_FEATURE_MAYBEPIX (1<<6) /* PIX smtp fixup mode */
/*
* smtp.c
diff --git a/gnu/dist/postfix/src/smtp/smtp_chat.c b/gnu/dist/postfix/src/smtp/smtp_chat.c
index 62c0de64f3f..d33a90e4e04 100644
--- a/gnu/dist/postfix/src/smtp/smtp_chat.c
+++ b/gnu/dist/postfix/src/smtp/smtp_chat.c
@@ -252,7 +252,7 @@ void smtp_chat_notify(SMTP_STATE *state)
notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
var_error_rcpt,
- NULL_CLEANUP_FLAGS, "NOTICE");
+ NULL_CLEANUP_FLAGS);
if (notice == 0) {
msg_warn("postmaster notify: %m");
return;
diff --git a/gnu/dist/postfix/src/smtp/smtp_proto.c b/gnu/dist/postfix/src/smtp/smtp_proto.c
index a7871312379..04e0db3fcde 100644
--- a/gnu/dist/postfix/src/smtp/smtp_proto.c
+++ b/gnu/dist/postfix/src/smtp/smtp_proto.c
@@ -82,6 +82,7 @@
#include <vstring_vstream.h>
#include <stringops.h>
#include <mymalloc.h>
+#include <iostuff.h>
/* Global library. */
@@ -174,6 +175,14 @@ int smtp_helo(SMTP_STATE *state)
session->namaddr, translit(resp->str, "\n", " ")));
/*
+ * XXX Some PIX firewall versions require flush before ".<CR><LF>" so it
+ * does not span a packet boundary. This hurts performance so it is not
+ * on by default.
+ */
+ if (resp->str[strspn(resp->str, "20 *\t\n")] == 0)
+ state->features |= SMTP_FEATURE_MAYBEPIX;
+
+ /*
* See if we are talking to ourself. This should not be possible with the
* way we implement DNS lookups. However, people are known to sometimes
* screw up the naming service. And, mailer loops are still possible when
@@ -188,9 +197,9 @@ int smtp_helo(SMTP_STATE *state)
} else if (strcasecmp(word, "ESMTP") == 0)
state->features |= SMTP_FEATURE_ESMTP;
}
- if (var_smtp_always_ehlo)
+ if (var_smtp_always_ehlo && (state->features & SMTP_FEATURE_MAYBEPIX) == 0)
state->features |= SMTP_FEATURE_ESMTP;
- if (var_smtp_never_ehlo)
+ if (var_smtp_never_ehlo || (state->features & SMTP_FEATURE_MAYBEPIX) != 0)
state->features &= ~SMTP_FEATURE_ESMTP;
/*
@@ -217,21 +226,30 @@ int smtp_helo(SMTP_STATE *state)
* overflow detection, ignore the message size limit advertised by the
* SMTP server. Otherwise, we might do the wrong thing when the server
* advertises a really huge message size limit.
+ *
+ * XXX Allow for "code (SP|-) ehlo-keyword (SP|=) ehlo-param...", because
+ * MicroSoft implemented AUTH based on an old draft.
*/
lines = resp->str;
while ((words = mystrtok(&lines, "\n")) != 0) {
- if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
+ if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t=")) != 0) {
if (strcasecmp(word, "8BITMIME") == 0)
state->features |= SMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= SMTP_FEATURE_PIPELINING;
- else if (strcasecmp(word, "SIZE") == 0)
+ else if (strcasecmp(word, "SIZE") == 0) {
state->features |= SMTP_FEATURE_SIZE;
+ if ((word = mystrtok(&words, " \t")) != 0) {
+ if (!alldig(word))
+ msg_warn("bad size limit \"%s\" in EHLO reply from %s",
+ word, session->namaddr);
+ else
+ state->size_limit = off_cvt_string(word);
+ }
+ }
#ifdef USE_SASL_AUTH
else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0)
smtp_sasl_helo_auth(state, words);
- else if (var_smtp_sasl_enable && strncasecmp(word, "AUTH=", 5) == 0)
- smtp_sasl_helo_auth(state, word + 5);
#endif
else if (strcasecmp(word, var_myhostname) == 0) {
msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
@@ -243,7 +261,8 @@ int smtp_helo(SMTP_STATE *state)
}
}
if (msg_verbose)
- msg_info("server features: 0x%x", state->features);
+ msg_info("server features: 0x%x size %.0f",
+ state->features, (double) state->size_limit);
#ifdef USE_SASL_AUTH
if (var_smtp_sasl_enable && (state->features & SMTP_FEATURE_AUTH))
@@ -302,6 +321,19 @@ int smtp_xfer(SMTP_STATE *state)
(recv_state <= SMTP_STATE_DOT)
/*
+ * See if we should even try to send this message at all. This code sits
+ * here rather than in the EHLO processing code, because of future SMTP
+ * connection caching.
+ */
+ if (state->size_limit > 0 && state->size_limit < request->data_size) {
+ smtp_mesg_fail(state, 552,
+ "message size %lu exceeds size limit %.0f of server %s",
+ request->data_size, (double) state->size_limit,
+ session->namaddr);
+ RETURN(0);
+ }
+
+ /*
* We use SMTP command pipelining if the server said it supported it.
* Since we use blocking I/O, RFC 2197 says that we should inspect the
* TCP window size and not send more than this amount of information.
@@ -487,9 +519,19 @@ int smtp_xfer(SMTP_STATE *state)
* rejected, ignore RCPT TO responses: all recipients are
* dead already. When all recipients are rejected the
* receiver may apply a course correction.
+ *
+ * XXX 2821: Section 4.5.3.1 says that a 552 RCPT TO reply
+ * must be treated as if the server replied with 452.
+ * However, this causes "too much mail data" to be
+ * treated as a recoverable error, which is wrong. I'll
+ * stick with RFC 821.
*/
case SMTP_STATE_RCPT:
if (!mail_from_rejected) {
+#ifdef notdef
+ if (resp->code == 552)
+ resp->code = 452;
+#endif
if (resp->code / 100 == 2) {
++nrcpt;
} else {
@@ -623,6 +665,8 @@ int smtp_xfer(SMTP_STATE *state)
if (prev_type != REC_TYPE_CONT)
if (vstring_str(state->scratch)[0] == '.')
smtp_fputc('.', session->stream);
+ if (var_smtp_break_lines)
+ rec_type = REC_TYPE_NORM;
if (rec_type == REC_TYPE_CONT)
smtp_fwrite(vstring_str(state->scratch),
VSTRING_LEN(state->scratch),
@@ -636,6 +680,14 @@ int smtp_xfer(SMTP_STATE *state)
if (prev_type == REC_TYPE_CONT) /* missing newline at end */
smtp_fputs("", 0, session->stream);
+ if ((state->features & SMTP_FEATURE_MAYBEPIX) != 0
+ && request->arrival_time < vstream_ftime(session->stream)
+ - var_smtp_pix_thresh) {
+ msg_info("%s: enabling PIX <CRLF>.<CRLF> workaround for %s",
+ request->queue_id, session->namaddr);
+ vstream_fflush(session->stream);/* hurts performance */
+ sleep(var_smtp_pix_delay); /* not to mention this */
+ }
if (vstream_ferror(state->src))
msg_fatal("queue file read error");
if (rec_type != REC_TYPE_XTRA)
diff --git a/gnu/dist/postfix/src/smtp/smtp_sasl_glue.c b/gnu/dist/postfix/src/smtp/smtp_sasl_glue.c
index 6336e2e5e48..0a4e28fe313 100644
--- a/gnu/dist/postfix/src/smtp/smtp_sasl_glue.c
+++ b/gnu/dist/postfix/src/smtp/smtp_sasl_glue.c
@@ -139,11 +139,11 @@ static int smtp_sasl_log(void *unused_context, int priority,
switch (priority) {
case SASL_LOG_ERR:
case SASL_LOG_WARNING:
- msg_warn("%s", message);
+ msg_warn("SASL authentication problem: %s", message);
break;
case SASL_LOG_INFO:
if (msg_verbose)
- msg_info("%s", message);
+ msg_info("SASL authentication info: %s", message);
break;
}
return (SASL_OK);
diff --git a/gnu/dist/postfix/src/smtp/smtp_sasl_proto.c b/gnu/dist/postfix/src/smtp/smtp_sasl_proto.c
index f3818236520..97c2592c02e 100644
--- a/gnu/dist/postfix/src/smtp/smtp_sasl_proto.c
+++ b/gnu/dist/postfix/src/smtp/smtp_sasl_proto.c
@@ -52,6 +52,10 @@
/* System library. */
#include <sys_defs.h>
+#include <string.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
/* Utility library. */
@@ -79,6 +83,8 @@ void smtp_sasl_helo_auth(SMTP_STATE *state, const char *words)
* then pretend that the server doesn't support SASL authentication.
*/
if (state->sasl_mechanism_list) {
+ if (strcasecmp(state->sasl_mechanism_list, words) == 0)
+ return;
myfree(state->sasl_mechanism_list);
msg_warn("%s offered AUTH option multiple times",
state->session->namaddr);
diff --git a/gnu/dist/postfix/src/smtp/smtp_state.c b/gnu/dist/postfix/src/smtp/smtp_state.c
index 790bdfa6bca..2c4cad35c49 100644
--- a/gnu/dist/postfix/src/smtp/smtp_state.c
+++ b/gnu/dist/postfix/src/smtp/smtp_state.c
@@ -68,6 +68,7 @@ SMTP_STATE *smtp_state_alloc(void)
#ifdef USE_SASL_AUTH
smtp_sasl_connect(state);
#endif
+ state->size_limit = 0;
return (state);
}
diff --git a/gnu/dist/postfix/src/smtpd/Makefile.in b/gnu/dist/postfix/src/smtpd/Makefile.in
index aebfcaeac19..8668041980e 100644
--- a/gnu/dist/postfix/src/smtpd/Makefile.in
+++ b/gnu/dist/postfix/src/smtpd/Makefile.in
@@ -70,19 +70,19 @@ depend: $(MAKES)
tests: smtpd_check_test smtpd_check_test2 smtpd_acl_test smtpd_token_test
smtpd_check_test: smtpd_check smtpd_check.in smtpd_check.ref
- ../postmap/postmap smtpd_check_access
+ ../postmap/postmap hash:smtpd_check_access
./smtpd_check <smtpd_check.in >smtpd_check.tmp 2>&1
diff smtpd_check.ref smtpd_check.tmp
rm -f smtpd_check.tmp smtpd_check_access.*
smtpd_check_test2: smtpd_check smtpd_check.in2 smtpd_check.ref2
- ../postmap/postmap smtpd_check_access
+ ../postmap/postmap hash:smtpd_check_access
./smtpd_check <smtpd_check.in2 >smtpd_check.tmp 2>&1
diff smtpd_check.ref2 smtpd_check.tmp
rm -f smtpd_check.tmp smtpd_check_access.*
smtpd_acl_test: smtpd_check smtpd_acl.in smtpd_acl.ref
- ../postmap/postmap smtpd_check_access
+ ../postmap/postmap hash:smtpd_check_access
./smtpd_check <smtpd_acl.in >smtpd_check.tmp 2>&1
diff smtpd_acl.ref smtpd_check.tmp
rm -f smtpd_check.tmp smtpd_check_access.*
@@ -113,6 +113,7 @@ smtpd.o: ../../include/record.h
smtpd.o: ../../include/rec_type.h
smtpd.o: ../../include/mail_proto.h
smtpd.o: ../../include/iostuff.h
+smtpd.o: ../../include/attr.h
smtpd.o: ../../include/cleanup_user.h
smtpd.o: ../../include/mail_date.h
smtpd.o: ../../include/mail_conf.h
@@ -125,6 +126,10 @@ smtpd.o: ../../include/mail_stream.h
smtpd.o: ../../include/mail_queue.h
smtpd.o: ../../include/tok822.h
smtpd.o: ../../include/resolve_clnt.h
+smtpd.o: ../../include/verp_sender.h
+smtpd.o: ../../include/string_list.h
+smtpd.o: ../../include/match_list.h
+smtpd.o: ../../include/match_ops.h
smtpd.o: ../../include/mail_server.h
smtpd.o: smtpd_token.h
smtpd.o: smtpd.h
@@ -147,6 +152,7 @@ smtpd_chat.o: ../../include/record.h
smtpd_chat.o: ../../include/rec_type.h
smtpd_chat.o: ../../include/mail_proto.h
smtpd_chat.o: ../../include/iostuff.h
+smtpd_chat.o: ../../include/attr.h
smtpd_chat.o: ../../include/mail_params.h
smtpd_chat.o: ../../include/mail_addr.h
smtpd_chat.o: ../../include/post_mail.h
@@ -173,6 +179,8 @@ smtpd_check.o: ../../include/htable.h
smtpd_check.o: ../../include/ctable.h
smtpd_check.o: ../../include/dns.h
smtpd_check.o: ../../include/namadr_list.h
+smtpd_check.o: ../../include/match_list.h
+smtpd_check.o: ../../include/match_ops.h
smtpd_check.o: ../../include/domain_list.h
smtpd_check.o: ../../include/mail_params.h
smtpd_check.o: ../../include/canon_addr.h
@@ -184,6 +192,8 @@ smtpd_check.o: ../../include/own_inet_addr.h
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/match_parent_style.h
+smtpd_check.o: ../../include/split_addr.h
smtpd_check.o: smtpd.h
smtpd_check.o: ../../include/mail_stream.h
smtpd_check.o: smtpd_sasl_glue.h
@@ -205,6 +215,8 @@ smtpd_sasl_glue.o: ../../include/sys_defs.h
smtpd_sasl_glue.o: ../../include/msg.h
smtpd_sasl_glue.o: ../../include/mymalloc.h
smtpd_sasl_glue.o: ../../include/namadr_list.h
+smtpd_sasl_glue.o: ../../include/match_list.h
+smtpd_sasl_glue.o: ../../include/match_ops.h
smtpd_sasl_glue.o: ../../include/name_mask.h
smtpd_sasl_glue.o: ../../include/mail_params.h
smtpd_sasl_glue.o: ../../include/smtp_stream.h
@@ -225,6 +237,7 @@ smtpd_sasl_proto.o: ../../include/mail_proto.h
smtpd_sasl_proto.o: ../../include/vstream.h
smtpd_sasl_proto.o: ../../include/vbuf.h
smtpd_sasl_proto.o: ../../include/iostuff.h
+smtpd_sasl_proto.o: ../../include/attr.h
smtpd_sasl_proto.o: ../../include/mail_error.h
smtpd_sasl_proto.o: ../../include/name_mask.h
smtpd_sasl_proto.o: smtpd.h
diff --git a/gnu/dist/postfix/src/smtpd/smtpd.h b/gnu/dist/postfix/src/smtpd/smtpd.h
index 29fb3d2d743..20386ad73fc 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd.h
+++ b/gnu/dist/postfix/src/smtpd/smtpd.h
@@ -76,6 +76,7 @@ typedef struct SMTPD_STATE {
VSTRING *sasl_encoded;
VSTRING *sasl_decoded;
#endif
+ int warn_if_reject;
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_acl.in b/gnu/dist/postfix/src/smtpd/smtpd_acl.in
index e633c48d561..99955409f73 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_acl.in
+++ b/gnu/dist/postfix/src/smtpd/smtpd_acl.in
@@ -4,6 +4,7 @@
smtpd_delay_reject 0
mynetworks 127.0.0.0/8,168.100.189.0/28
relay_domains porcupine.org
+smtpd_null_access_lookup_key <>
#
# Test check_domain_access()
#
@@ -105,3 +106,7 @@ rcpt ok@ok.domain
rcpt anyone@ok.domain
# Expect: OK
rcpt bad-sender@ok.domain
+#
+# check_sender_access specific
+#
+mail <>
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_acl.ref b/gnu/dist/postfix/src/smtpd/smtpd_acl.ref
index 34e35186627..294891fd0c4 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_acl.ref
+++ b/gnu/dist/postfix/src/smtpd/smtpd_acl.ref
@@ -7,6 +7,8 @@ OK
OK
>>> relay_domains porcupine.org
OK
+>>> smtpd_null_access_lookup_key <>
+OK
>>> #
>>> # Test check_domain_access()
>>> #
@@ -162,3 +164,9 @@ OK
>>> # Expect: OK
>>> rcpt bad-sender@ok.domain
OK
+>>> #
+>>> # check_sender_access specific
+>>> #
+>>> mail <>
+./smtpd_check: reject: MAIL from bar.duno.com[44.33.44.33]: 550 <>: Sender address rejected: Go away postmaster; from=<>
+550 <>: Sender address rejected: Go away postmaster
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_chat.c b/gnu/dist/postfix/src/smtpd/smtpd_chat.c
index 2d1f8772672..b9be60015cd 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_chat.c
+++ b/gnu/dist/postfix/src/smtpd/smtpd_chat.c
@@ -107,6 +107,9 @@ static void smtp_chat_append(SMTPD_STATE *state, char *direction)
{
char *line;
+ if (state->notify_mask == 0)
+ return;
+
if (state->history == 0)
state->history = argv_alloc(10);
line = concatenate(direction, STR(state->buffer), (char *) 0);
@@ -207,7 +210,7 @@ void smtpd_chat_notify(SMTPD_STATE *state)
notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
var_error_rcpt,
- NULL_CLEANUP_FLAGS, "NOTICE");
+ NULL_CLEANUP_FLAGS);
if (notice == 0) {
msg_warn("postmaster notify: %m");
return;
@@ -227,7 +230,5 @@ void smtpd_chat_notify(SMTPD_STATE *state)
post_mail_fputs(notice, "");
if (state->reason)
post_mail_fprintf(notice, "Session aborted, reason: %s", state->reason);
- else
- post_mail_fputs(notice, "No message was collected successfully.");
(void) post_mail_fclose(notice);
}
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_check.in b/gnu/dist/postfix/src/smtpd/smtpd_check.in
index f7c3cdd709d..44f969ce9c6 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_check.in
+++ b/gnu/dist/postfix/src/smtpd/smtpd_check.in
@@ -6,6 +6,7 @@
smtpd_delay_reject 0
mynetworks 127.0.0.0/8,168.100.189.0/28
relay_domains porcupine.org
+maps_rbl_domains blackholes.mail-abuse.org
#
# Test the client restrictions.
#
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_check.in2 b/gnu/dist/postfix/src/smtpd/smtpd_check.in2
index 0eaf8c02b9e..2b9000267b4 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_check.in2
+++ b/gnu/dist/postfix/src/smtpd/smtpd_check.in2
@@ -6,6 +6,7 @@
smtpd_delay_reject 0
mynetworks 127.0.0.0/8,168.100.189.0/28
relay_domains porcupine.org
+maps_rbl_domains blackholes.mail-abuse.org
#
# Test the client restrictions.
#
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_check.ref b/gnu/dist/postfix/src/smtpd/smtpd_check.ref
index b63c3f70545..121c9e32a5d 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_check.ref
+++ b/gnu/dist/postfix/src/smtpd/smtpd_check.ref
@@ -9,6 +9,8 @@ OK
OK
>>> relay_domains porcupine.org
OK
+>>> maps_rbl_domains blackholes.mail-abuse.org
+OK
>>> #
>>> # Test the client restrictions.
>>> #
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_check.ref2 b/gnu/dist/postfix/src/smtpd/smtpd_check.ref2
index c8d014f6000..2bd38266375 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_check.ref2
+++ b/gnu/dist/postfix/src/smtpd/smtpd_check.ref2
@@ -9,6 +9,8 @@ OK
OK
>>> relay_domains porcupine.org
OK
+>>> maps_rbl_domains blackholes.mail-abuse.org
+OK
>>> #
>>> # Test the client restrictions.
>>> #
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_check_access b/gnu/dist/postfix/src/smtpd/smtpd_check_access
index afb5487c965..b9cf1761fa0 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_check_access
+++ b/gnu/dist/postfix/src/smtpd/smtpd_check_access
@@ -29,3 +29,4 @@ reject.domain REJECT
reject@ok.domain REJECT
ok@ok.domain OK
ok.domain OK
+<> 550 Go away postmaster
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_sasl_glue.c b/gnu/dist/postfix/src/smtpd/smtpd_sasl_glue.c
index caa3ec04fb1..716b7873dd3 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_sasl_glue.c
+++ b/gnu/dist/postfix/src/smtpd/smtpd_sasl_glue.c
@@ -118,11 +118,11 @@ static int smtpd_sasl_log(void *unused_context, int priority,
switch (priority) {
case SASL_LOG_ERR:
case SASL_LOG_WARNING:
- msg_warn("%s", message);
+ msg_warn("SASL authentication problem: %s", message);
break;
case SASL_LOG_INFO:
if (msg_verbose)
- msg_info("%s", message);
+ msg_info("SASL authentication info: %s", message);
break;
}
return SASL_OK;
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_state.c b/gnu/dist/postfix/src/smtpd/smtpd_state.c
index ca492963487..6e7236543e9 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_state.c
+++ b/gnu/dist/postfix/src/smtpd/smtpd_state.c
@@ -91,6 +91,7 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream)
state->recursion = 0;
state->msg_size = 0;
state->junk_cmds = 0;
+ state->warn_if_reject = 0;
#ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state))
diff --git a/gnu/dist/postfix/src/smtpd/smtpd_token.c b/gnu/dist/postfix/src/smtpd/smtpd_token.c
index 09d64cd01e5..66bc6bc9c38 100644
--- a/gnu/dist/postfix/src/smtpd/smtpd_token.c
+++ b/gnu/dist/postfix/src/smtpd/smtpd_token.c
@@ -196,7 +196,7 @@ int smtpd_token(char *cp, SMTPD_TOKEN **argvp)
#include <vstream.h>
#include <vstring_vstream.h>
-main(int unused_argc, char **unused_argv)
+int main(int unused_argc, char **unused_argv)
{
VSTRING *vp = vstring_alloc(10);
int tok_argc;
diff --git a/gnu/dist/postfix/src/smtpstone/Makefile.in b/gnu/dist/postfix/src/smtpstone/Makefile.in
index 5cf7920e0e6..454a44c1343 100644
--- a/gnu/dist/postfix/src/smtpstone/Makefile.in
+++ b/gnu/dist/postfix/src/smtpstone/Makefile.in
@@ -1,6 +1,6 @@
SHELL = /bin/sh
-SRCS = smtp-source.c smtp-sink.c
-OBJS = smtp-source.o smtp-sink.o
+SRCS = smtp-source.c smtp-sink.c qmqp-source.c qmqp-sink.c
+OBJS = smtp-source.o smtp-sink.o qmqp-source.o qmqp-sink.o
HDRS =
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -10,7 +10,7 @@ DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
INC_DIR = ../../include
-PROG = smtp-source smtp-sink
+PROG = smtp-source smtp-sink qmqp-source qmqp-sink
LIBS = ../../lib/libglobal.a ../../lib/libutil.a
.c.o:; $(CC) $(CFLAGS) -c $*.c
@@ -26,9 +26,15 @@ smtp-sink: smtp-sink.o $(LIBS)
smtp-source: smtp-source.o $(LIBS)
$(CC) $(CFLAGS) -o $@ smtp-source.o $(LIBS) $(SYSLIBS)
+qmqp-sink: qmqp-sink.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ qmqp-sink.o $(LIBS) $(SYSLIBS)
+
+qmqp-source: qmqp-source.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ qmqp-source.o $(LIBS) $(SYSLIBS)
+
test: $(TESTPROG)
-update: ../../bin/smtp-source ../../bin/smtp-sink
+update: ../../bin/smtp-source ../../bin/smtp-sink ../../bin/qmqp-source
../../bin/smtp-source: smtp-source
cp $? $@
@@ -36,6 +42,12 @@ update: ../../bin/smtp-source ../../bin/smtp-sink
../../bin/smtp-sink: smtp-sink
cp $? $@
+../../bin/qmqp-source: qmqp-source
+ cp $? $@
+
+../../bin/qmqp-sink: qmqp-sink
+ cp $? $@
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
@@ -61,6 +73,36 @@ depend: $(MAKES)
@$(EXPORT) make -f Makefile.in Makefile 1>&2
# do not edit below this line - it is generated by 'make depend'
+qmqp-sink.o: qmqp-sink.c
+qmqp-sink.o: ../../include/sys_defs.h
+qmqp-sink.o: ../../include/msg.h
+qmqp-sink.o: ../../include/vstring.h
+qmqp-sink.o: ../../include/vbuf.h
+qmqp-sink.o: ../../include/vstream.h
+qmqp-sink.o: ../../include/listen.h
+qmqp-sink.o: ../../include/iostuff.h
+qmqp-sink.o: ../../include/events.h
+qmqp-sink.o: ../../include/mymalloc.h
+qmqp-sink.o: ../../include/msg_vstream.h
+qmqp-sink.o: ../../include/netstring.h
+qmqp-sink.o: ../../include/qmqp_proto.h
+qmqp-source.o: qmqp-source.c
+qmqp-source.o: ../../include/sys_defs.h
+qmqp-source.o: ../../include/msg.h
+qmqp-source.o: ../../include/msg_vstream.h
+qmqp-source.o: ../../include/vstream.h
+qmqp-source.o: ../../include/vbuf.h
+qmqp-source.o: ../../include/vstring.h
+qmqp-source.o: ../../include/get_hostname.h
+qmqp-source.o: ../../include/split_at.h
+qmqp-source.o: ../../include/connect.h
+qmqp-source.o: ../../include/iostuff.h
+qmqp-source.o: ../../include/mymalloc.h
+qmqp-source.o: ../../include/events.h
+qmqp-source.o: ../../include/find_inet.h
+qmqp-source.o: ../../include/netstring.h
+qmqp-source.o: ../../include/mail_date.h
+qmqp-source.o: ../../include/qmqp_proto.h
smtp-sink.o: smtp-sink.c
smtp-sink.o: ../../include/sys_defs.h
smtp-sink.o: ../../include/msg.h
diff --git a/gnu/dist/postfix/src/smtpstone/qmqp-sink.c b/gnu/dist/postfix/src/smtpstone/qmqp-sink.c
new file mode 100644
index 00000000000..1c696b8991b
--- /dev/null
+++ b/gnu/dist/postfix/src/smtpstone/qmqp-sink.c
@@ -0,0 +1,287 @@
+/*++
+/* NAME
+/* qmqp-sink 8
+/* SUMMARY
+/* multi-threaded QMQP test server
+/* SYNOPSIS
+/* .fi
+/* \fBqmqp-sink\fR [\fB-cv\fR] [\fB-x \fItime\fR]
+/* [\fBinet:\fR][\fIhost\fR]:\fIport\fR \fIbacklog\fR
+/*
+/* \fBqmqp-sink\fR [\fB-cv\fR] [\fB-x \fItime\fR]
+/* \fBunix:\fR\fIpathname\fR \fIbacklog\fR
+/* DESCRIPTION
+/* \fIqmqp-sink\fR listens on the named host (or address) and port.
+/* It receives messages from the network and throws them away.
+/* The purpose is to measure QMQP client performance, not protocol
+/* compliance.
+/* Connections can be accepted on IPV4 endpoints or UNIX-domain sockets.
+/* IPV4 is the default.
+/* This program is the complement of the \fIqmqp-source\fR program.
+/* .IP \fB-c\fR
+/* Display a running counter that is updated whenever a delivery
+/* is completed.
+/* .IP \fB-v\fR
+/* Increase verbosity. Specify \fB-v -v\fR to see some of the QMQP
+/* conversation.
+/* .IP "\fB-x \fItime\fR
+/* Terminate after \fItime\fR seconds. This is to facilitate memory
+/* leak testing.
+/* SEE ALSO
+/* qmqp-source, QMQP test message generator
+/* 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 <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <listen.h>
+#include <events.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+#include <msg_vstream.h>
+#include <netstring.h>
+
+/* Global library. */
+
+#include <qmqp_proto.h>
+
+/* Application-specific. */
+
+typedef struct {
+ VSTREAM *stream; /* client connection */
+ int count; /* bytes to go */
+} SINK_STATE;
+
+static int var_tmout;
+static VSTRING *buffer;
+static void disconnect(SINK_STATE *);
+static int count;
+static int counter;
+
+/* send_reply - finish conversation */
+
+static void send_reply(SINK_STATE *state)
+{
+ vstring_sprintf(buffer, "%cOk", QMQP_STAT_OK);
+ NETSTRING_PUT_BUF(state->stream, buffer);
+ netstring_fflush(state->stream);
+ if (count) {
+ counter++;
+ vstream_printf("%d\r", counter);
+ vstream_fflush(VSTREAM_OUT);
+ }
+ disconnect(state);
+}
+
+/* read_data - read over-all netstring data */
+
+static void read_data(int unused_event, char *context)
+{
+ SINK_STATE *state = (SINK_STATE *) context;
+ int fd = vstream_fileno(state->stream);
+ int count;
+
+ /*
+ * Refill the VSTREAM buffer, if necessary.
+ */
+ if (VSTREAM_GETC(state->stream) == VSTREAM_EOF)
+ netstring_except(state->stream, vstream_ftimeout(state->stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+ state->count--;
+
+ /*
+ * Flush the VSTREAM buffer. As documented, vstream_fseek() discards
+ * unread input.
+ */
+ if ((count = vstream_peek(state->stream)) > 0) {
+ state->count -= count;
+ if (state->count <= 0) {
+ send_reply(state);
+ return;
+ }
+ vstream_fseek(state->stream, 0L, 0);
+ }
+
+ /*
+ * Do not block while waiting for the arrival of more data.
+ */
+ event_disable_readwrite(fd);
+ event_enable_read(fd, read_data, context);
+}
+
+/* read_length - read over-all netstring length */
+
+static void read_length(int event, char *context)
+{
+ SINK_STATE *state = (SINK_STATE *) context;
+
+ switch (vstream_setjmp(state->stream)) {
+
+ default:
+ msg_panic("unknown error reading input");
+
+ case NETSTRING_ERR_TIME:
+ msg_panic("attempt to read non-readable socket");
+ /* NOTREACHED */
+
+ case NETSTRING_ERR_EOF:
+ msg_warn("lost connection");
+ disconnect(state);
+ return;
+
+ case NETSTRING_ERR_FORMAT:
+ msg_warn("netstring format error");
+ disconnect(state);
+ return;
+
+ case NETSTRING_ERR_SIZE:
+ msg_warn("netstring size error");
+ disconnect(state);
+ return;
+
+ /*
+ * Include the netstring terminator in the read byte count. This
+ * violates abstractions.
+ */
+ case 0:
+ state->count = netstring_get_length(state->stream) + 1;
+ read_data(event, context);
+ return;
+ }
+}
+
+/* disconnect - handle disconnection events */
+
+static void disconnect(SINK_STATE *state)
+{
+ event_disable_readwrite(vstream_fileno(state->stream));
+ vstream_fclose(state->stream);
+ myfree((char *) state);
+}
+
+/* connect_event - handle connection events */
+
+static void connect_event(int unused_event, char *context)
+{
+ int sock = CAST_CHAR_PTR_TO_INT(context);
+ struct sockaddr sa;
+ SOCKADDR_SIZE len = sizeof(sa);
+ SINK_STATE *state;
+ int fd;
+
+ if ((fd = accept(sock, &sa, &len)) >= 0) {
+ if (msg_verbose)
+ msg_info("connect (%s)",
+#ifdef AF_LOCAL
+ sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
+#else
+ sa.sa_family == AF_UNIX ? "AF_UNIX" :
+#endif
+ sa.sa_family == AF_INET ? "AF_INET" :
+#ifdef AF_INET6
+ sa.sa_family == AF_INET6 ? "AF_INET6" :
+#endif
+ "unknown protocol family");
+ non_blocking(fd, NON_BLOCKING);
+ state = (SINK_STATE *) mymalloc(sizeof(*state));
+ state->stream = vstream_fdopen(fd, O_RDWR);
+ netstring_setup(state->stream, var_tmout);
+ event_enable_read(fd, read_length, (char *) state);
+ }
+}
+
+/* terminate - voluntary exit */
+
+static void terminate(int unused_event, char *unused_context)
+{
+ exit(0);
+}
+
+/* usage - explain */
+
+static void usage(char *myname)
+{
+ msg_fatal("usage: %s [-cv] [-x time] [host]:port backlog", myname);
+}
+
+int main(int argc, char **argv)
+{
+ int sock;
+ int backlog;
+ int ch;
+ int ttl;
+
+ /*
+ * Initialize diagnostics.
+ */
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+
+ /*
+ * Parse JCL.
+ */
+ while ((ch = GETOPT(argc, argv, "cvx:")) > 0) {
+ switch (ch) {
+ case 'c':
+ count++;
+ break;
+ case 'v':
+ msg_verbose++;
+ break;
+ case 'x':
+ if ((ttl = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ event_request_timer(terminate, (char *) 0, ttl);
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+ if (argc - optind != 2)
+ usage(argv[0]);
+ if ((backlog = atoi(argv[optind + 1])) <= 0)
+ usage(argv[0]);
+
+ /*
+ * Initialize.
+ */
+ buffer = vstring_alloc(1024);
+ if (strncmp(argv[optind], "unix:", 5) == 0) {
+ sock = unix_listen(argv[optind] + 5, backlog, BLOCKING);
+ } else {
+ if (strncmp(argv[optind], "inet:", 5) == 0)
+ argv[optind] += 5;
+ sock = inet_listen(argv[optind], backlog, BLOCKING);
+ }
+
+ /*
+ * Start the event handler.
+ */
+ event_enable_read(sock, connect_event, CAST_INT_TO_CHAR_PTR(sock));
+ for (;;)
+ event_loop(-1);
+}
diff --git a/gnu/dist/postfix/src/smtpstone/qmqp-source.c b/gnu/dist/postfix/src/smtpstone/qmqp-source.c
new file mode 100644
index 00000000000..66fd33854e3
--- /dev/null
+++ b/gnu/dist/postfix/src/smtpstone/qmqp-source.c
@@ -0,0 +1,593 @@
+/*++
+/* NAME
+/* qmqp-source 8
+/* SUMMARY
+/* multi-threaded QMQP test generator
+/* SYNOPSIS
+/* .fi
+/* \fBqmqp-source\fR [\fIoptions\fR] [\fBinet:\fR]\fIhost\fR[:\fIport\fR]
+/*
+/* \fBqmqp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR
+/* DESCRIPTION
+/* qmqp-source connects to the named host and TCP port (default 628)
+/* and sends one or more messages to it, either sequentially
+/* or in parallel. The program speaks the QMQP protocol.
+/* Connections can be made to UNIX-domain and IPV4 servers.
+/* IPV4 is the default.
+/*
+/* Options:
+/* .IP -c
+/* Display a running counter that is incremented each time
+/* a delivery completes.
+/* .IP "-C count"
+/* When a host sends RESET instead of SYN|ACK, try \fIcount\fR times
+/* before giving up. The default count is 1. Specify a larger count in
+/* order to work around a problem with TCP/IP stacks that send RESET
+/* when the listen queue is full.
+/* .IP "-f from"
+/* Use the specified sender address (default: <foo@myhostname>).
+/* .IP "-l length"
+/* Send \fIlength\fR bytes as message payload. The length
+/* includes the message headers.
+/* .IP "-m message_count"
+/* Send the specified number of messages (default: 1).
+/* .IP "-r recipient_count"
+/* Send the specified number of recipients per transaction (default: 1).
+/* Recipient names are generated by prepending a number to the
+/* recipient address.
+/* .IP "-s session_count"
+/* Run the specified number of QMQP sessions in parallel (default: 1).
+/* .IP "-t to"
+/* Use the specified recipient address (default: <foo@myhostname>).
+/* .IP "-R interval"
+/* Wait for a random period of time 0 <= n <= interval between messages.
+/* Suspending one thread does not affect other delivery threads.
+/* .IP "-w interval"
+/* Wait a fixed time between messages.
+/* Suspending one thread does not affect other delivery threads.
+/* 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 <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <msg_vstream.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <get_hostname.h>
+#include <split_at.h>
+#include <connect.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <find_inet.h>
+#include <iostuff.h>
+#include <netstring.h>
+
+/* Global library. */
+
+#include <mail_date.h>
+#include <qmqp_proto.h>
+
+/* Application-specific. */
+
+ /*
+ * Per-session data structure with state.
+ *
+ * This software can maintain multiple parallel connections to the same QMQP
+ * server. However, it makes no more than one connection request at a time
+ * to avoid overwhelming the server with SYN packets and having to back off.
+ * Back-off would screw up the benchmark. Pending connection requests are
+ * kept in a linear list.
+ */
+typedef struct SESSION {
+ int xfer_count; /* # of xfers in session */
+ int rcpt_done; /* # of recipients done */
+ int rcpt_count; /* # of recipients to go */
+ VSTREAM *stream; /* open connection */
+ int connect_count; /* # of connect()s to retry */
+ struct SESSION *next; /* connect() queue linkage */
+} SESSION;
+
+static SESSION *last_session; /* connect() queue tail */
+
+static VSTRING *buffer;
+static int var_line_limit = 10240;
+static int var_timeout = 300;
+static const char *var_myhostname;
+static int session_count;
+static int message_count = 1;
+static struct sockaddr_in sin;
+
+#undef sun
+static struct sockaddr_un sun;
+static struct sockaddr *sa;
+static int sa_length;
+static int recipients = 1;
+static char *defaddr;
+static char *recipient;
+static char *sender;
+static int message_length = 1024;
+static int count = 0;
+static int counter = 0;
+static int connect_count = 1;
+static int random_delay = 0;
+static int fixed_delay = 0;
+static const char *mydate;
+static int mypid;
+
+static void enqueue_connect(SESSION *);
+static void start_connect(SESSION *);
+static void connect_done(int, char *);
+
+static void send_data(SESSION *);
+static void receive_reply(int, char *);
+
+static VSTRING *message_buffer;
+static VSTRING *sender_buffer;
+static VSTRING *recipient_buffer;
+
+/* Silly little macros. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* random_interval - generate a random value in 0 .. (small) interval */
+
+static int random_interval(int interval)
+{
+ return (rand() % (interval + 1));
+}
+
+/* socket_error - look up and reset the last socket error */
+
+static int socket_error(int sock)
+{
+ int error;
+ SOCKOPT_SIZE error_len;
+
+ /*
+ * Some Solaris 2 versions have getsockopt() itself return the error,
+ * instead of returning it via the parameter list.
+ */
+ error = 0;
+ error_len = sizeof(error);
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) < 0)
+ return (-1);
+ if (error) {
+ errno = error;
+ return (-1);
+ }
+
+ /*
+ * No problems.
+ */
+ return (0);
+}
+
+/* exception_text - translate exceptions from the netstring module */
+
+static char *exception_text(int except)
+{
+ ;
+
+ switch (except) {
+ case NETSTRING_ERR_EOF:
+ return ("lost connection");
+ case NETSTRING_ERR_TIME:
+ return ("timeout");
+ case NETSTRING_ERR_FORMAT:
+ return ("netstring format error");
+ case NETSTRING_ERR_SIZE:
+ return ("netstring size exceeds limit");
+ default:
+ msg_panic("exception_text: unknown exception %d", except);
+ }
+ /* NOTREACHED */
+}
+
+/* startup - connect to server but do not wait */
+
+static void startup(SESSION *session)
+{
+ if (message_count-- <= 0) {
+ myfree((char *) session);
+ session_count--;
+ return;
+ }
+ enqueue_connect(session);
+}
+
+/* start_event - invoke startup from timer context */
+
+static void start_event(int unused_event, char *context)
+{
+ SESSION *session = (SESSION *) context;
+
+ startup(session);
+}
+
+/* start_another - start another session */
+
+static void start_another(SESSION *session)
+{
+ if (random_delay > 0) {
+ event_request_timer(start_event, (char *) session,
+ random_interval(random_delay));
+ } else if (fixed_delay > 0) {
+ event_request_timer(start_event, (char *) session, fixed_delay);
+ } else {
+ startup(session);
+ }
+}
+
+/* enqueue_connect - queue a connection request */
+
+static void enqueue_connect(SESSION *session)
+{
+ session->next = 0;
+ if (last_session == 0) {
+ last_session = session;
+ start_connect(session);
+ } else {
+ last_session->next = session;
+ last_session = session;
+ }
+}
+
+/* dequeue_connect - connection request completed */
+
+static void dequeue_connect(SESSION *session)
+{
+ if (session == last_session) {
+ if (session->next != 0)
+ msg_panic("dequeue_connect: queue ends after last");
+ last_session = 0;
+ } else {
+ if (session->next == 0)
+ msg_panic("dequeue_connect: queue ends before last");
+ start_connect(session->next);
+ }
+}
+
+/* fail_connect - handle failed startup */
+
+static void fail_connect(SESSION *session)
+{
+ if (session->connect_count-- == 1)
+ msg_fatal("connect: %m");
+ msg_warn("connect: %m");
+ event_disable_readwrite(vstream_fileno(session->stream));
+ vstream_fclose(session->stream);
+ session->stream = 0;
+#ifdef MISSING_USLEEP
+ doze(10);
+#else
+ usleep(10);
+#endif
+ start_connect(session);
+}
+
+/* start_connect - start TCP handshake */
+
+static void start_connect(SESSION *session)
+{
+ int fd;
+
+ /*
+ * Some systems don't set the socket error when connect() fails early
+ * (loopback) so we must deal with the error immediately, rather than
+ * retrieving it later with getsockopt(). We can't use MSG_PEEK to
+ * distinguish between server disconnect and connection refused.
+ */
+ if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
+ msg_fatal("socket: %m");
+ (void) non_blocking(fd, NON_BLOCKING);
+ session->stream = vstream_fdopen(fd, O_RDWR);
+ event_enable_write(fd, connect_done, (char *) session);
+ netstring_setup(session->stream, var_timeout);
+ if (connect(fd, sa, sa_length) < 0 && errno != EINPROGRESS)
+ fail_connect(session);
+}
+
+/* connect_done - send message sender info */
+
+static void connect_done(int unused_event, char *context)
+{
+ SESSION *session = (SESSION *) context;
+ int fd = vstream_fileno(session->stream);
+
+ /*
+ * Try again after some delay when the connection failed, in case they
+ * run a Mickey Mouse protocol stack.
+ */
+ if (socket_error(fd) < 0) {
+ fail_connect(session);
+ } else {
+ dequeue_connect(session);
+ non_blocking(fd, BLOCKING);
+ event_disable_readwrite(fd);
+ send_data(session);
+ }
+}
+
+/* send_data - send message+sender+recipients */
+
+static void send_data(SESSION *session)
+{
+ int fd = vstream_fileno(session->stream);
+ int except;
+
+ /*
+ * Prepare for disaster.
+ */
+ if ((except = vstream_setjmp(session->stream)) != 0)
+ msg_fatal("%s while sending message", exception_text(except));
+
+ /*
+ * Send the message content, by wrapping three netstrings into an
+ * over-all netstring.
+ *
+ * XXX This should be done more carefully to avoid blocking when sending
+ * large messages over slow networks.
+ */
+ netstring_put_multi(session->stream,
+ STR(message_buffer), LEN(message_buffer),
+ STR(sender_buffer), LEN(sender_buffer),
+ STR(recipient_buffer), LEN(recipient_buffer),
+ 0);
+ netstring_fflush(session->stream);
+
+ /*
+ * Wake me up when the server replies or when something bad happens.
+ */
+ event_enable_read(fd, receive_reply, (char *) session);
+}
+
+/* receive_reply - read server reply */
+
+static void receive_reply(int unused_event, char *context)
+{
+ SESSION *session = (SESSION *) context;
+ int except;
+
+ /*
+ * Prepare for disaster.
+ */
+ if ((except = vstream_setjmp(session->stream)) != 0)
+ msg_fatal("%s while receiving server reply", exception_text(except));
+
+ /*
+ * Receive and process the server reply.
+ */
+ netstring_get(session->stream, buffer, var_line_limit);
+ if (msg_verbose)
+ vstream_printf("<< %.*s\n", LEN(buffer), STR(buffer));
+ if (STR(buffer)[0] != QMQP_STAT_OK)
+ msg_fatal("%s error: %.*s",
+ STR(buffer)[0] == QMQP_STAT_RETRY ? "recoverable" :
+ STR(buffer)[0] == QMQP_STAT_HARD ? "unrecoverable" :
+ "unknown", LEN(buffer) - 1, STR(buffer) + 1);
+
+ /*
+ * Update the optional running counter.
+ */
+ if (count) {
+ counter++;
+ vstream_printf("%d\r", counter);
+ vstream_fflush(VSTREAM_OUT);
+ }
+
+ /*
+ * Finish this session. QMQP sends only one message per session.
+ */
+ event_disable_readwrite(vstream_fileno(session->stream));
+ vstream_fclose(session->stream);
+ session->stream = 0;
+ start_another(session);
+}
+
+/* usage - explain */
+
+static void usage(char *myname)
+{
+ msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -f from -t to -R delay -v -w delay host[:port]", myname);
+}
+
+/* main - parse JCL and start the machine */
+
+int main(int argc, char **argv)
+{
+ SESSION *session;
+ char *host;
+ char *port;
+ char *path;
+ int path_len;
+ int sessions = 1;
+ int ch;
+ int n;
+ int i;
+
+ signal(SIGPIPE, SIG_IGN);
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+
+ /*
+ * Parse JCL.
+ */
+ while ((ch = GETOPT(argc, argv, "cC:f:l:m:r:R:s:t:vw:")) > 0) {
+ switch (ch) {
+ case 'c':
+ count++;
+ break;
+ case 'C':
+ if ((connect_count = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ break;
+ case 'f':
+ sender = optarg;
+ break;
+ case 'l':
+ if ((message_length = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ break;
+ case 'm':
+ if ((message_count = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ break;
+ case 'r':
+ if ((recipients = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ break;
+ case 'R':
+ if (fixed_delay > 0 || (random_delay = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ break;
+ case 's':
+ if ((sessions = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ break;
+ case 't':
+ recipient = optarg;
+ break;
+ case 'v':
+ msg_verbose++;
+ break;
+ case 'w':
+ if (random_delay > 0 || (fixed_delay = atoi(optarg)) <= 0)
+ usage(argv[0]);
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+ if (argc - optind != 1)
+ usage(argv[0]);
+
+ if (random_delay > 0)
+ srand(getpid());
+
+ /*
+ * Translate endpoint address to internal form.
+ */
+ if (strncmp(argv[optind], "unix:", 5) == 0) {
+ path = argv[optind] + 5;
+ path_len = strlen(path);
+ if (path_len >= (int) sizeof(sun.sun_path))
+ msg_fatal("unix-domain name too long: %s", path);
+ memset((char *) &sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+#ifdef HAS_SUN_LEN
+ sun.sun_len = path_len + 1;
+#endif
+ memcpy(sun.sun_path, path, path_len);
+ sa = (struct sockaddr *) & sun;
+ sa_length = sizeof(sun);
+ } else {
+ if (strncmp(argv[optind], "inet:", 5) == 0)
+ argv[optind] += 5;
+ if ((port = split_at(host = argv[optind], ':')) == 0 || *port == 0)
+ port = "628";
+ memset((char *) &sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = find_inet_addr(host);
+ sin.sin_port = find_inet_port(port, "tcp");
+ sa = (struct sockaddr *) & sin;
+ sa_length = sizeof(sin);
+ }
+
+ /*
+ * Allocate space for temporary buffer.
+ */
+ buffer = vstring_alloc(100);
+
+ /*
+ * Make sure we have sender and recipient addresses.
+ */
+ var_myhostname = get_hostname();
+ if (sender == 0 || recipient == 0) {
+ vstring_sprintf(buffer, "foo@%s", var_myhostname);
+ defaddr = mystrdup(vstring_str(buffer));
+ if (sender == 0)
+ sender = defaddr;
+ if (recipient == 0)
+ recipient = defaddr;
+ }
+
+ /*
+ * Prepare some results that may be used multiple times: the message
+ * content netstring, the sender netstring, and the recipient netstrings.
+ */
+ mydate = mail_date(time((time_t *) 0));
+ mypid = getpid();
+
+ message_buffer = vstring_alloc(message_length + 200);
+ vstring_sprintf(buffer,
+ "From: <%s>\nTo: <%s>\nDate: %s\nMessage-Id: <%d@%s>\n\n",
+ sender, recipient, mydate, mypid, var_myhostname);
+ for (n = 1; LEN(buffer) < message_length; n++) {
+ for (i = 0; i < n && i < 79; i++)
+ VSTRING_ADDCH(buffer, 'X');
+ VSTRING_ADDCH(buffer, '\n');
+ }
+ STR(buffer)[message_length - 1] = '\n';
+ netstring_memcpy(message_buffer, STR(buffer), message_length);
+
+ n = strlen(sender);
+ sender_buffer = vstring_alloc(n);
+ netstring_memcpy(sender_buffer, sender, n);
+
+ if (recipients == 1) {
+ n = strlen(recipient);
+ recipient_buffer = vstring_alloc(n);
+ netstring_memcpy(recipient_buffer, recipient, n);
+ } else {
+ recipient_buffer = vstring_alloc(100);
+ for (n = 0; n < recipients; n++) {
+ vstring_sprintf(buffer, "%d%s", n, recipient);
+ netstring_memcat(recipient_buffer, STR(buffer), LEN(buffer));
+ }
+ }
+
+ /*
+ * Start sessions.
+ */
+ while (sessions-- > 0) {
+ session = (SESSION *) mymalloc(sizeof(*session));
+ session->stream = 0;
+ session->xfer_count = 0;
+ session->connect_count = connect_count;
+ session->next = 0;
+ session_count++;
+ startup(session);
+ }
+ for (;;) {
+ event_loop(-1);
+ if (session_count <= 0 && message_count <= 0) {
+ if (count) {
+ VSTREAM_PUTC('\n', VSTREAM_OUT);
+ vstream_fflush(VSTREAM_OUT);
+ }
+ exit(0);
+ }
+ }
+}
diff --git a/gnu/dist/postfix/src/smtpstone/smtp-sink.c b/gnu/dist/postfix/src/smtpstone/smtp-sink.c
index 81aab916b36..24a5e278492 100644
--- a/gnu/dist/postfix/src/smtpstone/smtp-sink.c
+++ b/gnu/dist/postfix/src/smtpstone/smtp-sink.c
@@ -5,11 +5,10 @@
/* multi-threaded SMTP/LMTP test server
/* SYNOPSIS
/* .fi
-/* \fBsmtp-sink\fR [\fB-cLpv\fR] [\fB-w \fIdelay\fR]
-/* [\fBinet:\fR][\fIhost\fR]:\fIport\fR \fIbacklog\fR
+/* \fBsmtp-sink\fR [\fIoptions\fR] [\fBinet:\fR][\fIhost\fR]:\fIport\fR
+/* \fIbacklog\fR
/*
-/* \fBsmtp-sink\fR [\fB-cLpv\fR] [\fB-w \fIdelay\fR]
-/* \fBunix:\fR\fIpathname\fR \fIbacklog\fR
+/* \fBsmtp-sink\fR [\fIoptions\fR] \fBunix:\fR\fIpathname\fR \fIbacklog\fR
/* DESCRIPTION
/* \fIsmtp-sink\fR listens on the named host (or address) and port.
/* It takes SMTP messages from the network and throws them away.
@@ -18,17 +17,33 @@
/* Connections can be accepted on IPV4 endpoints or UNIX-domain sockets.
/* IPV4 is the default.
/* This program is the complement of the \fIsmtp-source\fR program.
-/* .IP -c
+/*
+/* Arguments:
+/* .IP \fB-c\fR
/* Display a running counter that is updated whenever an SMTP
/* QUIT command is executed.
-/* .IP -L
+/* .IP \fB-L\fR
/* Speak LMTP rather than SMTP.
-/* .IP -p
+/* .IP "\fB-n \fIcount\fR"
+/* Terminate after \fIcount\fR sessions. This is for testing purposes.
+/* .IP \fB-p\fR
/* Disable ESMTP command pipelining.
-/* .IP -v
+/* .IP \fB-P\fR
+/* Change the server greeting so that it appears to come through
+/* a CISCO PIX system.
+/* .IP \fB-v\fR
/* Show the SMTP conversations.
-/* .IP "-w delay"
+/* .IP "\fB-w \fIdelay\fR"
/* Wait \fIdelay\fR seconds before responding to a DATA command.
+/* .IP [\fBinet:\fR][\fIhost\fR]:\fIport\fR
+/* Listen on network interface \fIhost\fR (default: any interface)
+/* TCP port \fIport\fR. Both \fIhost\fR and \fIport\fR may be
+/* specified in numeric or symbolic form.
+/* .IP \fBunix:\fR\fIpathname\fR
+/* Listen on the UNIX-domain socket at \fIpathname\fR.
+/* .IP \fIbacklog\fR
+/* The maximum length the queue of pending connections,
+/* as defined by the listen(2) call.
/* SEE ALSO
/* smtp-source, SMTP/LMTP test message generator
/* LICENSE
@@ -77,6 +92,7 @@
typedef struct SINK_STATE {
VSTREAM *stream;
+ VSTRING *buffer;
int data_state;
int (*read) (struct SINK_STATE *);
int rcpts;
@@ -90,17 +106,18 @@ typedef struct SINK_STATE {
#define ST_CR_LF_DOT_CR_LF 5
static int var_tmout;
-static int var_max_line_length;
+static int var_max_line_length = 2048;
static char *var_myhostname;
-static VSTRING *buffer;
static int command_read(SINK_STATE *);
static int data_read(SINK_STATE *);
static void disconnect(SINK_STATE *);
static int count;
static int counter;
+static int max_count;
static int disable_pipelining;
static int fixed_delay;
static int enable_lmtp;
+static int pretend_pix;
/* ehlo_response - respond to EHLO command */
@@ -197,11 +214,10 @@ static int data_read(SINK_STATE *state)
struct data_trans *dp;
/*
- * We must avoid blocking I/O, so get out of here as soon as both the
- * VSTREAM and kernel read buffers dry up.
+ * A read may result in EOF, but is never supposed to time out - a time
+ * out means that we were trying to read when no data was available.
*/
- while (vstream_peek(state->stream) > 0
- || peekfd(vstream_fileno(state->stream)) > 0) {
+ for (;;) {
if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF)
return (-1);
for (dp = data_trans; dp->state != state->data_state; dp++)
@@ -224,8 +240,17 @@ static int data_read(SINK_STATE *state)
msg_info(".");
dot_response(state);
state->read = command_read;
+ state->data_state = ST_ANY;
break;
}
+
+ /*
+ * We must avoid blocking I/O, so get out of here as soon as both the
+ * VSTREAM and kernel read buffers dry up.
+ */
+ if (vstream_peek(state->stream) <= 0
+ && peekfd(vstream_fileno(state->stream)) <= 0)
+ return (0);
}
return (0);
}
@@ -258,9 +283,74 @@ static int command_read(SINK_STATE *state)
{
char *command;
SINK_COMMAND *cmdp;
+ int ch;
+ struct cmd_trans {
+ int state;
+ int want;
+ int next_state;
+ };
+ static struct cmd_trans cmd_trans[] = {
+ ST_ANY, '\r', ST_CR,
+ ST_CR, '\n', ST_CR_LF,
+ };
+ struct cmd_trans *cp;
- smtp_get(buffer, state->stream, var_max_line_length);
- if ((command = strtok(vstring_str(buffer), " \t")) == 0) {
+ /*
+ * A read may result in EOF, but is never supposed to time out - a time
+ * out means that we were trying to read when no data was available.
+ */
+ for (;;) {
+ if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF)
+ return (-1);
+
+ /*
+ * Sanity check. We don't want to store infinitely long commands.
+ */
+ if (VSTRING_LEN(state->buffer) >= var_max_line_length) {
+ msg_warn("command line too long");
+ return (-1);
+ }
+ VSTRING_ADDCH(state->buffer, ch);
+
+ /*
+ * Try to match the current character desired by the state machine.
+ * If that fails, try to restart the machine with a match for its
+ * first state.
+ */
+ for (cp = cmd_trans; cp->state != state->data_state; cp++)
+ /* void */ ;
+ if (ch == cp->want)
+ state->data_state = cp->next_state;
+ else if (ch == cmd_trans[0].want)
+ state->data_state = cmd_trans[0].next_state;
+ else
+ state->data_state = ST_ANY;
+ if (state->data_state == ST_CR_LF)
+ break;
+
+ /*
+ * We must avoid blocking I/O, so get out of here as soon as both the
+ * VSTREAM and kernel read buffers dry up.
+ */
+ if (vstream_peek(state->stream) <= 0
+ && peekfd(vstream_fileno(state->stream)) <= 0)
+ return (0);
+ }
+
+ /*
+ * Properly terminate the result, and reset the buffer write pointer for
+ * reading the next command. This is ugly, but not as ugly as trying to
+ * deal with all the early returns below.
+ */
+ vstring_truncate(state->buffer, VSTRING_LEN(state->buffer) - 2);
+ VSTRING_TERMINATE(state->buffer);
+ state->data_state = ST_ANY;
+ VSTRING_RESET(state->buffer);
+
+ /*
+ * Got a complete command line. Parse it.
+ */
+ if ((command = strtok(vstring_str(state->buffer), " \t")) == 0) {
smtp_printf(state->stream, "500 Error: unknown command");
return (0);
}
@@ -321,7 +411,10 @@ static void disconnect(SINK_STATE *state)
{
event_disable_readwrite(vstream_fileno(state->stream));
vstream_fclose(state->stream);
+ vstring_free(state->buffer);
myfree((char *) state);
+ if (max_count > 0 && counter >= max_count)
+ exit(0);
}
/* connect_event - handle connection events */
@@ -350,9 +443,13 @@ static void connect_event(int unused_event, char *context)
non_blocking(fd, NON_BLOCKING);
state = (SINK_STATE *) mymalloc(sizeof(*state));
state->stream = vstream_fdopen(fd, O_RDWR);
+ state->buffer = vstring_alloc(1024);
state->read = command_read;
- state->data_state = 0;
+ state->data_state = ST_ANY;
smtp_timeout_setup(state->stream, var_tmout);
+if (pretend_pix)
+ smtp_printf(state->stream, "220 ********");
+else
smtp_printf(state->stream, "220 %s ESMTP", var_myhostname);
event_enable_read(fd, read_event, (char *) state);
}
@@ -362,7 +459,7 @@ static void connect_event(int unused_event, char *context)
static void usage(char *myname)
{
- msg_fatal("usage: %s [-cLpv] [host]:port backlog", myname);
+ msg_fatal("usage: %s [-cLpPv] [-n count] [-w delay] [host]:port backlog", myname);
}
int main(int argc, char **argv)
@@ -379,7 +476,7 @@ int main(int argc, char **argv)
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "cLpvw:")) > 0) {
+ while ((ch = GETOPT(argc, argv, "cLn:pPvw:")) > 0) {
switch (ch) {
case 'c':
count++;
@@ -387,9 +484,15 @@ int main(int argc, char **argv)
case 'L':
enable_lmtp = 1;
break;
+ case 'n':
+ max_count = atoi(optarg);
+ break;
case 'p':
disable_pipelining = 1;
break;
+ case 'P':
+ pretend_pix=1;
+ break;
case 'v':
msg_verbose++;
break;
@@ -409,7 +512,6 @@ int main(int argc, char **argv)
/*
* Initialize.
*/
- buffer = vstring_alloc(1024);
var_myhostname = "smtp-sink";
if (strncmp(argv[optind], "unix:", 5) == 0) {
sock = unix_listen(argv[optind] + 5, backlog, BLOCKING);
diff --git a/gnu/dist/postfix/src/smtpstone/smtp-source.c b/gnu/dist/postfix/src/smtpstone/smtp-source.c
index 6ab916429bd..d05caae779a 100644
--- a/gnu/dist/postfix/src/smtpstone/smtp-source.c
+++ b/gnu/dist/postfix/src/smtpstone/smtp-source.c
@@ -9,48 +9,59 @@
/*
/* \fBsmtp-source\fR [\fIoptions\fR] \fBunix:\fIpathname\fR
/* DESCRIPTION
-/* smtp-source connects to the named host and port (default 25)
-/* and sends one or more little messages to it, either sequentially
+/* smtp-source connects to the named \fIhost\fR and TCP \fIport\fR
+/* (default: port 25)
+/* and sends one or more messages to it, either sequentially
/* or in parallel. The program speaks either SMTP (default) or
/* LMTP. Connections can be made to UNIX-domain and IPV4 servers.
/* IPV4 is the default.
/*
-/* Options:
-/* .IP -c
+/* Arguments:
+/* .IP \fB-c\fR
/* Display a running counter that is incremented each time
/* an SMTP DATA command completes.
-/* .IP "-C count"
+/* .IP "\fB-C \fIcount\fR"
/* When a host sends RESET instead of SYN|ACK, try \fIcount\fR times
/* before giving up. The default count is 1. Specify a larger count in
/* order to work around a problem with TCP/IP stacks that send RESET
/* when the listen queue is full.
-/* .IP -d
+/* .IP \fB-d\fR
/* Don't disconnect after sending a message; send the next
/* message over the same connection.
-/* .IP "-f from"
+/* .IP "\fB-f \fIfrom\fR"
/* Use the specified sender address (default: <foo@myhostname>).
-/* .IP -o
+/* .IP \fB-o\fR
/* Old mode: don't send HELO, and don't send message headers.
-/* .IP "-l length"
-/* Send \fIlength\fR bytes as message payload.
-/* .IP -L
+/* .IP "\fB-l \fIlength\fR"
+/* Send \fIlength\fR bytes as message payload. The length does not
+/* include message headers.
+/* .IP \fB-L\fR
/* Speak LMTP rather than SMTP.
-/* .IP "-m message_count"
+/* .IP "\fB-m \fImessage_count\fR"
/* Send the specified number of messages (default: 1).
-/* .IP "-r recipient_count"
+/* .IP "\fB-r \fIrecipient_count\fR"
/* Send the specified number of recipients per transaction (default: 1).
/* Recipient names are generated by prepending a number to the
-/* recipient address. The default is one recipient per transaction.
-/* .IP "-s session_count"
+/* recipient address.
+/* .IP "\fB-s \fIsession_count\fR"
/* Run the specified number of SMTP sessions in parallel (default: 1).
-/* .IP "-t to"
+/* .IP "\fB-S \fIsubject\fR"
+/* Send mail with the named subject line (default: none).
+/* .IP "\fB-t \fIto\fR"
/* Use the specified recipient address (default: <foo@myhostname>).
-/* .IP "-R interval"
+/* .IP "\fB-R \fIinterval\fR"
/* Wait for a random period of time 0 <= n <= interval between messages.
/* Suspending one thread does not affect other delivery threads.
-/* .IP "-w interval"
+/* .IP "\fB-w \fIinterval\fR"
/* Wait a fixed time between messages.
/* Suspending one thread does not affect other delivery threads.
+/* .IP [\fBinet:\fR]\fIhost\fR[:\fIport\fR]
+/* Connect via TCP to \fIhost\fR port \fIport\fR. The default
+/* port is \fBsmtp\fR.
+/* .IP \fBunix:\fIpathname\fR
+/* Connect to the UNIX-domain socket at \fIpathname\fR.
+/* BUGS
+/* No SMTP command pipelining support.
/* LICENSE
/* .ad
/* .fi
@@ -156,6 +167,7 @@ static int connect_count = 1;
static int random_delay = 0;
static int fixed_delay = 0;
static int talk_lmtp = 0;
+static char *subject = 0;
static void enqueue_connect(SESSION *);
static void start_connect(SESSION *);
@@ -644,6 +656,8 @@ static void data_done(int unused_event, char *context)
smtp_printf(session->stream, "Date: %s", mydate);
smtp_printf(session->stream, "Message-Id: <%04x.%04x.%04x@%s>",
mypid, vstream_fileno(session->stream), message_count, var_myhostname);
+ if (subject)
+ smtp_printf(session->stream, "Subject: %s", subject);
smtp_fputs("", 0, session->stream);
}
@@ -738,7 +752,7 @@ static void quit_done(int unused_event, char *context)
static void usage(char *myname)
{
- msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -d -f from -o -t to -R delay -v -w delay host[:port]", myname);
+ msg_fatal("usage: %s -s sess -l msglen -m msgs -c -C count -d -f from -o -t to -r rcptcount -R delay -v -w delay host[:port]", myname);
}
/* main - parse JCL and start the machine */
@@ -760,7 +774,7 @@ int main(int argc, char **argv)
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "cC:df:l:Lm:or:R:s:t:vw:")) > 0) {
+ while ((ch = GETOPT(argc, argv, "cC:df:l:Lm:or:R:s:S:t:vw:")) > 0) {
switch (ch) {
case 'c':
count++;
@@ -781,6 +795,7 @@ int main(int argc, char **argv)
message_data = mymalloc(message_length);
memset(message_data, 'X', message_length);
for (i = 80; i < message_length; i += 80) {
+ message_data[i - 80] = "0123456789"[(i / 80) % 10];
message_data[i - 2] = '\r';
message_data[i - 1] = '\n';
}
@@ -808,6 +823,9 @@ int main(int argc, char **argv)
if ((sessions = atoi(optarg)) <= 0)
usage(argv[0]);
break;
+ case 'S':
+ subject = optarg;
+ break;
case 't':
recipient = optarg;
break;
@@ -847,7 +865,7 @@ int main(int argc, char **argv)
} else {
if (strncmp(argv[optind], "inet:", 5) == 0)
argv[optind] += 5;
- if ((port = split_at(host = argv[optind], ':')) == 0)
+ if ((port = split_at(host = argv[optind], ':')) == 0 || *port == 0)
port = "smtp";
memset((char *) &sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
diff --git a/gnu/dist/postfix/src/trivial-rewrite/Makefile.in b/gnu/dist/postfix/src/trivial-rewrite/Makefile.in
index 1065ce0cfea..7f33c4c376f 100644
--- a/gnu/dist/postfix/src/trivial-rewrite/Makefile.in
+++ b/gnu/dist/postfix/src/trivial-rewrite/Makefile.in
@@ -70,6 +70,7 @@ resolve.o: ../../include/split_at.h
resolve.o: ../../include/mail_params.h
resolve.o: ../../include/mail_proto.h
resolve.o: ../../include/iostuff.h
+resolve.o: ../../include/attr.h
resolve.o: ../../include/mail_addr.h
resolve.o: ../../include/rewrite_clnt.h
resolve.o: ../../include/resolve_local.h
@@ -90,6 +91,7 @@ rewrite.o: ../../include/split_at.h
rewrite.o: ../../include/mail_params.h
rewrite.o: ../../include/mail_proto.h
rewrite.o: ../../include/iostuff.h
+rewrite.o: ../../include/attr.h
rewrite.o: ../../include/resolve_local.h
rewrite.o: ../../include/tok822.h
rewrite.o: ../../include/resolve_clnt.h
@@ -108,6 +110,8 @@ transport.o: ../../include/vstream.h
transport.o: ../../include/argv.h
transport.o: ../../include/mail_params.h
transport.o: ../../include/maps.h
+transport.o: ../../include/match_parent_style.h
+transport.o: ../../include/match_ops.h
transport.o: transport.h
trivial-rewrite.o: trivial-rewrite.c
trivial-rewrite.o: ../../include/sys_defs.h
@@ -123,6 +127,7 @@ trivial-rewrite.o: ../../include/argv.h
trivial-rewrite.o: ../../include/mail_params.h
trivial-rewrite.o: ../../include/mail_proto.h
trivial-rewrite.o: ../../include/iostuff.h
+trivial-rewrite.o: ../../include/attr.h
trivial-rewrite.o: ../../include/resolve_local.h
trivial-rewrite.o: ../../include/mail_conf.h
trivial-rewrite.o: ../../include/resolve_clnt.h
diff --git a/gnu/dist/postfix/src/trivial-rewrite/resolve.c b/gnu/dist/postfix/src/trivial-rewrite/resolve.c
index bd99fa61ae3..ba446301934 100644
--- a/gnu/dist/postfix/src/trivial-rewrite/resolve.c
+++ b/gnu/dist/postfix/src/trivial-rewrite/resolve.c
@@ -257,7 +257,9 @@ int resolve_proto(VSTREAM *stream)
{
int flags;
- if (mail_scan(stream, "%s", query) != 1)
+ if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_ADDR, query,
+ ATTR_TYPE_END) != 1)
return (-1);
resolve_addr(STR(query), channel, nexthop, nextrcpt, &flags);
@@ -266,8 +268,12 @@ int resolve_proto(VSTREAM *stream)
msg_info("%s -> (`%s' `%s' `%s' `%d')", STR(query), STR(channel),
STR(nexthop), STR(nextrcpt), flags);
- mail_print(stream, "%s %s %s %d",
- STR(channel), STR(nexthop), STR(nextrcpt), flags);
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_TRANSPORT, STR(channel),
+ ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, STR(nexthop),
+ ATTR_TYPE_STR, MAIL_ATTR_RECIP, STR(nextrcpt),
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
+ ATTR_TYPE_END);
if (vstream_fflush(stream) != 0) {
msg_warn("write resolver reply: %m");
diff --git a/gnu/dist/postfix/src/trivial-rewrite/rewrite.c b/gnu/dist/postfix/src/trivial-rewrite/rewrite.c
index 80ab11c252b..e1ae59cf751 100644
--- a/gnu/dist/postfix/src/trivial-rewrite/rewrite.c
+++ b/gnu/dist/postfix/src/trivial-rewrite/rewrite.c
@@ -203,7 +203,10 @@ void rewrite_addr(char *ruleset, char *addr, VSTRING *result)
int rewrite_proto(VSTREAM *stream)
{
- if (mail_scan(stream, "%s %s", ruleset, address) != 2)
+ if (attr_scan(stream, ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_RULE, ruleset,
+ ATTR_TYPE_STR, MAIL_ATTR_ADDR, address,
+ ATTR_TYPE_END) != 2)
return (-1);
rewrite_addr(vstring_str(ruleset), vstring_str(address), result);
@@ -212,7 +215,9 @@ int rewrite_proto(VSTREAM *stream)
msg_info("`%s' `%s' -> `%s'", vstring_str(ruleset),
vstring_str(address), vstring_str(result));
- mail_print(stream, "%s", vstring_str(result));
+ attr_print(stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_STR, MAIL_ATTR_ADDR, vstring_str(result),
+ ATTR_TYPE_END);
if (vstream_fflush(stream) != 0) {
msg_warn("write rewrite reply: %m");
diff --git a/gnu/dist/postfix/src/trivial-rewrite/transport.c b/gnu/dist/postfix/src/trivial-rewrite/transport.c
index baf55f69963..379e2395f0a 100644
--- a/gnu/dist/postfix/src/trivial-rewrite/transport.c
+++ b/gnu/dist/postfix/src/trivial-rewrite/transport.c
@@ -62,12 +62,14 @@
#include <mail_params.h>
#include <maps.h>
+#include <match_parent_style.h>
/* Application-specific. */
#include "transport.h"
static MAPS *transport_path;
+static int transport_match_parent_style;
/* transport_init - pre-jail initialization */
@@ -77,6 +79,7 @@ void transport_init(void)
msg_panic("transport_init: repeated call");
transport_path = maps_create("transport", var_transport_maps,
DICT_FLAG_LOCK);
+ transport_match_parent_style = match_parent_style(VAR_TRANSPORT_MAPS);
}
/* transport_lookup - map a transport domain */
@@ -85,6 +88,7 @@ int transport_lookup(const char *domain, VSTRING *channel, VSTRING *nexthop)
{
char *low_domain = lowercase(mystrdup(domain));
const char *name;
+ const char *next;
const char *value;
const char *host;
char *saved_value;
@@ -113,7 +117,7 @@ int transport_lookup(const char *domain, VSTRING *channel, VSTRING *nexthop)
* Specify if a key is partial or full, to avoid matching partial keys with
* regular expressions.
*/
- for (name = low_domain; name != 0; name = strchr(name + 1, '.')) {
+ for (name = low_domain; /* void */; name = next) {
if ((value = maps_find(transport_path, name, maps_flag)) != 0) {
saved_value = mystrdup(value);
if ((host = split_at(saved_value, ':')) == 0 || *host == 0)
@@ -132,6 +136,10 @@ int transport_lookup(const char *domain, VSTRING *channel, VSTRING *nexthop)
} else if (dict_errno != 0) {
msg_fatal("transport table lookup problem");
}
+ if ((next = strchr(name + 1, '.')) == 0)
+ break;
+ if (transport_match_parent_style == MATCH_FLAG_PARENT)
+ next++;
maps_flag = PARTIAL;
}
myfree(low_domain);
diff --git a/gnu/dist/postfix/src/trivial-rewrite/trivial-rewrite.c b/gnu/dist/postfix/src/trivial-rewrite/trivial-rewrite.c
index 61624ada5e4..71dc8b2f1dd 100644
--- a/gnu/dist/postfix/src/trivial-rewrite/trivial-rewrite.c
+++ b/gnu/dist/postfix/src/trivial-rewrite/trivial-rewrite.c
@@ -80,7 +80,7 @@
/* .fi
/* .IP \fBlocal_transport\fR
/* Where to deliver mail for destinations that match $\fBmydestination\fR
-/* or $\fBinet_interfaces\fR.
+/* or $\fBinet_interfaces\fR.
/* The default transport is \fBlocal\fR.
/* .sp
/* Syntax is \fItransport\fR:\fInexthop\fR; see \fBtransport\fR(5)
@@ -92,6 +92,10 @@
/* .sp
/* Syntax is \fItransport\fR:\fInexthop\fR; see \fBtransport\fR(5)
/* for details. The :\fInexthop\fR part is optional.
+/* .IP \fBparent_domain_matches_subdomains\fR
+/* List of Postfix features that use \fIdomain.name\fR patterns
+/* to match \fIsub.domain.name\fR (as opposed to
+/* requiring \fI.domain.name\fR patterns).
/* .IP \fBrelayhost\fR
/* The default host to send non-local mail to when no entry is matched
/* in the \fBtransport\fR(5) table.
@@ -181,7 +185,9 @@ static void rewrite_service(VSTREAM *stream, char *unused_service, char **argv)
* dedicated to address rewriting. All connection-management stuff is
* handled by the common code in multi_server.c.
*/
- if (mail_scan(stream, "%s", command) == 1) {
+ if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
+ ATTR_TYPE_STR, MAIL_ATTR_REQ, command,
+ ATTR_TYPE_END) == 1) {
if (strcmp(vstring_str(command), REWRITE_ADDR) == 0) {
status = rewrite_proto(stream);
} else if (strcmp(vstring_str(command), RESOLVE_ADDR) == 0) {
diff --git a/gnu/dist/postfix/src/util/alldig.c b/gnu/dist/postfix/src/util/alldig.c
new file mode 100644
index 00000000000..c4815f68c67
--- /dev/null
+++ b/gnu/dist/postfix/src/util/alldig.c
@@ -0,0 +1,48 @@
+/*++
+/* NAME
+/* alldig 3
+/* SUMMARY
+/* predicate if string is all numerical
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* int alldig(string)
+/* const char *string;
+/* DESCRIPTION
+/* alldig() determines if its argument is an all-numerical string.
+/* SEE ALSO
+/* An alldig() routine appears in Brian W. Kernighan, P.J. Plauger:
+/* "Software Tools", Addison-Wesley 1976.
+/* 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 <stringops.h>
+
+/* alldig - return true if string is all digits */
+
+int alldig(const char *string)
+{
+ const char *cp;
+
+ if (*string == 0)
+ return (0);
+ for (cp = string; *cp != 0; cp++)
+ if (!ISDIGIT(*cp))
+ return (0);
+ return (1);
+}
diff --git a/gnu/dist/postfix/src/util/attr.h b/gnu/dist/postfix/src/util/attr.h
index e81381ed337..c656dadf8e1 100644
--- a/gnu/dist/postfix/src/util/attr.h
+++ b/gnu/dist/postfix/src/util/attr.h
@@ -5,19 +5,80 @@
/* NAME
/* attr 3h
/* SUMMARY
-/* attribute list manager
+/* attribute list manipulations
/* SYNOPSIS
-/* #include <attr.h>
-/* DESCRIPTION
-/* .nf
+/* #include "attr.h"
+ DESCRIPTION
+ .nf
/*
- * External interface.
+ * System library.
*/
-extern void attr_enter(HTABLE *, const char *, const char *);
-extern void attr_free(HTABLE *);
+#include <stdarg.h>
-#define attr_find(table, name) htable_find((table), (name))
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * Attribute types. See attr_scan(3) for documentation.
+ */
+#define ATTR_TYPE_END 0 /* end of data */
+#define ATTR_TYPE_NUM 1 /* Unsigned integer */
+#define ATTR_TYPE_STR 2 /* Character string */
+#define ATTR_TYPE_HASH 3 /* Hash table */
+#define ATTR_TYPE_LONG 4 /* Unsigned long */
+
+ /*
+ * Flags that control processing. See attr_scan(3) for documentation.
+ */
+#define ATTR_FLAG_NONE 0
+#define ATTR_FLAG_MISSING (1<<0) /* Flag missing attribute */
+#define ATTR_FLAG_EXTRA (1<<1) /* Flag spurious attribute */
+#define ATTR_FLAG_MORE (1<<2) /* Don't skip or terminate */
+
+#define ATTR_FLAG_STRICT (ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA)
+#define ATTR_FLAG_ALL (07)
+
+#define attr_print attr_print0
+#define attr_vprint attr_vprint0
+#define attr_scan attr_scan0
+#define attr_vscan attr_vscan0
+
+ /*
+ * attr_print64.c.
+ */
+extern int attr_print64(VSTREAM *, int,...);
+extern int attr_vprint64(VSTREAM *, int, va_list);
+
+ /*
+ * attr_scan64.c.
+ */
+extern int attr_scan64(VSTREAM *, int,...);
+extern int attr_vscan64(VSTREAM *, int, va_list);
+
+ /*
+ * attr_print0.c.
+ */
+extern int attr_print0(VSTREAM *, int,...);
+extern int attr_vprint0(VSTREAM *, int, va_list);
+
+ /*
+ * attr_scan0.c.
+ */
+extern int attr_scan0(VSTREAM *, int,...);
+extern int attr_vscan0(VSTREAM *, int, va_list);
+
+ /*
+ * Attribute names for testing the compatibility of the read and write
+ * routines.
+ */
+#ifdef TEST
+#define ATTR_NAME_NUM "number"
+#define ATTR_NAME_STR "string"
+#define ATTR_NAME_LONG "long_number"
+#endif
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/attr_print0.c b/gnu/dist/postfix/src/util/attr_print0.c
new file mode 100644
index 00000000000..c39b72104da
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_print0.c
@@ -0,0 +1,209 @@
+/*++
+/* NAME
+/* attr_print0 3
+/* SUMMARY
+/* send attributes over byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_print0(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vprint0(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_print0() takes zero or more (name, value) simple attributes
+/* or (name, count, value) list attributes, and converts its input
+/* to a byte stream that can be recovered with attr_scan0(). The stream
+/* is not flushed.
+/*
+/* attr_vprint0() provides an alternate interface that is convenient
+/* for calling from within variadoc functions.
+/*
+/* Attributes are sent in the requested order as specified with the
+/* attr_print0() argument list. This routine satisfies the formatting
+/* rules as outlined in attr_scan0(3).
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to write the result to.
+/* .IP flags
+/* The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MORE
+/* After sending the requested attributes, leave the output stream in
+/* a state that is usable for more attribute sending operations on
+/* the same output attribute list.
+/* By default, attr_print0() automatically appends an attribute list
+/* terminator when it has sent the last requested attribute.
+/* .RE
+/* .IP type
+/* The type determines the arguments that follow.
+/* .RS
+/* .IP "ATTR_TYPE_NUM (char *, int)"
+/* This argument is followed by an attribute name and an integer.
+/* .IP "ATTR_TYPE_NUM (char *, long)"
+/* This argument is followed by an attribute name and a long integer.
+/* .IP "ATTR_TYPE_STR (char *, char *)"
+/* This argument is followed by an attribute name and a null-terminated
+/* string.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* The content of the hash table is sent as a sequence of string-valued
+/* attributes with names equal to the hash table lookup key.
+/* .IP ATTR_TYPE_END
+/* This terminates the attribute list.
+/* .RE
+/* DIAGNOSTICS
+/* The result value is 0 in case of success, VSTREAM_EOF in case
+/* of trouble.
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_scan0(3) recover attributes from byte stream
+/* 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 <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <htable.h>
+#include <attr.h>
+
+/* attr_vprint0 - send attribute list to stream */
+
+int attr_vprint0(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_print0";
+ int attr_type;
+ char *attr_name;
+ unsigned int_val;
+ unsigned long long_val;
+ char *str_val;
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Iterate over all (type, name, value) triples, and produce output on
+ * the fly.
+ */
+ while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) {
+ switch (attr_type) {
+ case ATTR_TYPE_NUM:
+ attr_name = va_arg(ap, char *);
+ vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
+ int_val = va_arg(ap, int);
+ vstream_fprintf(fp, "%u", (unsigned) int_val);
+ VSTREAM_PUTC('\0', fp);
+ if (msg_verbose)
+ msg_info("send attr %s = %u", attr_name, int_val);
+ break;
+ case ATTR_TYPE_LONG:
+ attr_name = va_arg(ap, char *);
+ vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
+ long_val = va_arg(ap, unsigned long);
+ vstream_fprintf(fp, "%lu", (unsigned long) long_val);
+ VSTREAM_PUTC('\0', fp);
+ if (msg_verbose)
+ msg_info("send attr %s = %lu", attr_name, long_val);
+ break;
+ case ATTR_TYPE_STR:
+ attr_name = va_arg(ap, char *);
+ vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
+ str_val = va_arg(ap, char *);
+ vstream_fwrite(fp, str_val, strlen(str_val) + 1);
+ if (msg_verbose)
+ msg_info("send attr %s = %s", attr_name, str_val);
+ break;
+ case ATTR_TYPE_HASH:
+ ht_info_list = htable_list(va_arg(ap, HTABLE *));
+ for (ht = ht_info_list; *ht; ht++) {
+ vstream_fwrite(fp, ht[0]->key, strlen(ht[0]->key) + 1);
+ vstream_fwrite(fp, ht[0]->value, strlen(ht[0]->value) + 1);
+ if (msg_verbose)
+ msg_info("send attr name %s value %s",
+ ht[0]->key, ht[0]->value);
+ }
+ myfree((char *) ht_info_list);
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, attr_type);
+ }
+ }
+ if ((flags & ATTR_FLAG_MORE) == 0)
+ VSTREAM_PUTC('\0', fp);
+ return (vstream_ferror(fp));
+}
+
+int attr_print0(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vprint0(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan0 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int main(int unused_argc, char **argv)
+{
+ HTABLE *table = htable_create(1);
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+ msg_verbose = 1;
+ htable_enter(table, "foo-name", mystrdup("foo-value"));
+ htable_enter(table, "bar-name", mystrdup("bar-value"));
+ attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END);
+ attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_END);
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ htable_free(table, myfree);
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_print64.c b/gnu/dist/postfix/src/util/attr_print64.c
new file mode 100644
index 00000000000..1fc37d7b346
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_print64.c
@@ -0,0 +1,253 @@
+/*++
+/* NAME
+/* attr_print64 3
+/* SUMMARY
+/* send attributes over byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_print64(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vprint64(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_print64() takes zero or more (name, value) simple attributes
+/* or (name, count, value) list attributes, and converts its input
+/* to a byte stream that can be recovered with attr_scan64(). The stream
+/* is not flushed.
+/*
+/* attr_vprint64() provides an alternate interface that is convenient
+/* for calling from within variadoc functions.
+/*
+/* Attributes are sent in the requested order as specified with the
+/* attr_print64() argument list. This routine satisfies the formatting
+/* rules as outlined in attr_scan64(3).
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to write the result to.
+/* .IP flags
+/* The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MORE
+/* After sending the requested attributes, leave the output stream in
+/* a state that is usable for more attribute sending operations on
+/* the same output attribute list.
+/* By default, attr_print64() automatically appends an attribute list
+/* terminator when it has sent the last requested attribute.
+/* .RE
+/* .IP type
+/* The type determines the arguments that follow.
+/* .RS
+/* .IP "ATTR_TYPE_NUM (char *, int)"
+/* This argument is followed by an attribute name and an integer.
+/* .IP "ATTR_TYPE_NUM (char *, long)"
+/* This argument is followed by an attribute name and a long integer.
+/* .IP "ATTR_TYPE_STR (char *, char *)"
+/* This argument is followed by an attribute name and a null-terminated
+/* string.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* The content of the hash table is sent as a sequence of string-valued
+/* attributes with names equal to the hash table lookup key.
+/* .IP ATTR_TYPE_END
+/* This terminates the attribute list.
+/* .RE
+/* DIAGNOSTICS
+/* The result value is 0 in case of success, VSTREAM_EOF in case
+/* of trouble.
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_scan64(3) recover attributes from byte stream
+/* 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 <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <htable.h>
+#include <base64_code.h>
+#include <attr.h>
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_print64_str - encode and send attribute information */
+
+static void attr_print64_str(VSTREAM *fp, const char *str, int len)
+{
+ static VSTRING *base64_buf;
+
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+
+ base64_encode(base64_buf, str, len);
+ vstream_fputs(STR(base64_buf), fp);
+}
+
+static void attr_print64_num(VSTREAM *fp, unsigned num)
+{
+ static VSTRING *plain;
+
+ if (plain == 0)
+ plain = vstring_alloc(10);
+
+ vstring_sprintf(plain, "%u", num);
+ attr_print64_str(fp, STR(plain), LEN(plain));
+}
+
+static void attr_print64_long_num(VSTREAM *fp, unsigned long long_num)
+{
+ static VSTRING *plain;
+
+ if (plain == 0)
+ plain = vstring_alloc(10);
+
+ vstring_sprintf(plain, "%lu", long_num);
+ attr_print64_str(fp, STR(plain), LEN(plain));
+}
+
+/* attr_vprint64 - send attribute list to stream */
+
+int attr_vprint64(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_print64";
+ int attr_type;
+ char *attr_name;
+ unsigned int_val;
+ unsigned long long_val;
+ char *str_val;
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Iterate over all (type, name, value) triples, and produce output on
+ * the fly.
+ */
+ while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) {
+ switch (attr_type) {
+ case ATTR_TYPE_NUM:
+ attr_name = va_arg(ap, char *);
+ attr_print64_str(fp, attr_name, strlen(attr_name));
+ int_val = va_arg(ap, int);
+ VSTREAM_PUTC(':', fp);
+ attr_print64_num(fp, (unsigned) int_val);
+ if (msg_verbose)
+ msg_info("send attr %s = %u", attr_name, int_val);
+ break;
+ case ATTR_TYPE_LONG:
+ attr_name = va_arg(ap, char *);
+ attr_print64_str(fp, attr_name, strlen(attr_name));
+ long_val = va_arg(ap, long);
+ VSTREAM_PUTC(':', fp);
+ attr_print64_long_num(fp, (unsigned long) long_val);
+ if (msg_verbose)
+ msg_info("send attr %s = %lu", attr_name, long_val);
+ break;
+ case ATTR_TYPE_STR:
+ attr_name = va_arg(ap, char *);
+ attr_print64_str(fp, attr_name, strlen(attr_name));
+ str_val = va_arg(ap, char *);
+ VSTREAM_PUTC(':', fp);
+ attr_print64_str(fp, str_val, strlen(str_val));
+ if (msg_verbose)
+ msg_info("send attr %s = %s", attr_name, str_val);
+ break;
+ case ATTR_TYPE_HASH:
+ ht_info_list = htable_list(va_arg(ap, HTABLE *));
+ for (ht = ht_info_list; *ht; ht++) {
+ attr_print64_str(fp, ht[0]->key, strlen(ht[0]->key));
+ VSTREAM_PUTC(':', fp);
+ attr_print64_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);
+ if (ht[1] != 0)
+ VSTREAM_PUTC('\n', fp);
+ }
+ myfree((char *) ht_info_list);
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, attr_type);
+ }
+ VSTREAM_PUTC('\n', fp);
+ }
+ if ((flags & ATTR_FLAG_MORE) == 0)
+ VSTREAM_PUTC('\n', fp);
+ return (vstream_ferror(fp));
+}
+
+int attr_print64(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vprint64(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan64 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int main(int unused_argc, char **argv)
+{
+ HTABLE *table = htable_create(1);
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+ msg_verbose = 1;
+ htable_enter(table, "foo-name", mystrdup("foo-value"));
+ htable_enter(table, "bar-name", mystrdup("bar-value"));
+ attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END);
+ attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_END);
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ htable_free(table, myfree);
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_scan.ref b/gnu/dist/postfix/src/util/attr_scan.ref
new file mode 100644
index 00000000000..cd06a27d82d
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan.ref
@@ -0,0 +1,36 @@
+./attr_print: send attr number = 4711
+./attr_print: send attr string = whoopee
+./attr_print: send attr name foo-name value foo-value
+./attr_print: send attr name bar-name value bar-value
+./attr_print: send attr number = 4711
+./attr_print: send attr string = whoopee
+./attr_scan: unknown_stream: wanted attribute: number
+./attr_scan: input attribute name: number
+./attr_scan: input attribute value: 4711
+./attr_scan: unknown_stream: wanted attribute: string
+./attr_scan: input attribute name: string
+./attr_scan: input attribute value: whoopee
+./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan: input attribute name: foo-name
+./attr_scan: input attribute value: foo-value
+./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan: input attribute name: bar-name
+./attr_scan: input attribute value: bar-value
+./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan: input attribute name: (end)
+./attr_scan: unknown_stream: wanted attribute: number
+./attr_scan: input attribute name: number
+./attr_scan: input attribute value: 4711
+./attr_scan: unknown_stream: wanted attribute: string
+./attr_scan: input attribute name: string
+./attr_scan: input attribute value: whoopee
+./attr_scan: unknown_stream: wanted attribute: (list terminator)
+./attr_scan: input attribute name: (end)
+number 4711
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
+number 4711
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
diff --git a/gnu/dist/postfix/src/util/attr_scan0.c b/gnu/dist/postfix/src/util/attr_scan0.c
new file mode 100644
index 00000000000..ea4cf880886
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan0.c
@@ -0,0 +1,441 @@
+/*++
+/* NAME
+/* attr_scan0 3
+/* SUMMARY
+/* recover attributes from byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_scan0(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vscan0(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_scan0() takes zero or more (name, value) request attributes
+/* and recovers the attribute values from the byte stream that was
+/* possibly generated by attr_print0().
+/*
+/* attr_vscan0() provides an alternative interface that is convenient
+/* for calling from within a variadic function.
+/*
+/* The input stream is formatted as follows, where (item)* stands
+/* for zero or more instances of the specified item, and where
+/* (item1 | item2) stands for choice:
+/*
+/* .in +5
+/* attr-list :== simple-attr* null
+/* .br
+/* simple-attr :== attr-name null attr-value null
+/* .br
+/* attr-name :== any string not containing null
+/* .br
+/* attr-value :== any string not containing null
+/* .br
+/* null :== the ASCII null character
+/* .in
+/*
+/* All attribute names and attribute values are sent as null terminated
+/* strings. Each string must be no longer than 2*var_line_limit
+/* characters. The formatting rules favor implementations in C.
+/*
+/* Normally, attributes must be received in the sequence as specified with
+/* the attr_scan0() argument list. The input stream may contain additional
+/* attributes at any point in the input stream, including additional
+/* instances of requested attributes.
+/*
+/* Additional input attributes or input attribute instances are silently
+/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
+/* (see below). This allows for some flexibility in the evolution of
+/* protocols while still providing the option of being strict where
+/* this is desirable.
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to recover the input attributes from.
+/* .IP flags
+/* The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MISSING
+/* Log a warning when the input attribute list terminates before all
+/* requested attributes are recovered. It is always an error when the
+/* input stream ends without the newline attribute list terminator.
+/* .IP ATTR_FLAG_EXTRA
+/* Log a warning and stop attribute recovery when the input stream
+/* contains an attribute that was not requested. This includes the
+/* case of additional instances of a requested attribute.
+/* .IP ATTR_FLAG_MORE
+/* After recovering the requested attributes, leave the input stream
+/* in a state that is usable for more attr_scan0() operations from the
+/* same input attribute list.
+/* By default, attr_scan0() skips forward past the input attribute list
+/* terminator.
+/* .IP ATTR_FLAG_STRICT
+/* For convenience, this value combines both ATTR_FLAG_MISSING and
+/* ATTR_FLAG_EXTRA.
+/* .IP ATTR_FLAG_NONE
+/* For convenience, this value requests none of the above.
+/* .RE
+/* .IP type
+/* 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.
+/* .IP "ATTR_TYPE_LONG (char *, long *)"
+/* This argument is followed by an attribute name and a long pointer.
+/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
+/* This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* All further input attributes are processed as string attributes.
+/* No specific attribute sequence is enforced.
+/* All attributes up to the attribute list terminator are read,
+/* but only the first instance of each attribute is stored.
+/* .sp
+/* The attribute string values are stored in the 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. Existing
+/* hash table entries are not replaced.
+/* .sp
+/* N.B. This construct must be followed by an ATTR_TYPE_END argument.
+/* .IP ATTR_TYPE_END
+/* This argument terminates the requested attribute list.
+/* .RE
+/* BUGS
+/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly
+/* untrusted sources. This is unsafe, unless the resulting table is
+/* queried only with known to be good attribute names.
+/* DIAGNOSTICS
+/* attr_scan0() and attr_vscan0() return -1 when malformed input is
+/* detected (string too long, incomplete line, missing end marker).
+/* Otherwise, the result value is the number of attributes that were
+/* successfully recovered from the input stream (a hash table counts
+/* as the number of entries stored into the table).
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_print0(3) send attributes over byte stream.
+/* 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 <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <htable.h>
+#include <attr.h>
+
+/* Application specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_scan0_string - pull a string from the input stream */
+
+static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
+{
+ extern int var_line_limit; /* XXX */
+ int limit = var_line_limit * 2;
+ int ch;
+
+ if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) {
+ msg_warn("premature end-of-input from %s while reading %s",
+ VSTREAM_PATH(fp), context);
+ return (-1);
+ }
+ if (ch != 0) {
+ msg_warn("string length > %d characters from %s while reading %s",
+ limit, VSTREAM_PATH(fp), context);
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
+ return (ch);
+}
+
+/* attr_scan0_number - pull a number from the input stream */
+
+static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_scan0_long_number - pull a number from the input stream */
+
+static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr,
+ VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_vscan0 - receive attribute list from stream */
+
+int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_scan0";
+ static VSTRING *str_buf = 0;
+ static VSTRING *name_buf = 0;
+ int wanted_type = -1;
+ char *wanted_name;
+ unsigned int *number;
+ unsigned long *long_number;
+ VSTRING *string;
+ HTABLE *hash_table;
+ int ch;
+ int conversions;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Initialize.
+ */
+ if (str_buf == 0) {
+ str_buf = vstring_alloc(10);
+ name_buf = vstring_alloc(10);
+ }
+
+ /*
+ * Iterate over all (type, name, value) triples.
+ */
+ for (conversions = 0; /* void */ ; conversions++) {
+
+ /*
+ * Determine the next attribute type and attribute name on the
+ * caller's wish list.
+ *
+ * If we're reading into a hash table, we already know that the
+ * attribute value is string-valued, and we get the attribute name
+ * from the input stream instead. This is secure only when the
+ * resulting table is queried with known to be good attribute names.
+ */
+ if (wanted_type != ATTR_TYPE_HASH) {
+ wanted_type = va_arg(ap, int);
+ if (wanted_type == ATTR_TYPE_END) {
+ if ((flags & ATTR_FLAG_MORE) != 0)
+ return (conversions);
+ wanted_name = "(list terminator)";
+ } else if (wanted_type == ATTR_TYPE_HASH) {
+ wanted_name = "(any attribute name or list terminator)";
+ hash_table = va_arg(ap, HTABLE *);
+ if (va_arg(ap, int) !=ATTR_TYPE_END)
+ msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
+ myname);
+ } else {
+ wanted_name = va_arg(ap, char *);
+ }
+ }
+
+ /*
+ * Locate the next attribute of interest in the input stream.
+ */
+ for (;;) {
+
+ /*
+ * Get the name of the next attribute. Hitting EOF is always bad.
+ * Hitting the end-of-input early is OK if the caller is prepared
+ * to deal with missing inputs.
+ */
+ if (msg_verbose)
+ msg_info("%s: wanted attribute: %s",
+ VSTREAM_PATH(fp), wanted_name);
+ if ((ch = attr_scan0_string(fp, name_buf,
+ "input attribute name")) == VSTREAM_EOF)
+ return (-1);
+ if (LEN(name_buf) == 0) {
+ if (wanted_type == ATTR_TYPE_END
+ || wanted_type == ATTR_TYPE_HASH)
+ return (conversions);
+ if ((flags & ATTR_FLAG_MISSING) != 0)
+ msg_warn("missing attribute %s in input from %s",
+ wanted_name, VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * See if the caller asks for this attribute.
+ */
+ if (wanted_type == ATTR_TYPE_HASH
+ || (wanted_type != ATTR_TYPE_END
+ && strcmp(wanted_name, STR(name_buf)) == 0))
+ break;
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("spurious attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * Skip over this attribute. The caller does not ask for it.
+ */
+ (void) attr_scan0_string(fp, str_buf, "input attribute value");
+ }
+
+ /*
+ * Do the requested conversion.
+ */
+ switch (wanted_type) {
+ case ATTR_TYPE_NUM:
+ number = va_arg(ap, unsigned int *);
+ if ((ch = attr_scan0_number(fp, number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ break;
+ case ATTR_TYPE_LONG:
+ long_number = va_arg(ap, unsigned long *);
+ if ((ch = attr_scan0_long_number(fp, long_number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ break;
+ case ATTR_TYPE_STR:
+ string = va_arg(ap, VSTRING *);
+ if ((ch = attr_scan0_string(fp, string,
+ "input attribute value")) < 0)
+ return (-1);
+ break;
+ case ATTR_TYPE_HASH:
+ if ((ch = attr_scan0_string(fp, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (htable_locate(hash_table, STR(name_buf)) != 0) {
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("duplicate attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+ } else {
+ htable_enter(hash_table, STR(name_buf),
+ mystrdup(STR(str_buf)));
+ }
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, wanted_type);
+ }
+ }
+}
+
+/* attr_scan0 - read attribute list from stream */
+
+int attr_scan0(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vscan0(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan0 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int var_line_limit = 2048;
+
+int main(int unused_argc, char **used_argv)
+{
+ VSTRING *str_val = vstring_alloc(1);
+ HTABLE *table = htable_create(1);
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+ int int_val;
+ long long_val;
+ int ret;
+
+ msg_verbose = 1;
+ msg_vstream_init(used_argv[0], VSTREAM_ERR);
+ if ((ret = attr_scan0(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END)) > 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if ((ret = attr_scan0(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_END)) == 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ vstring_free(str_val);
+ htable_free(table, myfree);
+
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_scan0.ref b/gnu/dist/postfix/src/util/attr_scan0.ref
new file mode 100644
index 00000000000..105382fd1ef
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan0.ref
@@ -0,0 +1,46 @@
+./attr_print0: send attr number = 4711
+./attr_print0: send attr long_number = 1234
+./attr_print0: send attr string = whoopee
+./attr_print0: send attr name foo-name value foo-value
+./attr_print0: send attr name bar-name value bar-value
+./attr_print0: send attr number = 4711
+./attr_print0: send attr long_number = 1234
+./attr_print0: send attr string = whoopee
+./attr_scan0: unknown_stream: wanted attribute: number
+./attr_scan0: input attribute name: number
+./attr_scan0: input attribute value: 4711
+./attr_scan0: unknown_stream: wanted attribute: long_number
+./attr_scan0: input attribute name: long_number
+./attr_scan0: input attribute value: 1234
+./attr_scan0: unknown_stream: wanted attribute: string
+./attr_scan0: input attribute name: string
+./attr_scan0: input attribute value: whoopee
+./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan0: input attribute name: foo-name
+./attr_scan0: input attribute value: foo-value
+./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan0: input attribute name: bar-name
+./attr_scan0: input attribute value: bar-value
+./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan0: input attribute name: (end)
+./attr_scan0: unknown_stream: wanted attribute: number
+./attr_scan0: input attribute name: number
+./attr_scan0: input attribute value: 4711
+./attr_scan0: unknown_stream: wanted attribute: long_number
+./attr_scan0: input attribute name: long_number
+./attr_scan0: input attribute value: 1234
+./attr_scan0: unknown_stream: wanted attribute: string
+./attr_scan0: input attribute name: string
+./attr_scan0: input attribute value: whoopee
+./attr_scan0: unknown_stream: wanted attribute: (list terminator)
+./attr_scan0: input attribute name: (end)
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
diff --git a/gnu/dist/postfix/src/util/attr_scan64.c b/gnu/dist/postfix/src/util/attr_scan64.c
new file mode 100644
index 00000000000..d8631ef1cac
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan64.c
@@ -0,0 +1,506 @@
+/*++
+/* NAME
+/* attr_scan64 3
+/* SUMMARY
+/* recover attributes from byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_scan64(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vscan64(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_scan64() takes zero or more (name, value) request attributes
+/* and recovers the attribute values from the byte stream that was
+/* possibly generated by attr_print64().
+/*
+/* attr_vscan64() provides an alternative interface that is convenient
+/* for calling from within a variadic function.
+/*
+/* The input stream is formatted as follows, where (item)* stands
+/* for zero or more instances of the specified item, and where
+/* (item1 | item2) stands for choice:
+/*
+/* .in +5
+/* attr-list :== simple-attr* newline
+/* .br
+/* simple-attr :== attr-name colon attr-value newline
+/* .br
+/* attr-name :== any base64 encoded string
+/* .br
+/* attr-value :== any base64 encoded string
+/* .br
+/* colon :== the ASCII colon character
+/* .br
+/* newline :== the ASCII newline character
+/* .in
+/*
+/* All attribute names and attribute values are sent as base64-encoded
+/* strings. Each base64 encoding must be no longer than 2*var_line_limit
+/* characters. The formatting rules aim to make implementations in PERL
+/* and other languages easy.
+/*
+/* Normally, attributes must be received in the sequence as specified with
+/* the attr_scan64() argument list. The input stream may contain additional
+/* attributes at any point in the input stream, including additional
+/* instances of requested attributes.
+/*
+/* Additional input attributes or input attribute instances are silently
+/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
+/* (see below). This allows for some flexibility in the evolution of
+/* protocols while still providing the option of being strict where
+/* this is desirable.
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to recover the input attributes from.
+/* .IP flags
+/* The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MISSING
+/* Log a warning when the input attribute list terminates before all
+/* requested attributes are recovered. It is always an error when the
+/* input stream ends without the newline attribute list terminator.
+/* .IP ATTR_FLAG_EXTRA
+/* Log a warning and stop attribute recovery when the input stream
+/* contains an attribute that was not requested. This includes the
+/* case of additional instances of a requested attribute.
+/* .IP ATTR_FLAG_MORE
+/* After recovering the requested attributes, leave the input stream
+/* in a state that is usable for more attr_scan64() operations from the
+/* same input attribute list.
+/* By default, attr_scan64() skips forward past the input attribute list
+/* terminator.
+/* .IP ATTR_FLAG_STRICT
+/* For convenience, this value combines both ATTR_FLAG_MISSING and
+/* ATTR_FLAG_EXTRA.
+/* .IP ATTR_FLAG_NONE
+/* For convenience, this value requests none of the above.
+/* .RE
+/* .IP type
+/* 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.
+/* .IP "ATTR_TYPE_LONG (char *, long *)"
+/* This argument is followed by an attribute name and a long pointer.
+/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
+/* This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* All further input attributes are processed as string attributes.
+/* No specific attribute sequence is enforced.
+/* All attributes up to the attribute list terminator are read,
+/* but only the first instance of each attribute is stored.
+/* .sp
+/* The attribute string values are stored in the 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. Existing
+/* hash table entries are not replaced.
+/* .sp
+/* N.B. This construct must be followed by an ATTR_TYPE_END argument.
+/* .IP ATTR_TYPE_END
+/* This argument terminates the requested attribute list.
+/* .RE
+/* BUGS
+/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly
+/* untrusted sources. This is unsafe, unless the resulting table is
+/* queried only with known to be good attribute names.
+/* DIAGNOSTICS
+/* attr_scan64() and attr_vscan64() return -1 when malformed input is
+/* detected (string too long, incomplete line, missing end marker).
+/* Otherwise, the result value is the number of attributes that were
+/* successfully recovered from the input stream (a hash table counts
+/* as the number of entries stored into the table).
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_print64(3) send attributes over byte stream.
+/* 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 <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <htable.h>
+#include <base64_code.h>
+#include <attr.h>
+
+/* Application specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_scan64_string - pull a string from the input stream */
+
+static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
+{
+ static VSTRING *base64_buf = 0;
+ extern int var_line_limit; /* XXX */
+ int limit = var_line_limit * 2;
+ int ch;
+
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+
+ VSTRING_RESET(base64_buf);
+ while ((ch = VSTREAM_GETC(fp)) != ':' && ch != '\n') {
+ if (ch == VSTREAM_EOF) {
+ msg_warn("premature end-of-input from %s while reading %s",
+ VSTREAM_PATH(fp), 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) {
+ msg_warn("malformed base64 data from %s: %.100s",
+ VSTREAM_PATH(fp), STR(base64_buf));
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
+ return (ch);
+}
+
+/* attr_scan64_number - pull a number from the input stream */
+
+static int attr_scan64_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan64_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_scan64_long_number - pull a number from the input stream */
+
+static int attr_scan64_long_number(VSTREAM *fp, unsigned long *ptr,
+ VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan64_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_vscan64 - receive attribute list from stream */
+
+int attr_vscan64(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_scan64";
+ static VSTRING *str_buf = 0;
+ static VSTRING *name_buf = 0;
+ int wanted_type = -1;
+ char *wanted_name;
+ unsigned int *number;
+ unsigned long *long_number;
+ VSTRING *string;
+ HTABLE *hash_table;
+ int ch;
+ int conversions;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Initialize.
+ */
+ if (str_buf == 0) {
+ str_buf = vstring_alloc(10);
+ name_buf = vstring_alloc(10);
+ }
+
+ /*
+ * Iterate over all (type, name, value) triples.
+ */
+ for (conversions = 0; /* void */ ; conversions++) {
+
+ /*
+ * Determine the next attribute type and attribute name on the
+ * caller's wish list.
+ *
+ * If we're reading into a hash table, we already know that the
+ * attribute value is string-valued, and we get the attribute name
+ * from the input stream instead. This is secure only when the
+ * resulting table is queried with known to be good attribute names.
+ */
+ if (wanted_type != ATTR_TYPE_HASH) {
+ wanted_type = va_arg(ap, int);
+ if (wanted_type == ATTR_TYPE_END) {
+ if ((flags & ATTR_FLAG_MORE) != 0)
+ return (conversions);
+ wanted_name = "(list terminator)";
+ } else if (wanted_type == ATTR_TYPE_HASH) {
+ wanted_name = "(any attribute name or list terminator)";
+ hash_table = va_arg(ap, HTABLE *);
+ if (va_arg(ap, int) !=ATTR_TYPE_END)
+ msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
+ myname);
+ } else {
+ wanted_name = va_arg(ap, char *);
+ }
+ }
+
+ /*
+ * Locate the next attribute of interest in the input stream.
+ */
+ for (;;) {
+
+ /*
+ * Get the name of the next attribute. Hitting EOF is always bad.
+ * Hitting the end-of-input early is OK if the caller is prepared
+ * to deal with missing inputs.
+ */
+ if (msg_verbose)
+ msg_info("%s: wanted attribute: %s",
+ VSTREAM_PATH(fp), wanted_name);
+ if ((ch = attr_scan64_string(fp, name_buf,
+ "input attribute name")) == VSTREAM_EOF)
+ return (-1);
+ if (ch == '\n' && LEN(name_buf) == 0) {
+ if (wanted_type == ATTR_TYPE_END
+ || wanted_type == ATTR_TYPE_HASH)
+ return (conversions);
+ if ((flags & ATTR_FLAG_MISSING) != 0)
+ msg_warn("missing attribute %s in input from %s",
+ wanted_name, VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * See if the caller asks for this attribute.
+ */
+ if (wanted_type == ATTR_TYPE_HASH
+ || (wanted_type != ATTR_TYPE_END
+ && strcmp(wanted_name, STR(name_buf)) == 0))
+ break;
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("spurious attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * Skip over this attribute. The caller does not ask for it.
+ */
+ while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
+ /* void */ ;
+ }
+
+ /*
+ * Do the requested conversion. If the target attribute is a
+ * non-array type, disallow sending a multi-valued attribute, and
+ * disallow sending no value. If the target attribute is an array
+ * type, allow the sender to send a zero-element array (i.e. no value
+ * at all). XXX Need to impose a bound on the number of array
+ * elements.
+ */
+ switch (wanted_type) {
+ case ATTR_TYPE_NUM:
+ if (ch != ':') {
+ msg_warn("missing value for number attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ number = va_arg(ap, unsigned int *);
+ if ((ch = attr_scan64_number(fp, number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ break;
+ case ATTR_TYPE_LONG:
+ if (ch != ':') {
+ msg_warn("missing value for number attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ long_number = va_arg(ap, unsigned long *);
+ if ((ch = attr_scan64_long_number(fp, long_number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ break;
+ case ATTR_TYPE_STR:
+ if (ch != ':') {
+ msg_warn("missing value for string attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ string = va_arg(ap, VSTRING *);
+ if ((ch = attr_scan64_string(fp, string,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ break;
+ case ATTR_TYPE_HASH:
+ if (ch != ':') {
+ msg_warn("missing value for string attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ if ((ch = attr_scan64_string(fp, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ if (htable_locate(hash_table, STR(name_buf)) != 0) {
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("duplicate attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+ } else {
+ htable_enter(hash_table, STR(name_buf),
+ mystrdup(STR(str_buf)));
+ }
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, wanted_type);
+ }
+ }
+}
+
+/* attr_scan64 - read attribute list from stream */
+
+int attr_scan64(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vscan64(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan64 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int var_line_limit = 2048;
+
+int main(int unused_argc, char **used_argv)
+{
+ VSTRING *str_val = vstring_alloc(1);
+ HTABLE *table = htable_create(1);
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+ int int_val;
+ long long_val;
+ int ret;
+
+ msg_verbose = 1;
+ msg_vstream_init(used_argv[0], VSTREAM_ERR);
+ if ((ret = attr_scan64(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END)) > 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if ((ret = attr_scan64(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_END)) == 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ vstring_free(str_val);
+ htable_free(table, myfree);
+
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_scan64.ref b/gnu/dist/postfix/src/util/attr_scan64.ref
new file mode 100644
index 00000000000..1b3a01581cf
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan64.ref
@@ -0,0 +1,46 @@
+./attr_print64: send attr number = 4711
+./attr_print64: send attr long_number = 1234
+./attr_print64: send attr string = whoopee
+./attr_print64: send attr name foo-name value foo-value
+./attr_print64: send attr name bar-name value bar-value
+./attr_print64: send attr number = 4711
+./attr_print64: send attr long_number = 1234
+./attr_print64: send attr string = whoopee
+./attr_scan64: unknown_stream: wanted attribute: number
+./attr_scan64: input attribute name: number
+./attr_scan64: input attribute value: 4711
+./attr_scan64: unknown_stream: wanted attribute: long_number
+./attr_scan64: input attribute name: long_number
+./attr_scan64: input attribute value: 1234
+./attr_scan64: unknown_stream: wanted attribute: string
+./attr_scan64: input attribute name: string
+./attr_scan64: input attribute value: whoopee
+./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan64: input attribute name: foo-name
+./attr_scan64: input attribute value: foo-value
+./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan64: input attribute name: bar-name
+./attr_scan64: input attribute value: bar-value
+./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan64: input attribute name: (end)
+./attr_scan64: unknown_stream: wanted attribute: number
+./attr_scan64: input attribute name: number
+./attr_scan64: input attribute value: 4711
+./attr_scan64: unknown_stream: wanted attribute: long_number
+./attr_scan64: input attribute name: long_number
+./attr_scan64: input attribute value: 1234
+./attr_scan64: unknown_stream: wanted attribute: string
+./attr_scan64: input attribute name: string
+./attr_scan64: input attribute value: whoopee
+./attr_scan64: unknown_stream: wanted attribute: (list terminator)
+./attr_scan64: input attribute name: (end)
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
diff --git a/gnu/dist/postfix/src/util/base64_code.c b/gnu/dist/postfix/src/util/base64_code.c
new file mode 100644
index 00000000000..21210e6053d
--- /dev/null
+++ b/gnu/dist/postfix/src/util/base64_code.c
@@ -0,0 +1,201 @@
+/*++
+/* 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>
+#include <string.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 unsigned 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 = (unsigned char *) 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,x,l) { \
+ if (base64_decode((b),(x),(l)) == 0) \
+ msg_panic("bad base64: %s", (x)); \
+ }
+#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/gnu/dist/postfix/src/util/base64_code.h b/gnu/dist/postfix/src/util/base64_code.h
new file mode 100644
index 00000000000..d018947e1c1
--- /dev/null
+++ b/gnu/dist/postfix/src/util/base64_code.h
@@ -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
diff --git a/gnu/dist/postfix/src/util/cache.in b/gnu/dist/postfix/src/util/cache.in
new file mode 100644
index 00000000000..89974bd2c66
--- /dev/null
+++ b/gnu/dist/postfix/src/util/cache.in
@@ -0,0 +1,26 @@
+a
+1
+b
+2
+c
+3
+d
+4
+e
+5
+f
+6
+f
+e
+d
+c
+b
+a
+1
+b
+c
+d
+e
+f
+6
+f
diff --git a/gnu/dist/postfix/src/util/dict.c b/gnu/dist/postfix/src/util/dict.c
index 7d07d3be0be..5fd30887983 100644
--- a/gnu/dist/postfix/src/util/dict.c
+++ b/gnu/dist/postfix/src/util/dict.c
@@ -182,6 +182,7 @@
#include "vstring.h"
#include "readlline.h"
#include "mac_parse.h"
+#include "stringops.h"
#include "dict.h"
#include "dict_ht.h"
@@ -372,43 +373,18 @@ void dict_load_file(const char *dict_name, const char *path)
void dict_load_fp(const char *dict_name, VSTREAM *fp)
{
VSTRING *buf;
- char *start;
char *member;
char *val;
- char *cp;
- char *ep;
int lineno;
-
- /*
- * Ugly macros to make complex expressions less unreadable.
- */
-#define SKIP(start, var, cond) \
- for (var = start; *var && (cond); var++);
-
-#define TRIM(s) { \
- char *p; \
- for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \
- *p = 0; \
- }
+ const char *err;
buf = vstring_alloc(100);
lineno = 0;
- while (readlline(buf, fp, &lineno, READLL_STRIPNL)) {
- start = STR(buf);
- SKIP(start, member, ISSPACE(*member)); /* find member begin */
- if (*member == 0 || *member == '#')
- continue; /* comment or blank line */
- SKIP(member, ep, !ISSPACE(*ep) && *ep != '='); /* find member end */
- SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */
- if (*cp && *cp != '=') /* need '=' or end of string */
- msg_fatal("%s, line %d: whitespace in attribute name: \"%s\"",
- VSTREAM_PATH(fp), lineno, member);
- if (*cp)
- cp++; /* skip over '=' */
- *ep = 0; /* terminate member name */
- SKIP(cp, val, ISSPACE(*val)); /* skip leading blanks */
- TRIM(val); /* trim trailing blanks */
+ while (readlline(buf, fp, &lineno)) {
+ if ((err = split_nameval(STR(buf), &member, &val)) != 0)
+ msg_fatal("%s, line %d: %s: \"%s\"",
+ VSTREAM_PATH(fp), lineno, err, STR(buf));
dict_update(dict_name, member, val);
}
vstring_free(buf);
diff --git a/gnu/dist/postfix/src/util/dict.h b/gnu/dist/postfix/src/util/dict.h
index 5a669aa096d..1d9d8c4a129 100644
--- a/gnu/dist/postfix/src/util/dict.h
+++ b/gnu/dist/postfix/src/util/dict.h
@@ -55,6 +55,7 @@ extern DICT *dict_debug(DICT *);
#define DICT_FLAG_DUP_REPLACE (1<<7) /* if file, replace dups */
#define DICT_FLAG_SYNC_UPDATE (1<<8) /* if file, sync updates */
#define DICT_FLAG_DEBUG (1<<9) /* log access */
+#define DICT_FLAG_FOLD_KEY (1<<10) /* lowercase the lookup key */
extern int dict_unknown_allowed;
extern int dict_errno;
diff --git a/gnu/dist/postfix/src/util/dict_db.c b/gnu/dist/postfix/src/util/dict_db.c
index 69518e10f8a..423244a3a7f 100644
--- a/gnu/dist/postfix/src/util/dict_db.c
+++ b/gnu/dist/postfix/src/util/dict_db.c
@@ -437,6 +437,23 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
#endif
+ /*
+ * Mismatches between #include file and library are a common cause for
+ * trouble.
+ */
+#if DB_VERSION_MAJOR > 1
+ int major_version;
+ int minor_version;
+ int patch_version;
+
+ (void) db_version(&major_version, &minor_version, &patch_version);
+ if (major_version != DB_VERSION_MAJOR)
+ msg_fatal("incorrect version of Berkeley DB: "
+ "compiled against %d.%d.%d, linked against %d.%d.%d",
+ DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
+ major_version, minor_version, patch_version);
+#endif
+
db_path = concatenate(path, ".db", (char *) 0);
/*
diff --git a/gnu/dist/postfix/src/util/dict_ldap.c b/gnu/dist/postfix/src/util/dict_ldap.c
index ebbfe8f9003..2cf8936c43e 100644
--- a/gnu/dist/postfix/src/util/dict_ldap.c
+++ b/gnu/dist/postfix/src/util/dict_ldap.c
@@ -62,6 +62,9 @@
/* The cache size in bytes. Does nothing if the cache is off, of course.
/* .IP \fIldapsource_\fRdereference
/* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page.
+/* .IP \fIldapsource_\fRdebuglevel
+/* Debug level. See 'loglevel' option in slapd.conf(5) man page.
+/* Currently only in openldap libraries (and derivatives).
/* SEE ALSO
/* dict(3) generic dictionary manager
/* AUTHOR(S)
@@ -77,6 +80,9 @@
/* John Hensley
/* john@sunislelodge.com
/*
+/* LaMont Jones
+/* lamont@hp.com
+/*
/*--*/
/* System library. */
@@ -94,6 +100,14 @@
#include <ldap.h>
#include <string.h>
+/* Handle differences between LDAP SDK's constant definitions */
+#ifndef LDAP_CONST
+#define LDAP_CONST const
+#endif
+#ifndef LDAP_OPT_SUCCESS
+#define LDAP_OPT_SUCCESS 0
+#endif
+
/* Utility library. */
#include "match_list.h"
@@ -131,6 +145,7 @@ typedef struct {
long cache_expiry;
long cache_size;
int dereference;
+ int debuglevel;
LDAP *ld;
} DICT_LDAP;
@@ -139,6 +154,13 @@ typedef struct {
*/
static jmp_buf env;
+static void dict_ldap_logprint(LDAP_CONST char *data)
+{
+ char *myname = "dict_ldap_debug";
+
+ msg_info("%s: %s", myname, data);
+}
+
static void dict_ldap_timeout(int unused_sig)
{
longjmp(env, 1);
@@ -153,6 +175,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
#ifdef LDAP_API_FEATURE_X_MEMCACHE
LDAPMemCache *dircache;
+
#endif
#ifdef LDAP_OPT_NETWORK_TIMEOUT
@@ -170,14 +193,16 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
dict_ldap->ld = ldap_init(dict_ldap->server_host,
(int) dict_ldap->server_port);
if (dict_ldap->ld == NULL) {
- msg_warn("%s: Unable to int LDAP server %s",
+ msg_warn("%s: Unable to init LDAP server %s",
myname, dict_ldap->server_host);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
mytimeval.tv_sec = dict_ldap->timeout;
mytimeval.tv_usec = 0;
- ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval);
+ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval) !=
+ LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to set network timeout.", myname);
#else
if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
msg_warn("%s: Error setting signal handler for open timeout: %m",
@@ -212,11 +237,24 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
* Mattice for this, and to Hery Rakotoarisoa for the v3 update.
*/
#if (LDAP_API_VERSION >= 2000)
- ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF, &(dict_ldap->dereference));
+ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF,
+ &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to set dereference option.", myname);
#else
dict_ldap->ld->ld_deref = dict_ldap->dereference;
#endif
+#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
+ if (dict_ldap->debuglevel > 0 &&
+ ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
+ (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
+ msg_warn("%s: Unable to set ber logprint function.", myname);
+ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEBUG_LEVEL,
+ &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to set LDAP debug level.", myname);
+#endif
+
+
/*
* If this server requires a bind, do so. Thanks to Sam Tardieu for
* noticing that the original bind call was broken.
@@ -267,7 +305,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
} else {
if (msg_verbose)
msg_info("%s: Caching enabled for %s",
- myname, dict_ldap->ldapsource);
+ myname, dict_ldap->ldapsource);
}
}
#else
@@ -342,7 +380,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
if (strcasecmp(dict_ldap->result_attributes->argv[i],
attr) == 0) {
if (msg_verbose)
- msg_info("%s: search returned %d value(s) for requested result attribute %s", myname, i, attr);
+ msg_info("%s: search returned %ld value(s) for requested result attribute %s", myname, i, attr);
break;
}
}
@@ -374,21 +412,23 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
0, &tv, &resloop);
}
switch (rc) {
- case LDAP_SUCCESS:
- dict_ldap_get_values(dict_ldap, resloop, result);
- break;
- case LDAP_NO_SUCH_OBJECT:
- /* Go ahead and treat this as though the DN existed
- * and just didn't have any result attributes.
- */
- msg_warn("%s: DN %s not found, skipping ", myname,
- vals[i]);
- break;
- default:
- msg_warn("%s: search error %d: %s ", myname, rc,
+ case LDAP_SUCCESS:
+ dict_ldap_get_values(dict_ldap, resloop, result);
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+
+ /*
+ * Go ahead and treat this as though the DN existed
+ * and just didn't have any result attributes.
+ */
+ msg_warn("%s: DN %s not found, skipping ", myname,
+ vals[i]);
+ break;
+ default:
+ msg_warn("%s: search error %d: %s ", myname, rc,
ldap_err2string(rc));
- dict_errno = DICT_ERR_RETRY;
- break;
+ dict_errno = DICT_ERR_RETRY;
+ break;
}
if (resloop != 0)
@@ -428,11 +468,12 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
* load on the LDAP server.
*/
if (dict_ldap->domain) {
- const char *p=strrchr(name,'@');
+ const char *p = strrchr(name, '@');
+
if (p != 0)
- p=p+1;
+ p = p + 1;
else
- p=name;
+ p = name;
if (match_list_match(dict_ldap->domain, p) == 0) {
if (msg_verbose)
msg_info("%s: domain of %s not found in domain list", myname,
@@ -454,7 +495,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (dict_ldap->ld == NULL) {
if (msg_verbose)
msg_info
- ("%s: No existing connection for ldapsource %s, reopening",
+ ("%s: No existing connection for LDAP source %s, reopening",
myname, dict_ldap->ldapsource);
dict_ldap_connect(dict_ldap);
@@ -465,7 +506,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (dict_errno)
return (0);
} else if (msg_verbose)
- msg_info("%s: Using existing connection for ldapsource %s",
+ msg_info("%s: Using existing connection for LDAP source %s",
myname, dict_ldap->ldapsource);
@@ -540,31 +581,32 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
/*
* Make sure it's %[sud] and not something else. For backward
- * compatibilty, treat anything other than %u or %d as %s, with
- * a warning.
+ * compatibilty, treat anything other than %u or %d as %s, with a
+ * warning.
*/
if (*(sub) == '%') {
- char *u=vstring_str(escaped_name);
- char *p=strchr(u,'@');
- switch (*(sub+1)) {
- case 'd':
- if (p)
- vstring_strcat(filter_buf, p+1);
- break;
- case 'u':
- if (p)
- vstring_strncat(filter_buf, u, p-u);
- else
- vstring_strcat(filter_buf, u);
- break;
- default:
- msg_warn
- ("%s: Invalid lookup substitution format '%%%c'!",
- myname, *(sub + 1));
- /* fall through */
- case 's':
+ char *u = vstring_str(escaped_name);
+ char *p = strchr(u, '@');
+
+ switch (*(sub + 1)) {
+ case 'd':
+ if (p)
+ vstring_strcat(filter_buf, p + 1);
+ break;
+ case 'u':
+ if (p)
+ vstring_strncat(filter_buf, u, p - u);
+ else
vstring_strcat(filter_buf, u);
- break;
+ break;
+ default:
+ msg_warn
+ ("%s: Invalid lookup substitution format '%%%c'!",
+ myname, *(sub + 1));
+ /* fall through */
+ case 's':
+ vstring_strcat(filter_buf, u);
+ break;
}
sub++;
} else
@@ -580,11 +622,35 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
msg_info("%s: Searching with filter %s", myname,
vstring_str(filter_buf));
- if ((rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
- dict_ldap->scope,
- vstring_str(filter_buf),
- dict_ldap->result_attributes->argv,
- 0, &tv, &res)) == LDAP_SUCCESS) {
+ rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
+ dict_ldap->scope,
+ vstring_str(filter_buf),
+ dict_ldap->result_attributes->argv,
+ 0, &tv, &res);
+
+ if (rc == LDAP_SERVER_DOWN) {
+ if (msg_verbose)
+ msg_info("%s: Lost connection for LDAP source %s, reopening",
+ myname, dict_ldap->ldapsource);
+
+ ldap_unbind(dict_ldap->ld);
+ dict_ldap->ld = NULL;
+ dict_ldap_connect(dict_ldap);
+
+ /*
+ * if dict_ldap_connect() set dict_errno, abort.
+ */
+ if (dict_errno)
+ return (0);
+
+ rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
+ dict_ldap->scope,
+ vstring_str(filter_buf),
+ dict_ldap->result_attributes->argv,
+ 0, &tv, &res);
+
+ }
+ if (rc == LDAP_SUCCESS) {
/*
* Search worked; extract the requested result_attribute.
@@ -598,7 +664,9 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
*/
#if (LDAP_API_VERSION >= 2000)
- ldap_get_option(dict_ldap->ld, LDAP_OPT_ERROR_NUMBER, &rc);
+ if (ldap_get_option(dict_ldap->ld, LDAP_OPT_ERROR_NUMBER, &rc) !=
+ LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to get last error number.", myname);
if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR)
msg_warn("%s: Had some trouble with entries returned by search: %s", myname, ldap_err2string(rc));
#else
@@ -641,7 +709,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
ldap_msgfree(res);
if (filter_buf != 0)
vstring_free(filter_buf);
- if (escaped_name != 0)
+ if (escaped_name != 0)
vstring_free(escaped_name);
/*
@@ -759,7 +827,12 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
mystrdup((char *) get_mail_conf_str(vstring_str(config_param),
"", 0, 0));
if (*domainlist) {
+#ifdef MATCH_FLAG_NONE
+ dict_ldap->domain = match_list_init(MATCH_FLAG_NONE,
+ domainlist, 1, match_string);
+#else
dict_ldap->domain = match_list_init(domainlist, 1, match_string);
+#endif
if (dict_ldap->domain == NULL)
msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it", myname, domainlist);
if (msg_verbose)
@@ -892,6 +965,18 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
msg_info("%s: %s is %d", myname, vstring_str(config_param),
dict_ldap->dereference);
+ /*
+ * Debug level.
+ */
+#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
+ vstring_sprintf(config_param, "%s_debuglevel", ldapsource);
+ dict_ldap->debuglevel = get_mail_conf_int(vstring_str(config_param), 0, 0,
+ 0);
+ if (msg_verbose)
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->debuglevel);
+#endif
+
dict_ldap_connect(dict_ldap);
/*
@@ -908,7 +993,7 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
/*
* Otherwise, we're all set. Return the new dict_ldap structure.
*/
- return (DICT_DEBUG(&dict_ldap->dict));
+ return (DICT_DEBUG (&dict_ldap->dict));
}
#endif
diff --git a/gnu/dist/postfix/src/util/dict_mysql.c b/gnu/dist/postfix/src/util/dict_mysql.c
index 28fc7015639..f778e928d08 100644
--- a/gnu/dist/postfix/src/util/dict_mysql.c
+++ b/gnu/dist/postfix/src/util/dict_mysql.c
@@ -274,14 +274,14 @@ static MYSQL_RES *plmysql_query(PLMYSQL *PLDB,
if (res == 0 && host->stat == STATACTIVE) {
if (!(mysql_query(host->db, query))) {
if ((res = mysql_store_result(host->db)) == 0) {
- msg_warn("%s", mysql_error(host->db));
+ msg_warn("mysql query failed: %s", mysql_error(host->db));
plmysql_down_host(host);
} else {
if (msg_verbose)
msg_info("dict_mysql: successful query from host %s", host->hostname);
}
} else {
- msg_warn("%s", mysql_error(host->db));
+ msg_warn("mysql query failed: %s", mysql_error(host->db));
plmysql_down_host(host);
}
}
@@ -323,7 +323,8 @@ static void plmysql_connect_single(HOST *host, char *dbname, char *username, cha
host->hostname);
host->stat = STATACTIVE;
} else {
- msg_warn("%s", mysql_error(host->db));
+ msg_warn("connect to mysql server %s: %s",
+ host->hostname, mysql_error(host->db));
plmysql_down_host(host);
}
if (hostname)
@@ -366,7 +367,7 @@ DICT *dict_mysql_open(const char *name, int unused_open_flags, int dict_flags)
if (dict_mysql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
dict_register(name, (DICT *) dict_mysql);
- return (DICT_DEBUG(&dict_mysql->dict));
+ return (DICT_DEBUG (&dict_mysql->dict));
}
/* mysqlname_parse - parse mysql configuration file */
diff --git a/gnu/dist/postfix/src/util/dict_open.c b/gnu/dist/postfix/src/util/dict_open.c
index 2c15ac897ef..52a5dddc14b 100644
--- a/gnu/dist/postfix/src/util/dict_open.c
+++ b/gnu/dist/postfix/src/util/dict_open.c
@@ -81,6 +81,8 @@
/* .IP DICT_FLAG_SYNC_UPDATE
/* With file-based maps, flush I/O buffers to file after each update.
/* Thus feature is not supported with some file-based dictionaries.
+/* .IP DICT_FLAG_FOLD_KEY
+/* Fold the lookup key to lower case.
/* .PP
/* The dictionary types are as follows:
/* .IP environ
@@ -166,6 +168,7 @@
#include <dict_mysql.h>
#include <dict_pcre.h>
#include <dict_regexp.h>
+#include <dict_static.h>
#include <stringops.h>
#include <split_at.h>
#include <htable.h>
@@ -212,6 +215,7 @@ static DICT_OPEN_INFO dict_open_info[] = {
#ifdef HAS_POSIX_REGEXP
DICT_TYPE_REGEXP, dict_regexp_open,
#endif
+ DICT_TYPE_STATIC, dict_static_open,
0,
};
@@ -339,6 +343,7 @@ static NORETURN usage(char *myname)
int main(int argc, char **argv)
{
VSTRING *keybuf = vstring_alloc(1);
+ VSTRING *inbuf = vstring_alloc(1);
DICT *dict;
char *dict_name;
int open_flags;
@@ -374,13 +379,16 @@ int main(int argc, char **argv)
dict_name = argv[optind];
dict = dict_open(dict_name, open_flags, DICT_FLAG_LOCK);
dict_register(dict_name, dict);
- while (vstring_fgets_nonl(keybuf, VSTREAM_IN)) {
- bufp = vstring_str(keybuf);
- if ((cmd = mystrtok(&bufp, " ")) == 0)
+ while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
+ bufp = vstring_str(inbuf);
+ if ((cmd = mystrtok(&bufp, " ")) == 0 || *bufp == 0) {
+ vstream_printf("usage: del key|get key|put key=value\n");
+ vstream_fflush(VSTREAM_OUT);
continue;
+ }
if (dict_changed())
msg_warn("dictionary has changed");
- key = mystrtok(&bufp, " =");
+ key = vstring_str(unescape(keybuf, mystrtok(&bufp, " =")));
value = mystrtok(&bufp, " =");
if (strcmp(cmd, "del") == 0 && key && !value) {
if (dict_del(dict, key))
@@ -404,6 +412,7 @@ int main(int argc, char **argv)
vstream_fflush(VSTREAM_OUT);
}
vstring_free(keybuf);
+ vstring_free(inbuf);
dict_close(dict);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/dict_pcre.c b/gnu/dist/postfix/src/util/dict_pcre.c
index 6d3c6f52393..1cc0f19f946 100644
--- a/gnu/dist/postfix/src/util/dict_pcre.c
+++ b/gnu/dist/postfix/src/util/dict_pcre.c
@@ -257,13 +257,7 @@ DICT *dict_pcre_open(const char *map, int unused_flags, int dict_flags)
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) {
msg_fatal("open %s: %m", map);
}
- while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) {
-
- if (*vstring_str(line_buffer) == '#') /* Skip comments */
- continue;
-
- if (*vstring_str(line_buffer) == 0) /* Skip blank lines */
- continue;
+ while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
trimblanks(p, 0)[0] = 0; /* Trim space at end */
diff --git a/gnu/dist/postfix/src/util/dict_regexp.c b/gnu/dist/postfix/src/util/dict_regexp.c
index 1debbe96ff2..732208ca67d 100644
--- a/gnu/dist/postfix/src/util/dict_regexp.c
+++ b/gnu/dist/postfix/src/util/dict_regexp.c
@@ -356,15 +356,9 @@ DICT *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) {
msg_fatal("open %s: %m", map);
}
- while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) {
+ while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
- if (*p == '#') /* Skip comments */
- continue;
-
- if (*p == 0) /* Skip blank lines */
- continue;
-
trimblanks(p, 0)[0] = 0; /* Trim space at end */
rule = dict_regexp_parseline(lineno, p, &nsub, map_fp);
diff --git a/gnu/dist/postfix/src/util/dict_static.c b/gnu/dist/postfix/src/util/dict_static.c
new file mode 100644
index 00000000000..1b06a89aba4
--- /dev/null
+++ b/gnu/dist/postfix/src/util/dict_static.c
@@ -0,0 +1,72 @@
+/*++
+/* NAME
+/* dict_static 3
+/* SUMMARY
+/* dictionary manager interface to static variables
+/* SYNOPSIS
+/* #include <dict_static.h>
+/*
+/* DICT *dict_static_open(name, dummy, dict_flags)
+/* const char *name;
+/* int dummy;
+/* int dict_flags;
+/* DESCRIPTION
+/* dict_static_open() implements a dummy dictionary that returns
+/* as lookup result the dictionary name, regardless of the lookup
+/* key value.
+/*
+/* The \fIdummy\fR argument is ignored.
+/* SEE ALSO
+/* dict(3) generic dictionary manager
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* jeffm
+/* ghostgun.com
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <stdio.h> /* sprintf() prototype */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include "mymalloc.h"
+#include "msg.h"
+#include "dict.h"
+#include "dict_static.h"
+
+/* dict_static_lookup - access static value*/
+
+static const char *dict_static_lookup(DICT *dict, const char *name)
+{
+ dict_errno = 0;
+
+ return (dict->name);
+}
+
+/* dict_static_close - close static dictionary */
+
+static void dict_static_close(DICT *dict)
+{
+ dict_free(dict);
+}
+
+/* dict_static_open - make association with static variable */
+
+DICT *dict_static_open(const char *name, int unused_flags, int dict_flags)
+{
+ DICT *dict;
+
+ dict = dict_alloc(DICT_TYPE_STATIC, name, sizeof(*dict));
+ dict->lookup = dict_static_lookup;
+ dict->close = dict_static_close;
+ dict->flags = dict_flags | DICT_FLAG_FIXED;
+ return (DICT_DEBUG (dict));
+}
diff --git a/gnu/dist/postfix/src/util/dict_static.h b/gnu/dist/postfix/src/util/dict_static.h
new file mode 100644
index 00000000000..d4ad1cc11d3
--- /dev/null
+++ b/gnu/dist/postfix/src/util/dict_static.h
@@ -0,0 +1,35 @@
+#ifndef _DICT_STATIC_H_INCLUDED_
+#define _DICT_STATIC_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_static 3h
+/* SUMMARY
+/* dictionary manager interface to static settings
+/* SYNOPSIS
+/* #include <dict_static.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * External interface.
+ */
+#define DICT_TYPE_STATIC "static"
+
+extern DICT *dict_static_open(const char *, int, int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* jeffm
+/* ghostgun.com
+/*--*/
+
+#endif
diff --git a/gnu/dist/postfix/src/util/dir_forest.c b/gnu/dist/postfix/src/util/dir_forest.c
index 289cd122e10..929f937c730 100644
--- a/gnu/dist/postfix/src/util/dir_forest.c
+++ b/gnu/dist/postfix/src/util/dir_forest.c
@@ -104,7 +104,7 @@ char *dir_forest(VSTRING *buf, const char *path, int depth)
}
VSTRING_TERMINATE(buf);
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %s -> %s", myname, path, vstring_str(buf));
return (vstring_str(buf));
}
diff --git a/gnu/dist/postfix/src/util/events.c b/gnu/dist/postfix/src/util/events.c
index c07504a26d6..99e1269565c 100644
--- a/gnu/dist/postfix/src/util/events.c
+++ b/gnu/dist/postfix/src/util/events.c
@@ -32,6 +32,8 @@
/*
/* void event_disable_readwrite(fd)
/* int fd;
+/*
+/* void event_drain()
/* DESCRIPTION
/* This module delivers I/O and timer events.
/* Multiple I/O streams and timers can be monitored simultaneously.
@@ -98,6 +100,10 @@
/* event_disable_readwrite() disables further I/O events on the specified
/* I/O channel. The application is allowed to cancel non-existing
/* I/O event requests.
+/*
+/* event_drain() repeatedly calls event_loop() until no more timer
+/* events or I/O events are pending. This routine must not be called
+/* from an event_whatever() callback routine.
/* DIAGNOSTICS
/* Panics: interface violations. Fatal errors: out of memory,
/* system call failure. Warnings: the number of available
@@ -242,6 +248,21 @@ time_t event_time(void)
return (event_present);
}
+/* event_drain - loop until all pending events are done */
+
+void event_drain(void)
+{
+ fd_set zero_mask;
+
+ if (EVENT_INIT_NEEDED())
+ return;
+
+ FD_ZERO(&zero_mask);
+ while (event_timer_head.pred != event_timer_head.succ
+ || memcmp(&zero_mask, &event_xmask, sizeof(zero_mask)) != 0)
+ event_loop(-1);
+}
+
/* event_enable_read - enable read events */
void event_enable_read(int fd, EVENT_NOTIFY_RDWR callback, char *context)
diff --git a/gnu/dist/postfix/src/util/events.h b/gnu/dist/postfix/src/util/events.h
index bc39b4787c4..d7235093b21 100644
--- a/gnu/dist/postfix/src/util/events.h
+++ b/gnu/dist/postfix/src/util/events.h
@@ -29,6 +29,7 @@ extern void event_disable_readwrite(int);
extern time_t event_request_timer(EVENT_NOTIFY_TIME, char *, int);
extern int event_cancel_timer(EVENT_NOTIFY_TIME, char *);
extern void event_loop(int);
+extern void event_drain(void);
/*
* Event codes.
diff --git a/gnu/dist/postfix/src/util/fifo_trigger.c b/gnu/dist/postfix/src/util/fifo_trigger.c
index b2801143610..841011a1bfc 100644
--- a/gnu/dist/postfix/src/util/fifo_trigger.c
+++ b/gnu/dist/postfix/src/util/fifo_trigger.c
@@ -13,7 +13,8 @@
/* int timeout;
/* DESCRIPTION
/* fifo_trigger() wakes up the named fifo server by writing
-/* the contents of the specified buffer to the fifo.
+/* the contents of the specified buffer to the fifo. There is
+/* no guarantee that the written data will actually be received.
/*
/* Arguments:
/* .IP service
@@ -26,7 +27,7 @@
/* Deadline in seconds. Specify a value <= 0 to disable
/* the time limit.
/* DIAGNOSTICS
-/* The result is zero in case of success, -1 in case of problems.
+/* The result is zero when the fifo could be opened, -1 otherwise.
/* BUGS
/* LICENSE
/* .ad
@@ -49,25 +50,37 @@
#include <msg.h>
#include <iostuff.h>
+#include <safe_open.h>
#include <trigger.h>
/* fifo_trigger - wakeup fifo server */
int fifo_trigger(const char *service, const char *buf, int len, int timeout)
{
+ static VSTRING *why;
char *myname = "fifo_trigger";
+ VSTREAM *fp;
int fd;
+ if (why == 0)
+ why = vstring_alloc(1);
+
/*
* Write the request to the service fifo. According to POSIX, the open
* shall always return immediately, and shall return an error when no
* process is reading from the FIFO.
+ *
+ * Use safe_open() so that we don't follow symlinks, and so that we don't
+ * open files with multiple hard links. We're not (yet) going to bother
+ * the caller with safe_open() specific quirks such as the why argument.
*/
- if ((fd = open(service, O_WRONLY | O_NONBLOCK, 0)) < 0) {
+ if ((fp = safe_open(service, O_WRONLY | O_NONBLOCK, 0,
+ (struct stat *) 0, -1, -1, why)) == 0) {
if (msg_verbose)
- msg_info("%s: open %s: %m", myname, service);
+ msg_info("%s: open %s: %s", myname, service, vstring_str(why));
return (-1);
}
+ fd = vstream_fileno(fp);
/*
* Write the request...
@@ -80,7 +93,7 @@ int fifo_trigger(const char *service, const char *buf, int len, int timeout)
/*
* Disconnect.
*/
- if (close(fd))
+ if (vstream_fclose(fp))
if (msg_verbose)
msg_warn("%s: close %s: %m", myname, service);
return (0);
diff --git a/gnu/dist/postfix/src/util/inet_trigger.c b/gnu/dist/postfix/src/util/inet_trigger.c
index e92c2d8dbfb..dddbcb62aac 100644
--- a/gnu/dist/postfix/src/util/inet_trigger.c
+++ b/gnu/dist/postfix/src/util/inet_trigger.c
@@ -16,6 +16,10 @@
/* a brief connection to it and by writing the contents of the
/* named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
@@ -45,6 +49,7 @@
/* System library. */
#include <sys_defs.h>
+#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
@@ -53,13 +58,42 @@
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct inet_trigger {
+ int fd;
+ char *service;
+};
+
+/* inet_trigger_event - disconnect from peer */
+
+static void inet_trigger_event(int event, char *context)
+{
+ struct inet_trigger *ip = (struct inet_trigger *) context;
+ static char *myname = "inet_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, ip->service);
+ event_disable_readwrite(ip->fd);
+ event_cancel_timer(inet_trigger_event, context);
+ if (close(ip->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, ip->service);
+ myfree(ip->service);
+ myfree((char *) ip);
+}
+
+
/* inet_trigger - wakeup INET-domain server */
int inet_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "inet_trigger";
+ struct inet_trigger *ip;
int fd;
if (msg_verbose > 1)
@@ -73,19 +107,24 @@ int inet_trigger(const char *service, const char *buf, int len, int timeout)
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
+ close_on_exec(fd, CLOSE_ON_EXEC);
+ ip = (struct inet_trigger *) mymalloc(sizeof(*ip));
+ ip->fd = fd;
+ ip->service = mystrdup(service);
/*
* Write the request...
*/
- if (write_buf(fd, buf, len, timeout) < 0)
+ if (write_buf(fd, buf, len, timeout) < 0
+ || write_buf(fd, "", 1, timeout) < 0)
if (msg_verbose)
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(inet_trigger_event, (char *) ip, timeout + 100);
+ event_enable_read(fd, inet_trigger_event, (char *) ip);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/intv.c b/gnu/dist/postfix/src/util/intv.c
new file mode 100644
index 00000000000..1d117fe0209
--- /dev/null
+++ b/gnu/dist/postfix/src/util/intv.c
@@ -0,0 +1,114 @@
+/*++
+/* NAME
+/* intv 3
+/* SUMMARY
+/* integer array utilities
+/* SYNOPSIS
+/* #include <intv.h>
+/*
+/* INTV *intv_alloc(len)
+/* int len;
+/*
+/* INTV *intv_free(intvp)
+/* INTV *intvp;
+/*
+/* void intv_add(intvp, count, arg, ...)
+/* INTV *intvp;
+/* int count;
+/* int *arg;
+/* DESCRIPTION
+/* The functions in this module manipulate arrays of integers.
+/* An INTV structure contains the following members:
+/* .IP len
+/* The actual length of the \fIintv\fR array.
+/* .IP intc
+/* The number of \fIintv\fR elements used.
+/* .IP intv
+/* An array of integer values.
+/* .PP
+/* intv_alloc() returns an empty integer array of the requested
+/* length. The result is ready for use by intv_add().
+/*
+/* intv_add() copies zero or more integers and adds them to the
+/* specified integer array.
+/*
+/* intv_free() releases storage for an integer array, and conveniently
+/* returns a null pointer.
+/* SEE ALSO
+/* msg(3) diagnostics interface
+/* DIAGNOSTICS
+/* Fatal errors: memory allocation problem.
+/* 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 libraries. */
+
+#include <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Application-specific. */
+
+#include "mymalloc.h"
+#include "msg.h"
+#include "intv.h"
+
+/* intv_free - destroy integer array */
+
+INTV *intv_free(INTV *intvp)
+{
+ myfree((char *) intvp->intv);
+ myfree((char *) intvp);
+ return (0);
+}
+
+/* intv_alloc - initialize integer array */
+
+INTV *intv_alloc(int len)
+{
+ INTV *intvp;
+
+ /*
+ * Sanity check.
+ */
+ if (len < 1)
+ msg_panic("intv_alloc: bad array length %d", len);
+
+ /*
+ * Initialize.
+ */
+ intvp = (INTV *) mymalloc(sizeof(*intvp));
+ intvp->len = len;
+ intvp->intv = (int *) mymalloc(intvp->len * sizeof(intvp->intv[0]));
+ intvp->intc = 0;
+ return (intvp);
+}
+
+/* intv_add - add integer to vector */
+
+void intv_add(INTV *intvp, int count,...)
+{
+ va_list ap;
+
+ /*
+ * Make sure that always intvp->intc < intvp->len.
+ */
+ va_start(ap, count);
+ while (count-- > 0) {
+ if (intvp->intc >= intvp->len) {
+ intvp->len *= 2;
+ intvp->intv = (int *) myrealloc((char *) intvp->intv,
+ intvp->len * sizeof(int));
+ }
+ intvp->intv[intvp->intc++] = va_arg(ap, int);
+ }
+ va_end(ap);
+}
diff --git a/gnu/dist/postfix/src/util/intv.h b/gnu/dist/postfix/src/util/intv.h
new file mode 100644
index 00000000000..d84c5b4b191
--- /dev/null
+++ b/gnu/dist/postfix/src/util/intv.h
@@ -0,0 +1,38 @@
+#ifndef _INTV_H_INCLUDED_
+#define _INTV_H_INCLUDED_
+
+/*++
+/* NAME
+/* intv 3h
+/* SUMMARY
+/* string array utilities
+/* SYNOPSIS
+/* #include "intv.h"
+ DESCRIPTION
+ .nf
+
+ /*
+ * External interface.
+ */
+typedef struct INTV {
+ int len; /* number of array elements */
+ int intc; /* array elements in use */
+ int *intv; /* integer array */
+} INTV;
+
+extern INTV *intv_alloc(int);
+extern void intv_add(INTV *, int,...);
+extern INTV *intv_free(INTV *);
+
+/* 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/gnu/dist/postfix/src/util/make_dirs.c b/gnu/dist/postfix/src/util/make_dirs.c
index b3efcddbf60..ad1b451de70 100644
--- a/gnu/dist/postfix/src/util/make_dirs.c
+++ b/gnu/dist/postfix/src/util/make_dirs.c
@@ -54,6 +54,7 @@ int make_dirs(const char *path, int perms)
int saved_ch;
struct stat st;
int ret;
+ mode_t saved_mode = 0;
/*
* Initialize. Make a copy of the path that we can safely clobber.
@@ -78,14 +79,31 @@ int make_dirs(const char *path, int perms)
ret = -1;
break;
}
+ saved_mode = st.st_mode;
} else {
if (errno != ENOENT)
break;
+
+ /*
+ * Create a new directory. Unfortunately, mkdir(2) has no
+ * equivalent of open(2)'s O_CREAT|O_EXCL safety net, so we must
+ * require that the parent directory is not world writable.
+ * Detecting a lost race condition after the fact is not
+ * sufficient, as an attacker could repeat the attack and add one
+ * directory level at a time.
+ */
+ if (saved_mode & S_IWOTH) {
+ msg_warn("refusing to mkdir %s: parent directory is writable by everyone",
+ saved_path);
+ errno = EPERM;
+ ret = -1;
+ break;
+ }
if ((ret = mkdir(saved_path, perms)) < 0) {
if (errno != EEXIST)
break;
/* Race condition? */
- if ((ret = stat(saved_path, &st)) < 0)
+ if ((ret = stat(saved_path, &st)) < 0)
break;
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
diff --git a/gnu/dist/postfix/src/util/match_list.h b/gnu/dist/postfix/src/util/match_list.h
index aba88b8f694..910ae713bf2 100644
--- a/gnu/dist/postfix/src/util/match_list.h
+++ b/gnu/dist/postfix/src/util/match_list.h
@@ -15,9 +15,9 @@
* External interface.
*/
typedef struct MATCH_LIST MATCH_LIST;
-typedef int (*MATCH_LIST_FN) (const char *, const char *);
+typedef int (*MATCH_LIST_FN) (int, const char *, const char *);
-extern MATCH_LIST *match_list_init(const char *, int,...);
+extern MATCH_LIST *match_list_init(int, const char *, int,...);
extern int match_list_match(MATCH_LIST *,...);
extern void match_list_free(MATCH_LIST *);
diff --git a/gnu/dist/postfix/src/util/match_ops.h b/gnu/dist/postfix/src/util/match_ops.h
index a938c04fb34..e147f7b50dd 100644
--- a/gnu/dist/postfix/src/util/match_ops.h
+++ b/gnu/dist/postfix/src/util/match_ops.h
@@ -13,9 +13,13 @@
/* External interface. */
-extern int match_string(const char *, const char *);
-extern int match_hostname(const char *, const char *);
-extern int match_hostaddr(const char *, const char *);
+#define MATCH_FLAG_NONE 0
+#define MATCH_FLAG_PARENT (1<<0)
+#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT)
+
+extern int match_string(int, const char *, const char *);
+extern int match_hostname(int, const char *, const char *);
+extern int match_hostaddr(int, const char *, const char *);
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/msg.c b/gnu/dist/postfix/src/util/msg.c
index 08379a90b86..683359b2b37 100644
--- a/gnu/dist/postfix/src/util/msg.c
+++ b/gnu/dist/postfix/src/util/msg.c
@@ -20,6 +20,10 @@
/* NORETURN msg_fatal(format, ...)
/* const char *format;
/*
+/* NORETURN msg_fatal_status(status, format, ...)
+/* int status;
+/* const char *format;
+/*
/* NORETURN msg_panic(format, ...)
/* const char *format;
/*
@@ -48,11 +52,14 @@
/* msg_fatal() reports an unrecoverable error and terminates the program
/* with a non-zero exit status.
/*
+/* msg_fatal_status() reports an unrecoverable error and terminates the
+/* program with the specified exit status.
+/*
/* msg_panic() reports an internal inconsistency, terminates the
/* program immediately (i.e. without calling the optional user-specified
/* cleanup routine), and forces a core dump when possible.
/*
-/* msg_cleanup() specifies a function that msg_fatal() should
+/* msg_cleanup() specifies a function that msg_fatal[_status]() should
/* invoke before terminating the program, and returns the
/* current function pointer. Specify a null argument to disable
/* this feature.
@@ -163,6 +170,23 @@ NORETURN msg_fatal(const char *fmt,...)
exit(1);
}
+/* msg_fatal_status - report error and terminate gracefully */
+
+NORETURN msg_fatal_status(int status, const char *fmt,...)
+{
+ va_list ap;
+
+ if (msg_exiting++ == 0) {
+ va_start(ap, fmt);
+ msg_vprintf(MSG_FATAL, fmt, ap);
+ va_end(ap);
+ if (msg_cleanup_fn)
+ msg_cleanup_fn();
+ }
+ sleep(1);
+ exit(status);
+}
+
/* msg_panic - report error and dump core */
NORETURN msg_panic(const char *fmt,...)
diff --git a/gnu/dist/postfix/src/util/msg.h b/gnu/dist/postfix/src/util/msg.h
index fae8a2327c9..42198a2a71b 100644
--- a/gnu/dist/postfix/src/util/msg.h
+++ b/gnu/dist/postfix/src/util/msg.h
@@ -22,6 +22,7 @@ extern void PRINTFLIKE(1, 2) msg_info(const char *,...);
extern void PRINTFLIKE(1, 2) msg_warn(const char *,...);
extern void PRINTFLIKE(1, 2) msg_error(const char *,...);
extern NORETURN PRINTFLIKE(1, 2) msg_fatal(const char *,...);
+extern NORETURN PRINTFLIKE(2, 3) msg_fatal_status(int, const char *,...);
extern NORETURN PRINTFLIKE(1, 2) msg_panic(const char *,...);
extern int msg_error_limit(int);
diff --git a/gnu/dist/postfix/src/util/mymalloc.c b/gnu/dist/postfix/src/util/mymalloc.c
index e20531c0605..61014a800e0 100644
--- a/gnu/dist/postfix/src/util/mymalloc.c
+++ b/gnu/dist/postfix/src/util/mymalloc.c
@@ -90,7 +90,10 @@
typedef struct MBLOCK {
int signature; /* set when block is active */
int length; /* user requested length */
- char payload[1]; /* actually a bunch of bytes */
+ union {
+ ALIGN_TYPE align;
+ char payload[1]; /* actually a bunch of bytes */
+ } u;
} MBLOCK;
#define SIGNATURE 0xdead
@@ -99,7 +102,7 @@ typedef struct MBLOCK {
#define CHECK_IN_PTR(ptr, real_ptr, len, fname) { \
if (ptr == 0) \
msg_panic("%s: null pointer input", fname); \
- real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, payload[0])); \
+ real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, u.payload[0])); \
if (real_ptr->signature != SIGNATURE) \
msg_panic("%s: corrupt or unallocated memory block", fname); \
real_ptr->signature = 0; \
@@ -110,10 +113,10 @@ typedef struct MBLOCK {
#define CHECK_OUT_PTR(ptr, real_ptr, len) { \
real_ptr->signature = SIGNATURE; \
real_ptr->length = len; \
- ptr = real_ptr->payload; \
+ ptr = real_ptr->u.payload; \
}
-#define SPACE_FOR(len) (offsetof(MBLOCK, payload[0]) + len)
+#define SPACE_FOR(len) (offsetof(MBLOCK, u.payload[0]) + len)
/* mymalloc - allocate memory or bust */
@@ -165,6 +168,8 @@ void myfree(char *ptr)
char *mystrdup(const char *str)
{
+ if (str == 0)
+ msg_panic("mystrdup: null pointer argument");
return (strcpy(mymalloc(strlen(str) + 1), str));
}
@@ -173,10 +178,12 @@ char *mystrdup(const char *str)
char *mystrndup(const char *str, int len)
{
char *result;
- int slen;
+ char *cp;
- if ((slen = strlen(str)) < len)
- len = slen;
+ if (str == 0)
+ msg_panic("mystrndup: null pointer argument");
+ if ((cp = memchr(str, 0, len)) != 0)
+ len = cp - str;
result = memcpy(mymalloc(len + 1), str, len);
result[len] = 0;
return (result);
@@ -186,5 +193,7 @@ char *mystrndup(const char *str, int len)
char *mymemdup(const char *ptr, int len)
{
+ if (ptr == 0)
+ msg_panic("mymemdup: null pointer argument");
return (memcpy(mymalloc(len), ptr, len));
}
diff --git a/gnu/dist/postfix/src/util/myrand.c b/gnu/dist/postfix/src/util/myrand.c
new file mode 100644
index 00000000000..39fcbfd9e47
--- /dev/null
+++ b/gnu/dist/postfix/src/util/myrand.c
@@ -0,0 +1,63 @@
+/*++
+/* NAME
+/* myrand 3
+/* SUMMARY
+/* rand wrapper
+/* SYNOPSIS
+/* #include <myrand.h>
+/*
+/* void mysrand(seed)
+/* int seed;
+/*
+/* int myrand()
+/* DESCRIPTION
+/* This module implements a wrapper for the portable, pseudo-random
+/* number generator. The wrapper adds automatic initialization.
+/*
+/* mysrand() performs initialization. This call may be skipped.
+/*
+/* myrand() returns a pseudo-random number in the range [0, RAND_MAX].
+/* If mysrand() was not called, it is invoked with the process ID
+/* ex-or-ed with the time of day in seconds.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* WARNING
+/* Do not use this code for generating unpredictable numbers.
+/* 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>
+#include <unistd.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <myrand.h>
+
+static int myrand_initdone = 0;
+
+/* mysrand - initialize */
+
+void mysrand(int seed)
+{
+ srand(seed);
+ myrand_initdone = 1;
+}
+
+/* myrand - pseudo-random number */
+
+int myrand(void)
+{
+ if (myrand_initdone == 0)
+ mysrand(getpid() ^ time((time_t *) 0));
+ return (rand());
+}
diff --git a/gnu/dist/postfix/src/util/myrand.h b/gnu/dist/postfix/src/util/myrand.h
new file mode 100644
index 00000000000..49c5abb8433
--- /dev/null
+++ b/gnu/dist/postfix/src/util/myrand.h
@@ -0,0 +1,31 @@
+#ifndef _MYRAND_H_INCLUDED_
+#define _MYRAND_H_INCLUDED_
+
+/*++
+/* NAME
+/* myrand 3h
+/* SUMMARY
+/* rand wrapper
+/* SYNOPSIS
+/* #include <myrand.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * External interface.
+ */
+extern void mysrand(int);
+extern int myrand(void);
+
+/* 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/gnu/dist/postfix/src/util/netstring.c b/gnu/dist/postfix/src/util/netstring.c
new file mode 100644
index 00000000000..3f3e73112ad
--- /dev/null
+++ b/gnu/dist/postfix/src/util/netstring.c
@@ -0,0 +1,351 @@
+/*++
+/* NAME
+/* netstring 3
+/* SUMMARY
+/* netstring stream I/O support
+/* SYNOPSIS
+/* #include <netstring.h>
+/*
+/* void netstring_setup(stream, timeout)
+/* VSTREAM *stream;
+/* int timeout;
+/*
+/* void netstring_except(stream, exception)
+/* VSTREAM *stream;
+/* int exception;
+/*
+/* VSTRING *netstring_get(stream, buf, limit)
+/* VSTREAM *stream;
+/* VSTRING *buf;
+/* int limit;
+/*
+/* void netstring_put(stream, data, len)
+/* VSTREAM *stream;
+/* const char *data;
+/* int len;
+/*
+/* void netstring_put_multi(stream, data, len, data, len, ..., 0)
+/* VSTREAM *stream;
+/* const char *data;
+/* int len;
+/*
+/* void NETSTRING_PUT_BUF(stream, buf)
+/* VSTREAM *stream;
+/* VSTRING *buf;
+/*
+/* void netstring_fflush(stream)
+/* VSTREAM *stream;
+/*
+/* VSTRING *netstring_memcpy(buf, data, len)
+/* VSTRING *buf;
+/* const char *data;
+/* int len;
+/*
+/* VSTRING *netstring_memcat(buf, data, len)
+/* VSTRING *buf;
+/* const char *src;
+/* int len;
+/* AUXILIARY ROUTINES
+/* int netstring_get_length(stream)
+/* VSTREAM *stream;
+/*
+/* VSTRING *netstring_get_data(stream, buf, len)
+/* VSTREAM *stream;
+/* VSTRING *buf;
+/* int len;
+/*
+/* void netstring_get_terminator(stream)
+/* VSTREAM *stream;
+/* DESCRIPTION
+/* This module reads and writes netstrings with error detection:
+/* timeouts, unexpected end-of-file, or format errors. Netstring
+/* is a data format designed by Daniel Bernstein.
+/*
+/* netstring_setup() arranges for a time limit on the netstring
+/* read and write operations described below.
+/* This routine alters the behavior of streams as follows:
+/* .IP \(bu
+/* The read/write timeout is set to the specified value.
+/* .IP \(bu
+/* The stream is configured to enable exception handling.
+/* .PP
+/* netstring_except() raises the specified exception on the
+/* named stream. See the DIAGNOSTICS section below.
+/*
+/* netstring_get() reads a netstring from the specified stream
+/* and extracts its content. The limit specifies a maximal size.
+/* Specify zero to disable the size limit. The result is not null
+/* terminated. The result value is the buf argument.
+/*
+/* netstring_put() encapsulates the specified string as a netstring
+/* and sends the result to the specified stream.
+/* The stream output buffer is not flushed.
+/*
+/* netstring_put_multi() encapsulates the content of multiple strings
+/* as one netstring and sends the result to the specified stream. The
+/* argument list must be terminated with a null data pointer.
+/* The stream output buffer is not flushed.
+/*
+/* NETSTRING_PUT_BUF() is a macro that provides a VSTRING-based
+/* wrapper for the netstring_put() routine.
+/*
+/* netstring_fflush() flushes the output buffer of the specified
+/* stream and handles any errors.
+/*
+/* netstring_memcpy() encapsulates the specified data as a netstring
+/* and copies the result over the specified buffer. The result
+/* value is the buffer.
+/*
+/* netstring_memcat() encapsulates the specified data as a netstring
+/* and appends the result to the specified buffer. The result
+/* value is the buffer.
+/*
+/* The following routines provide low-level access to a netstring
+/* stream.
+/*
+/* netstring_get_length() reads a length field from the specified
+/* stream, and absorbs the netstring length field terminator.
+/*
+/* netstring_get_data() reads the specified number of bytes from the
+/* specified stream into the specified buffer, and absorbs the
+/* netstring terminator. The result value is the buf argument.
+/*
+/* netstring_get_terminator() reads the netstring terminator from
+/* the specified stream.
+/* DIAGNOSTICS
+/* .fi
+/* .ad
+/* In case of error, a vstream_longjmp() call is performed to the
+/* context specified with vstream_setjmp().
+/* Error codes passed along with vstream_longjmp() are:
+/* .IP NETSTRING_ERR_EOF
+/* An I/O error happened, or the peer has disconnected unexpectedly.
+/* .IP NETSTRING_ERR_TIME
+/* The time limit specified to netstring_setup() was exceeded.
+/* .IP NETSTRING_ERR_FORMAT
+/* The input contains an unexpected character value.
+/* .IP NETSTRING_ERR_SIZE
+/* The input is larger than acceptable.
+/* BUGS
+/* The timeout deadline affects all I/O on the named stream, not
+/* just the I/O done on behalf of this module.
+/*
+/* The timeout deadline overwrites any previously set up state on
+/* the named stream.
+/*
+/* netstrings are not null terminated, which makes printing them
+/* a bit awkward.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* SEE ALSO
+/* http://cr.yp.to/proto/netstrings.txt, netstring definition
+/* 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 <stdarg.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <netstring.h>
+
+/* Application-specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* netstring_setup - initialize netstring stream */
+
+void netstring_setup(VSTREAM *stream, int timeout)
+{
+ vstream_control(stream,
+ VSTREAM_CTL_TIMEOUT, timeout,
+ VSTREAM_CTL_EXCEPT,
+ VSTREAM_CTL_END);
+}
+
+/* netstring_except - process netstring stream exception */
+
+void netstring_except(VSTREAM *stream, int exception)
+{
+ vstream_longjmp(stream, exception);
+}
+
+/* netstring_get_length - read netstring length + terminator */
+
+int netstring_get_length(VSTREAM *stream)
+{
+ char *myname = "netstring_get_length";
+ int len = 0;
+ int ch;
+
+ for (;;) {
+ switch (ch = VSTREAM_GETC(stream)) {
+ case VSTREAM_EOF:
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+ case ':':
+ if (msg_verbose > 1)
+ msg_info("%s: read netstring length %d", myname, len);
+ return (len);
+ default:
+ if (!ISDIGIT(ch))
+ netstring_except(stream, NETSTRING_ERR_FORMAT);
+ len = len * 10 + ch - '0';
+ break;
+ }
+ }
+}
+
+/* netstring_get_data - read netstring payload + terminator */
+
+VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, int len)
+{
+ char *myname = "netstring_get_data";
+
+ /*
+ * Allocate buffer space.
+ */
+ VSTRING_RESET(buf);
+ VSTRING_SPACE(buf, len);
+
+ /*
+ * Read the payload and absorb the terminator.
+ */
+ if (vstream_fread(stream, STR(buf), len) != len)
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+ if (msg_verbose > 1)
+ msg_info("%s: read netstring data %.*s",
+ myname, len < 30 ? len : 30, STR(buf));
+ netstring_get_terminator(stream);
+
+ /*
+ * Position the buffer.
+ */
+ VSTRING_AT_OFFSET(buf, len);
+ return (buf);
+}
+
+/* netstring_get_terminator - absorb netstring terminator */
+
+void netstring_get_terminator(VSTREAM *stream)
+{
+ if (VSTREAM_GETC(stream) != ',')
+ netstring_except(stream, NETSTRING_ERR_FORMAT);
+}
+
+/* netstring_get - read string from netstring stream */
+
+VSTRING *netstring_get(VSTREAM *stream, VSTRING *buf, int limit)
+{
+ int len;
+
+ len = netstring_get_length(stream);
+ if (limit && len > limit)
+ netstring_except(stream, NETSTRING_ERR_SIZE);
+ netstring_get_data(stream, buf, len);
+ return (buf);
+}
+
+/* netstring_put - send string as netstring */
+
+void netstring_put(VSTREAM *stream, const char *data, int len)
+{
+ char *myname = "netstring_put";
+
+ if (msg_verbose > 1)
+ msg_info("%s: write netstring len %d data %.*s",
+ myname, len, len < 30 ? len : 30, data);
+ vstream_fprintf(stream, "%d:", len);
+ vstream_fwrite(stream, data, len);
+ VSTREAM_PUTC(',', stream);
+}
+
+/* netstring_put_multi - send multiple strings as one netstring */
+
+void netstring_put_multi(VSTREAM *stream,...)
+{
+ char *myname = "netstring_put_multi";
+ int total;
+ char *data;
+ int data_len;
+ va_list ap;
+
+ /*
+ * Figure out the total result size.
+ */
+ va_start(ap, stream);
+ for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len)
+ if ((data_len = va_arg(ap, int)) < 0)
+ msg_panic("netstring_put_multi: bad data length %d", data_len);
+ va_end(ap);
+
+ /*
+ * Debugging support.
+ */
+ if (msg_verbose > 1) {
+ va_start(ap, stream);
+ data = va_arg(ap, char *);
+ data_len = va_arg(ap, int);
+ msg_info("%s: write netstring len %d data %.*s",
+ myname, total, data_len < 30 ? data_len : 30, data);
+ va_end(ap);
+ }
+
+ /*
+ * Send the length, content and terminator.
+ */
+ vstream_fprintf(stream, "%d:", total);
+ va_start(ap, stream);
+ while ((data = va_arg(ap, char *)) != 0) {
+ data_len = va_arg(ap, int);
+ if (data_len > 0)
+ if (vstream_fwrite(stream, data, data_len) != data_len)
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+ va_end(ap);
+ }
+ vstream_fwrite(stream, ",", 1);
+}
+
+/* netstring_fflush - flush netstring stream */
+
+void netstring_fflush(VSTREAM *stream)
+{
+ if (vstream_fflush(stream) == VSTREAM_EOF)
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+}
+
+/* netstring_memcpy - copy data as in-memory netstring */
+
+VSTRING *netstring_memcpy(VSTRING *buf, const char *src, int len)
+{
+ vstring_sprintf(buf, "%d:", len);
+ vstring_memcat(buf, src, len);
+ VSTRING_ADDCH(buf, ',');
+ return (buf);
+}
+
+/* netstring_memcat - append data as in-memory netstring */
+
+VSTRING *netstring_memcat(VSTRING *buf, const char *src, int len)
+{
+ vstring_sprintf_append(buf, "%d:", len);
+ vstring_memcat(buf, src, len);
+ VSTRING_ADDCH(buf, ',');
+ return (buf);
+}
diff --git a/gnu/dist/postfix/src/util/netstring.h b/gnu/dist/postfix/src/util/netstring.h
new file mode 100644
index 00000000000..6b82d90e4e8
--- /dev/null
+++ b/gnu/dist/postfix/src/util/netstring.h
@@ -0,0 +1,54 @@
+#ifndef _NETSTRING_H_INCLUDED_
+#define _NETSTRING_H_INCLUDED_
+
+/*++
+/* NAME
+/* netstring 3h
+/* SUMMARY
+/* netstring stream I/O support
+/* SYNOPSIS
+/* #include <netstring.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * External interface.
+ */
+#define NETSTRING_ERR_EOF 1 /* unexpected disconnect */
+#define NETSTRING_ERR_TIME 2 /* time out */
+#define NETSTRING_ERR_FORMAT 3 /* format error */
+#define NETSTRING_ERR_SIZE 4 /* netstring too large */
+
+extern void netstring_except(VSTREAM *, int);
+extern void netstring_setup(VSTREAM *, int);
+extern int netstring_get_length(VSTREAM *);
+extern VSTRING *netstring_get_data(VSTREAM *, VSTRING *, int);
+extern void netstring_get_terminator(VSTREAM *);
+extern VSTRING *netstring_get(VSTREAM *, VSTRING *, int);
+extern void netstring_put(VSTREAM *, const char *, int);
+extern void netstring_put_multi(VSTREAM *,...);
+extern void netstring_fflush(VSTREAM *);
+extern VSTRING *netstring_memcpy(VSTRING *, const char *, int);
+extern VSTRING *netstring_memcat(VSTRING *, const char *, int);
+
+#define NETSTRING_PUT_BUF(str, buf) \
+ netstring_put((str), vstring_str(buf), VSTRING_LEN(buf))
+
+/* 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/gnu/dist/postfix/src/util/rand_sleep.c b/gnu/dist/postfix/src/util/rand_sleep.c
index d119f074f57..33ffecb51c4 100644
--- a/gnu/dist/postfix/src/util/rand_sleep.c
+++ b/gnu/dist/postfix/src/util/rand_sleep.c
@@ -45,6 +45,7 @@
/* Utility library. */
#include <msg.h>
+#include <myrand.h>
#include <iostuff.h>
/* rand_sleep - block for random time */
@@ -52,7 +53,6 @@
void rand_sleep(unsigned delay, unsigned variation)
{
char *myname = "rand_sleep";
- static pid_t my_pid;
unsigned usec;
/*
@@ -66,9 +66,7 @@ void rand_sleep(unsigned delay, unsigned variation)
/*
* Use the semi-crappy random number generator.
*/
- if (my_pid == 0)
- srand((my_pid = getpid()) ^ time((time_t *) 0));
- usec = (delay - variation / 2) + variation * (double) rand() / RAND_MAX;
+ usec = (delay - variation / 2) + variation * (double) myrand() / RAND_MAX;
doze(usec);
}
diff --git a/gnu/dist/postfix/src/util/readlline.c b/gnu/dist/postfix/src/util/readlline.c
index 314d6962bf0..9be31eab109 100644
--- a/gnu/dist/postfix/src/util/readlline.c
+++ b/gnu/dist/postfix/src/util/readlline.c
@@ -6,30 +6,37 @@
/* SYNOPSIS
/* #include <readlline.h>
/*
-/* VSTRING *readlline(buf, fp, lineno, stripnl)
+/* VSTRING *readlline(buf, fp, lineno)
/* 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. 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 "blank lines and comments"
+/* Empty lines and whitespace-only lines are ignored, as
+/* are lines whose first non-whitespace character is a `#'.
+/* .IP "multi-line text"
+/* A logical line starts with non-whitespace text. A line that
+/* starts with whitespace continues a logical line.
+/* .PP
+/* The result value is the input buffer argument or a null pointer
+/* when no input is found.
/*
/* Arguments:
/* .IP buf
-/* A variable-length buffer for input.
+/* A variable-length buffer for input. The result is null terminated.
/* .IP fp
/* Handle to an open stream.
/* .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.
+/* after reading a newline character.
+/* .RE
+/* DIAGNOSTICS
+/* Warning: a continuation line that does not continue preceding text.
+/* The invalid input is ignored, to avoid complicating caller code.
+/* SECURITY
+/* readlline() imposes no logical line length limit therefore it
+/* should be used for reading trusted information only.
/* LICENSE
/* .ad
/* .fi
@@ -44,40 +51,73 @@
/* System library. */
#include <sys_defs.h>
+#include <ctype.h>
/* Utility library. */
+#include "msg.h"
#include "vstream.h"
#include "vstring.h"
#include "readlline.h"
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+#define END(x) vstring_end(x)
+
/* readlline - read one logical line */
-VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno, int stripnl)
+VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno)
{
int ch;
int next;
+ int start;
+ char *cp;
+
+ VSTRING_RESET(buf);
/*
- * Lines that start with whitespace continue the preceding line.
+ * Ignore comment lines, all whitespace lines, and empty lines. Terminate
+ * at EOF or at the beginning of the next logical line.
*/
- 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') {
- ch = next;
- } else {
- if (next != VSTREAM_EOF)
- vstream_ungetc(fp, next);
+ for (;;) {
+ /* Read one line, possibly not newline terminated. */
+ start = LEN(buf);
+ while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
+ VSTRING_ADDCH(buf, ch);
+ if (ch == '\n' && lineno != 0)
+ *lineno += 1;
+ /* Ignore comment line, all whitespace line, or empty line. */
+ for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++)
+ /* void */ ;
+ if (cp == END(buf) || *cp == '#')
+ vstring_truncate(buf, start);
+ /* Terminate at EOF or at the beginning of the next logical line. */
+ if (ch == VSTREAM_EOF)
+ break;
+ if (LEN(buf) > 0) {
+ if ((next = VSTREAM_GETC(fp)) != VSTREAM_EOF)
+ vstream_ungetc(fp, next);
+ if (next != '#' && !ISSPACE(next))
break;
- }
}
- VSTRING_ADDCH(buf, ch);
}
+
+ /*
+ * Invalid input: continuing text without preceding text. Allowing this
+ * would complicate "postconf -e", which implements its own multi-line
+ * parsing routine. Do not abort, just warn, so that critical programs
+ * like postmap do not leave behind a truncated table.
+ */
+ if (LEN(buf) > 0 && ISSPACE(*STR(buf))) {
+ msg_warn("%s: logical line must not start with whitespace: \"%.30s%s\"",
+ VSTREAM_PATH(fp), STR(buf),
+ LEN(buf) > 30 ? "..." : "");
+ return (readlline(buf, fp, lineno));
+ }
+
+ /*
+ * Done.
+ */
VSTRING_TERMINATE(buf);
- return (VSTRING_LEN(buf) || ch == '\n' ? buf : 0);
+ return (LEN(buf) > 0 ? buf : 0);
}
diff --git a/gnu/dist/postfix/src/util/readlline.h b/gnu/dist/postfix/src/util/readlline.h
index 00658433cc2..8a8bd548711 100644
--- a/gnu/dist/postfix/src/util/readlline.h
+++ b/gnu/dist/postfix/src/util/readlline.h
@@ -20,10 +20,7 @@
/*
* External interface.
*/
-extern VSTRING *readlline(VSTRING *, VSTREAM *, int *, int);
-
-#define READLL_STRIPNL 1
-#define READLL_KEEPNL 0
+extern VSTRING *readlline(VSTRING *, VSTREAM *, int *);
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/safe_open.c b/gnu/dist/postfix/src/util/safe_open.c
index e1e88f8b475..d20c9404c1a 100644
--- a/gnu/dist/postfix/src/util/safe_open.c
+++ b/gnu/dist/postfix/src/util/safe_open.c
@@ -41,8 +41,9 @@
/* Panic: interface violations.
/*
/* A null result means there was a problem. The nature of the
-/* problem is returned via the \fIwhy\fR buffer; some errors
-/* cannot be reported via \fIerrno\fR.
+/* problem is returned via the \fIwhy\fR buffer; when an error
+/* cannot be reported via \fIerrno\fR, the generic value EPERM
+/* (operation not permitted) is used instead.
/* HISTORY
/* .fi
/* .ad
@@ -54,7 +55,7 @@
/* be fooled by delaying the open() until the inode found with
/* lstat() has been re-used for a sensitive file (article
/* <20000103212443.A5807@monad.swb.de> posted to bugtraq on
-/* Jan 3, 2000). This can be a concern for a set-uid process
+/* Jan 3, 2000). This can be a concern for a set-ugid process
/* that runs under the control of a user and that can be
/* manipulated with start/stop signals.
/* LICENSE
@@ -113,8 +114,10 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
} else if (fstat_st->st_nlink != 1) {
vstring_sprintf(why, "file has %d hard links",
(int) fstat_st->st_nlink);
+ errno = EPERM;
} else if (S_ISDIR(fstat_st->st_mode)) {
vstring_sprintf(why, "file is a directory");
+ errno = EISDIR;
}
/*
@@ -135,10 +138,12 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
*/
else if (lstat(path, &lstat_st) < 0) {
vstring_sprintf(why, "file status changed unexpectedly: %m");
+ errno = EPERM;
} else if (S_ISLNK(lstat_st.st_mode)) {
if (lstat_st.st_uid == 0)
return (fp);
vstring_sprintf(why, "file is a symbolic link");
+ errno = EPERM;
} else if (fstat_st->st_dev != lstat_st.st_dev
|| fstat_st->st_ino != lstat_st.st_ino
#ifdef HAS_ST_GEN
@@ -147,6 +152,7 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
|| fstat_st->st_nlink != lstat_st.st_nlink
|| fstat_st->st_mode != lstat_st.st_mode) {
vstring_sprintf(why, "file status changed unexpectedly");
+ errno = EPERM;
}
/*
@@ -158,9 +164,7 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
/*
* End up here in case of fstat()/lstat() problems or inconsistencies.
- * Reset errno to reduce confusion.
*/
- errno = 0;
vstream_fclose(fp);
return (0);
}
@@ -190,7 +194,7 @@ static VSTREAM *safe_open_create(const char *path, int flags, int mode,
if (CHANGE_OWNER(user, group)
&& fchown(vstream_fileno(fp), user, group) < 0) {
- vstring_sprintf(why, "cannot change file ownership: %m");
+ msg_warn("%s: cannot change file ownership: %m", path);
}
/*
diff --git a/gnu/dist/postfix/src/util/sane_accept.c b/gnu/dist/postfix/src/util/sane_accept.c
index d9d8464d0ad..9ae9f6e3f49 100644
--- a/gnu/dist/postfix/src/util/sane_accept.c
+++ b/gnu/dist/postfix/src/util/sane_accept.c
@@ -36,6 +36,7 @@
/* Utility library. */
+#include "msg.h"
#include "sane_accept.h"
/* sane_accept - sanitize accept() error returns */
@@ -55,6 +56,10 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
EWOULDBLOCK,
0,
};
+ static int accept_warn_errors[] = {
+ ECONNABORTED,
+ 0,
+ };
int count;
int err;
int fd;
@@ -68,6 +73,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
* XXX LINUX < 2.1 accept() wakes up before the three-way handshake is
* complete, so it can fail with ECONNRESET and other "false alarm"
* indications.
+ *
+ * XXX FreeBSD 4.2-STABLE accept() returns ECONNABORTED when a UNIX-domain
+ * client has disconnected in the mean time. The data that was sent with
+ * connect() write() close() is lost, even though the write() and close()
+ * reported successful completion. This was fixed shortly before FreeBSD
+ * 4.3. However, other systems may make that same mistake again, so we're
+ * adding a special warning.
*/
if ((fd = accept(sock, sa, len)) < 0) {
for (count = 0; (err = accept_ok_errors[count]) != 0; count++) {
@@ -76,6 +88,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
break;
}
}
+ for (count = 0; (err = accept_warn_errors[count]) != 0; count++) {
+ if (errno == err) {
+ msg_warn("accept: %m");
+ errno = EAGAIN;
+ break;
+ }
+ }
}
return (fd);
}
diff --git a/gnu/dist/postfix/src/util/split_nameval.c b/gnu/dist/postfix/src/util/split_nameval.c
new file mode 100644
index 00000000000..f3e519bad1e
--- /dev/null
+++ b/gnu/dist/postfix/src/util/split_nameval.c
@@ -0,0 +1,94 @@
+/*++
+/* NAME
+/* split_nameval 3
+/* SUMMARY
+/* name-value splitter
+/* SYNOPSIS
+/* #include <split_nameval.h>
+/*
+/* const char *split_nameval(buf, name, value)
+/* char *buf;
+/* char **name;
+/* char **value;
+/* DESCRIPTION
+/* split_nameval() takes a logical line from readlline() and expects
+/* text of the form "name = value" or "name =". The buffer
+/* argument is broken up into name and value substrings.
+/*
+/* Arguments:
+/* .IP buf
+/* Result from readlline() or equivalent. The buffer is modified.
+/* .IP name
+/* Upon successful completion, this is set to the name
+/* substring.
+/* .IP value
+/* Upon successful completion, this is set to the value
+/* substring.
+/* FEATURES
+/* SEE ALSO
+/* dict(3) mid-level dictionary routines
+/* BUGS
+/* DIAGNOSTICS
+/* Fatal errors: out of memory.
+/*
+/* The result is a null pointer in case of success, a string
+/* describing the error otherwise: missing '=' after attribute
+/* name; missing attribute name.
+/* 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 libraries. */
+
+#include "sys_defs.h"
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+
+/* split_nameval - split text into name and value */
+
+const char *split_nameval(char *buf, char **name, char **value)
+{
+ char *np; /* name substring */
+ char *vp; /* value substring */
+ char *cp;
+ char *ep;
+
+ /*
+ * Ugly macros to make complex expressions less unreadable.
+ */
+#define SKIP(start, var, cond) \
+ for (var = start; *var && (cond); var++);
+
+#define TRIM(s) { \
+ char *p; \
+ for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \
+ *p = 0; \
+ }
+
+ SKIP(buf, np, ISSPACE(*np)); /* find name begin */
+ if (*np == 0)
+ return ("missing attribute name");
+ SKIP(np, ep, !ISSPACE(*ep) && *ep != '='); /* find name end */
+ SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */
+ if (*cp != '=') /* need '=' */
+ return ("missing '=' after attribute name");
+ *ep = 0; /* terminate name */
+ cp++; /* skip over '=' */
+ SKIP(cp, vp, ISSPACE(*vp)); /* skip leading blanks */
+ TRIM(vp); /* trim trailing blanks */
+ *name = np;
+ *value = vp;
+ return (0);
+}
diff --git a/gnu/dist/postfix/src/util/stream_trigger.c b/gnu/dist/postfix/src/util/stream_trigger.c
index 585f392bf6e..9cb57e5135e 100644
--- a/gnu/dist/postfix/src/util/stream_trigger.c
+++ b/gnu/dist/postfix/src/util/stream_trigger.c
@@ -15,6 +15,10 @@
/* stream_trigger() wakes up the named stream server by making
/* a brief connection to it and writing the named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
@@ -51,13 +55,41 @@
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct stream_trigger {
+ int fd;
+ char *service;
+};
+
+/* stream_trigger_event - disconnect from peer */
+
+static void stream_trigger_event(int event, char *context)
+{
+ struct stream_trigger *sp = (struct stream_trigger *) context;
+ static char *myname = "stream_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, sp->service);
+ event_disable_readwrite(sp->fd);
+ event_cancel_timer(stream_trigger_event, context);
+ if (close(sp->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, sp->service);
+ myfree(sp->service);
+ myfree((char *) sp);
+}
+
/* stream_trigger - wakeup stream server */
int stream_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "stream_trigger";
+ struct stream_trigger *sp;
int fd;
if (msg_verbose > 1)
@@ -71,19 +103,28 @@ int stream_trigger(const char *service, const char *buf, int len, int timeou
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
+ close_on_exec(fd, CLOSE_ON_EXEC);
+
+ /*
+ * Stash away context.
+ */
+ sp = (struct stream_trigger *) mymalloc(sizeof(*sp));
+ sp->fd = fd;
+ sp->service = mystrdup(service);
/*
* Write the request...
*/
- if (write_buf(fd, buf, len, timeout) < 0)
+ if (write_buf(fd, buf, len, timeout) < 0
+ || write_buf(fd, "", 1, timeout) < 0)
if (msg_verbose)
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(stream_trigger_event, (char *) sp, timeout + 100);
+ event_enable_read(fd, stream_trigger_event, (char *) sp);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/stringops.h b/gnu/dist/postfix/src/util/stringops.h
index 1310d4cb062..0cd80c797d1 100644
--- a/gnu/dist/postfix/src/util/stringops.h
+++ b/gnu/dist/postfix/src/util/stringops.h
@@ -30,6 +30,8 @@ extern char *translit(char *, const char *, const char *);
extern char *basename(const char *);
#endif
extern VSTRING *unescape(VSTRING *, const char *);
+extern int alldig(const char *);
+extern const char *split_nameval(char *, char **, char **);
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/unix_trigger.c b/gnu/dist/postfix/src/util/unix_trigger.c
index d0f03d6c78f..6a3b1916073 100644
--- a/gnu/dist/postfix/src/util/unix_trigger.c
+++ b/gnu/dist/postfix/src/util/unix_trigger.c
@@ -15,6 +15,10 @@
/* unix_trigger() wakes up the named UNIX-domain server by making
/* a brief connection to it and writing the named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
@@ -43,6 +47,7 @@
/* System library. */
#include <sys_defs.h>
+#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
@@ -51,13 +56,41 @@
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct unix_trigger {
+ int fd;
+ char *service;
+};
+
+/* unix_trigger_event - disconnect from peer */
+
+static void unix_trigger_event(int event, char *context)
+{
+ struct unix_trigger *up = (struct unix_trigger *) context;
+ static char *myname = "unix_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, up->service);
+ event_disable_readwrite(up->fd);
+ event_cancel_timer(unix_trigger_event, context);
+ if (close(up->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, up->service);
+ myfree(up->service);
+ myfree((char *) up);
+}
+
/* unix_trigger - wakeup UNIX-domain server */
int unix_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "unix_trigger";
+ struct unix_trigger *up;
int fd;
if (msg_verbose > 1)
@@ -71,19 +104,28 @@ int unix_trigger(const char *service, const char *buf, int len, int timeout)
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
+ close_on_exec(fd, CLOSE_ON_EXEC);
+
+ /*
+ * Stash away context.
+ */
+ up = (struct unix_trigger *) mymalloc(sizeof(*up));
+ up->fd = fd;
+ up->service = mystrdup(service);
/*
* Write the request...
*/
- if (write_buf(fd, buf, len, timeout) < 0)
+ if (write_buf(fd, buf, len, timeout) < 0
+ || write_buf(fd, "", 1, timeout) < 0)
if (msg_verbose)
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(unix_trigger_event, (char *) up, timeout + 100);
+ event_enable_read(fd, unix_trigger_event, (char *) up);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/vstream.c b/gnu/dist/postfix/src/util/vstream.c
index e2ec94ddcf7..22c59440ca7 100644
--- a/gnu/dist/postfix/src/util/vstream.c
+++ b/gnu/dist/postfix/src/util/vstream.c
@@ -72,6 +72,9 @@
/* int vstream_fileno(stream)
/* VSTREAM *stream;
/*
+/* void *vstream_context(stream)
+/* VSTREAM *stream;
+/*
/* int vstream_ferror(stream)
/* VSTREAM *stream;
/*
@@ -97,7 +100,7 @@
/* int vstream_setjmp(stream)
/* VSTREAM *stream;
/*
-/* void longjmp(stream, val)
+/* void vstream_longjmp(stream, val)
/* VSTREAM *stream;
/* int val;
/*
@@ -175,11 +178,11 @@
/*
/* VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT).
/*
-/* vstream_unget() pushes back a character onto the specified stream
+/* vstream_ungetc() pushes back a character onto the specified stream
/* and returns the character, or VSTREAM_EOF in case of problems.
/* It is an error to push back before reading (or immediately after
/* changing the stream offset via vstream_fseek()). Upon successful
-/* return, vstream_unget() clears the end-of-file stream flag.
+/* return, vstream_ungetc() clears the end-of-file stream flag.
/*
/* vstream_fputs() appends the given null-terminated string to the
/* specified buffered stream. The result is 0 in case of success,
@@ -250,6 +253,9 @@
/* a buffered stream. With streams that have separate read/write
/* file descriptors, the result is the current descriptor.
/*
+/* vstream_context() returns the application context that is passed on to
+/* the application-specified read/write routines.
+/*
/* VSTREAM_PATH() is an unsafe macro that returns the name stored
/* with vstream_fopen() or with vstream_control(). The macro is
/* unsafe because it evaluates some arguments more than once.
diff --git a/gnu/dist/postfix/src/util/vstream.h b/gnu/dist/postfix/src/util/vstream.h
index f38259b2701..3ec6382956b 100644
--- a/gnu/dist/postfix/src/util/vstream.h
+++ b/gnu/dist/postfix/src/util/vstream.h
@@ -90,6 +90,7 @@ extern VSTREAM *vstream_fdopen(int, int);
#define VSTREAM_GETCHAR() VSTREAM_GETC(VSTREAM_IN)
#define vstream_fileno(vp) ((vp)->fd)
+#define vstream_context(vp) ((vp)->context)
#define vstream_ferror(vp) vbuf_error(&(vp)->buf)
#define vstream_feof(vp) vbuf_eof(&(vp)->buf)
#define vstream_ftimeout(vp) vbuf_timeout(&(vp)->buf)
diff --git a/gnu/dist/postfix/src/util/vstring.c b/gnu/dist/postfix/src/util/vstring.c
index 356b114b2b8..0f3e33b5e40 100644
--- a/gnu/dist/postfix/src/util/vstring.c
+++ b/gnu/dist/postfix/src/util/vstring.c
@@ -67,6 +67,16 @@
/* const char *src;
/* int len;
/*
+/* VSTRING *vstring_memcpy(vp, src, len)
+/* VSTRING *vp;
+/* const char *src;
+/* int len;
+/*
+/* VSTRING *vstring_memcat(vp, src, len)
+/* VSTRING *vp;
+/* const char *src;
+/* int len;
+/*
/* VSTRING *vstring_sprintf(vp, format, ...)
/* VSTRING *vp;
/* const char *format;
@@ -101,14 +111,15 @@
/* of at least "len" bytes. The minimal length is 1. The result
/* is a null-terminated string of length zero.
/*
-/* vstring_ctl() gives control over memory management policy.
+/* vstring_ctl() gives additional control over vstring behavior.
/* The function takes a VSTRING pointer and a list of zero
-/* or more (name,value) pairs. The expected valye type of the
-/* value depends on the specified name. The name codes are:
+/* or more (name,value) pairs. The expected value type
+/* depends on the specified name. The value name codes are:
/* .IP "VSTRING_CTL_MAXLEN (int)"
/* Specifies a hard upper limit on a string's length. When the
/* length would be exceeded, the program simulates a memory
/* allocation problem (i.e. it terminates through msg_fatal()).
+/* This fuctionality is currently unimplemented.
/* .IP "VSTRING_CTL_END (no value)"
/* Specifies the end of the argument list. Forgetting to terminate
/* the argument list may cause the program to crash.
@@ -174,6 +185,14 @@
/* vstring_strncat() copies at most \fIlen\fR characters. Otherwise it is
/* identical to vstring_strcat().
/*
+/* vstring_memcpy() copies \fIlen\fR bytes to a variable-length string.
+/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the
+/* target and result value. The result is not null-terminated.
+/*
+/* vstring_memcat() appends \fIlen\fR bytes to a variable-length string.
+/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the
+/* target and result value. The result is not null-terminated.
+/*
/* vstring_sprintf() produces a formatted string according to its
/* \fIformat\fR argument. See vstring_vsprintf() for details.
/*
@@ -392,6 +411,29 @@ VSTRING *vstring_strncat(VSTRING *vp, const char *src, int len)
return (vp);
}
+/* vstring_memcpy - copy buffer of limited length */
+
+VSTRING *vstring_memcpy(VSTRING *vp, const char *src, int len)
+{
+ VSTRING_RESET(vp);
+
+ VSTRING_SPACE(vp, len);
+ memcpy(vstring_str(vp), src, len);
+ VSTRING_AT_OFFSET(vp, len);
+ return (vp);
+}
+
+/* vstring_memcat - append buffer of limited length */
+
+VSTRING *vstring_memcat(VSTRING *vp, const char *src, int len)
+{
+ VSTRING_SPACE(vp, len);
+ memcpy(vstring_end(vp), src, len);
+ len += VSTRING_LEN(vp);
+ VSTRING_AT_OFFSET(vp, len);
+ return (vp);
+}
+
/* vstring_export - VSTRING to bare string */
char *vstring_export(VSTRING *vp)
diff --git a/gnu/dist/postfix/src/util/vstring.h b/gnu/dist/postfix/src/util/vstring.h
index 6ae8947e784..b1b81bb8acd 100644
--- a/gnu/dist/postfix/src/util/vstring.h
+++ b/gnu/dist/postfix/src/util/vstring.h
@@ -38,6 +38,8 @@ extern VSTRING *vstring_strcpy(VSTRING *, const char *);
extern VSTRING *vstring_strncpy(VSTRING *, const char *, int);
extern VSTRING *vstring_strcat(VSTRING *, const char *);
extern VSTRING *vstring_strncat(VSTRING *, const char *, int);
+extern VSTRING *vstring_memcpy(VSTRING *, const char *, int);
+extern VSTRING *vstring_memcat(VSTRING *, const char *, int);
extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf(VSTRING *, const char *,...);
extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf_append(VSTRING *, const char *,...);
extern char *vstring_export(VSTRING *);
diff --git a/gnu/dist/postfix/src/util/vstring_vstream.c b/gnu/dist/postfix/src/util/vstring_vstream.c
index 078d255e1ac..2f644bf1dc7 100644
--- a/gnu/dist/postfix/src/util/vstring_vstream.c
+++ b/gnu/dist/postfix/src/util/vstring_vstream.c
@@ -27,6 +27,11 @@
/* VSTRING *vp;
/* VSTREAM *fp;
/* int bound;
+/*
+/* int vstring_get_null_bound(vp, fp, bound)
+/* VSTRING *vp;
+/* VSTREAM *fp;
+/* int bound;
/* DESCRIPTION
/* The routines in this module each read one newline or null-terminated
/* string from an input stream. In all cases the result is either the
@@ -41,7 +46,7 @@
/* vstring_get_null() reads a null-terminated string from the named
/* stream.
/*
-/* vstring_get_bound() and vstring_get_nonl_bound() read no more
+/* the vstring_get<whatever>_bound() routines read no more
/* than \fIbound\fR characters. Otherwise they behave like the
/* unbounded versions documented above.
/* DIAGNOSTICS
@@ -154,6 +159,22 @@ int vstring_get_nonl_bound(VSTRING *vp, VSTREAM *fp, int bound)
return (c == '\n' ? c : VSTRING_GET_RESULT(vp));
}
+/* vstring_get_null_bound - read null-terminated string from file */
+
+int vstring_get_null_bound(VSTRING *vp, VSTREAM *fp, int bound)
+{
+ int c;
+
+ if (bound <= 0)
+ msg_panic("vstring_get_nonl_bound: invalid bound %d", bound);
+
+ VSTRING_RESET(vp);
+ while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
+ VSTRING_ADDCH(vp, c);
+ VSTRING_TERMINATE(vp);
+ return (c == 0 ? c : VSTRING_GET_RESULT(vp));
+}
+
#ifdef TEST
/*
diff --git a/gnu/dist/postfix/src/util/vstring_vstream.h b/gnu/dist/postfix/src/util/vstring_vstream.h
index 897167ca640..68bd8106db0 100644
--- a/gnu/dist/postfix/src/util/vstring_vstream.h
+++ b/gnu/dist/postfix/src/util/vstring_vstream.h
@@ -24,6 +24,7 @@ extern int vstring_get_nonl(VSTRING *, VSTREAM *);
extern int vstring_get_null(VSTRING *, VSTREAM *);
extern int vstring_get_bound(VSTRING *, VSTREAM *, int);
extern int vstring_get_nonl_bound(VSTRING *, VSTREAM *, int);
+extern int vstring_get_null_bound(VSTRING *, VSTREAM *, int);
/*
* Backwards compatibility for code that still uses the vstring_fgets()
diff --git a/gnu/dist/postfix/src/virtual/.printfck b/gnu/dist/postfix/src/virtual/.printfck
new file mode 100644
index 00000000000..66016ed453c
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/.printfck
@@ -0,0 +1,25 @@
+been_here_xt 2 0
+bounce_append 5 0
+cleanup_out_format 1 0
+defer_append 5 0
+mail_command 1 0
+mail_print 1 0
+msg_error 0 0
+msg_fatal 0 0
+msg_info 0 0
+msg_panic 0 0
+msg_warn 0 0
+opened 4 0
+post_mail_fprintf 1 0
+qmgr_message_bounce 2 0
+rec_fprintf 2 0
+sent 4 0
+smtp_cmd 1 0
+smtp_mesg_fail 2 0
+smtp_printf 1 0
+smtp_rcpt_fail 3 0
+smtp_site_fail 2 0
+udp_syslog 1 0
+vstream_fprintf 1 0
+vstream_printf 0 0
+vstring_sprintf 1 0
diff --git a/gnu/dist/postfix/src/virtual/Makefile.in b/gnu/dist/postfix/src/virtual/Makefile.in
new file mode 100644
index 00000000000..8bf523ed3f8
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/Makefile.in
@@ -0,0 +1,166 @@
+SHELL = /bin/sh
+SRCS = virtual.c mailbox.c recipient.c deliver_attr.c maildir.c unknown.c
+OBJS = virtual.o mailbox.o recipient.o deliver_attr.o maildir.o unknown.o
+HDRS = virtual.h
+TESTSRC =
+WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
+ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
+ -Wunused
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) -I..
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+PROG = virtual
+TESTPROG=
+INC_DIR = ../../include
+LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a $(AUXLIBS)
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+Makefile: Makefile.in
+ (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs; cat $?) >$@
+
+test: $(TESTPROG)
+
+update: ../../libexec/$(PROG)
+
+../../libexec/$(PROG): $(PROG)
+ cp $(PROG) ../../libexec
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ cp *.h printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
+ done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @make -f Makefile.in Makefile
+
+# do not edit below this line - it is generated by 'make depend'
+deliver_attr.o: deliver_attr.c
+deliver_attr.o: ../../include/sys_defs.h
+deliver_attr.o: ../../include/msg.h
+deliver_attr.o: ../../include/vstream.h
+deliver_attr.o: ../../include/vbuf.h
+deliver_attr.o: virtual.h
+deliver_attr.o: ../../include/vstring.h
+deliver_attr.o: ../../include/deliver_request.h
+deliver_attr.o: ../../include/recipient_list.h
+deliver_attr.o: ../../include/maps.h
+deliver_attr.o: ../../include/dict.h
+deliver_attr.o: ../../include/argv.h
+deliver_attr.o: ../../include/mbox_conf.h
+mailbox.o: mailbox.c
+mailbox.o: ../../include/sys_defs.h
+mailbox.o: ../../include/msg.h
+mailbox.o: ../../include/vstring.h
+mailbox.o: ../../include/vbuf.h
+mailbox.o: ../../include/vstream.h
+mailbox.o: ../../include/mymalloc.h
+mailbox.o: ../../include/stringops.h
+mailbox.o: ../../include/set_eugid.h
+mailbox.o: ../../include/mail_copy.h
+mailbox.o: ../../include/mbox_open.h
+mailbox.o: ../../include/safe_open.h
+mailbox.o: ../../include/defer.h
+mailbox.o: ../../include/bounce.h
+mailbox.o: ../../include/sent.h
+mailbox.o: ../../include/mail_params.h
+mailbox.o: virtual.h
+mailbox.o: ../../include/deliver_request.h
+mailbox.o: ../../include/recipient_list.h
+mailbox.o: ../../include/maps.h
+mailbox.o: ../../include/dict.h
+mailbox.o: ../../include/argv.h
+mailbox.o: ../../include/mbox_conf.h
+maildir.o: maildir.c
+maildir.o: ../../include/sys_defs.h
+maildir.o: ../../include/msg.h
+maildir.o: ../../include/mymalloc.h
+maildir.o: ../../include/stringops.h
+maildir.o: ../../include/vstring.h
+maildir.o: ../../include/vbuf.h
+maildir.o: ../../include/vstream.h
+maildir.o: ../../include/make_dirs.h
+maildir.o: ../../include/set_eugid.h
+maildir.o: ../../include/get_hostname.h
+maildir.o: ../../include/sane_fsops.h
+maildir.o: ../../include/mail_copy.h
+maildir.o: ../../include/bounce.h
+maildir.o: ../../include/defer.h
+maildir.o: ../../include/sent.h
+maildir.o: ../../include/mail_params.h
+maildir.o: virtual.h
+maildir.o: ../../include/deliver_request.h
+maildir.o: ../../include/recipient_list.h
+maildir.o: ../../include/maps.h
+maildir.o: ../../include/dict.h
+maildir.o: ../../include/argv.h
+maildir.o: ../../include/mbox_conf.h
+recipient.o: recipient.c
+recipient.o: ../../include/sys_defs.h
+recipient.o: ../../include/msg.h
+recipient.o: ../../include/mymalloc.h
+recipient.o: ../../include/stringops.h
+recipient.o: ../../include/vstring.h
+recipient.o: ../../include/vbuf.h
+recipient.o: ../../include/bounce.h
+recipient.o: virtual.h
+recipient.o: ../../include/vstream.h
+recipient.o: ../../include/deliver_request.h
+recipient.o: ../../include/recipient_list.h
+recipient.o: ../../include/maps.h
+recipient.o: ../../include/dict.h
+recipient.o: ../../include/argv.h
+recipient.o: ../../include/mbox_conf.h
+unknown.o: unknown.c
+unknown.o: ../../include/sys_defs.h
+unknown.o: ../../include/msg.h
+unknown.o: ../../include/bounce.h
+unknown.o: virtual.h
+unknown.o: ../../include/vstream.h
+unknown.o: ../../include/vbuf.h
+unknown.o: ../../include/vstring.h
+unknown.o: ../../include/deliver_request.h
+unknown.o: ../../include/recipient_list.h
+unknown.o: ../../include/maps.h
+unknown.o: ../../include/dict.h
+unknown.o: ../../include/argv.h
+unknown.o: ../../include/mbox_conf.h
+virtual.o: virtual.c
+virtual.o: ../../include/sys_defs.h
+virtual.o: ../../include/msg.h
+virtual.o: ../../include/vstring.h
+virtual.o: ../../include/vbuf.h
+virtual.o: ../../include/vstream.h
+virtual.o: ../../include/iostuff.h
+virtual.o: ../../include/set_eugid.h
+virtual.o: ../../include/dict.h
+virtual.o: ../../include/argv.h
+virtual.o: ../../include/mail_queue.h
+virtual.o: ../../include/recipient_list.h
+virtual.o: ../../include/deliver_request.h
+virtual.o: ../../include/deliver_completed.h
+virtual.o: ../../include/mail_params.h
+virtual.o: ../../include/mail_conf.h
+virtual.o: ../../include/mail_server.h
+virtual.o: virtual.h
+virtual.o: ../../include/maps.h
+virtual.o: ../../include/mbox_conf.h
diff --git a/gnu/dist/postfix/src/virtual/deliver_attr.c b/gnu/dist/postfix/src/virtual/deliver_attr.c
new file mode 100644
index 00000000000..63b15580fcf
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/deliver_attr.c
@@ -0,0 +1,74 @@
+/*++
+/* NAME
+/* deliver_attr 3
+/* SUMMARY
+/* initialize message delivery attributes
+/* SYNOPSIS
+/* #include "virtual.h"
+/*
+/* void deliver_attr_init(attrp)
+/* DELIVER_ATTR *attrp;
+/*
+/* void deliver_attr_dump(attrp)
+/* DELIVER_ATTR *attrp;
+/* DESCRIPTION
+/* deliver_attr_init() initializes a structure with message delivery
+/* attributes to a known initial state (all zeros).
+/*
+/* deliver_attr_dump() logs the contents of the given attribute list.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* Application-specific. */
+
+#include "virtual.h"
+
+/* deliver_attr_init - set message delivery attributes to all-zero state */
+
+void deliver_attr_init(DELIVER_ATTR *attrp)
+{
+ attrp->level = 0;
+ attrp->fp = 0;
+ attrp->queue_name = 0;
+ attrp->queue_id = 0;
+ attrp->offset = 0;
+ attrp->sender = 0;
+ attrp->recipient = 0;
+ attrp->user = 0;
+ attrp->delivered = 0;
+ attrp->relay = 0;
+}
+
+/* deliver_attr_dump - log message delivery attributes */
+
+void deliver_attr_dump(DELIVER_ATTR *attrp)
+{
+ msg_info("level: %d", attrp->level);
+ msg_info("path: %s", VSTREAM_PATH(attrp->fp));
+ msg_info("fp: 0x%lx", (long) attrp->fp);
+ msg_info("queue_name: %s", attrp->queue_name ? attrp->queue_name : "null");
+ msg_info("queue_id: %s", attrp->queue_id ? attrp->queue_id : "null");
+ msg_info("offset: %ld", attrp->offset);
+ msg_info("sender: %s", attrp->sender ? attrp->sender : "null");
+ msg_info("recipient: %s", attrp->recipient ? attrp->recipient : "null");
+ msg_info("user: %s", attrp->user ? attrp->user : "null");
+ msg_info("delivered: %s", attrp->delivered ? attrp->delivered : "null");
+ msg_info("relay: %s", attrp->relay ? attrp->relay : "null");
+}
diff --git a/gnu/dist/postfix/src/virtual/mailbox.c b/gnu/dist/postfix/src/virtual/mailbox.c
new file mode 100644
index 00000000000..21782404b62
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/mailbox.c
@@ -0,0 +1,246 @@
+/*++
+/* NAME
+/* mailbox 3
+/* SUMMARY
+/* mailbox delivery
+/* SYNOPSIS
+/* #include "virtual.h"
+/*
+/* int deliver_mailbox(state, usr_attr, statusp)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* int *statusp;
+/* DESCRIPTION
+/* deliver_mailbox() delivers to UNIX-style mailbox or to maildir.
+/*
+/* A zero result means that the named user was not found.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* .IP usr_attr
+/* Attributes describing user rights and mailbox location.
+/* .IP statusp
+/* Delivery status: see below.
+/* DIAGNOSTICS
+/* The message delivery status is non-zero when delivery should be tried
+/* again.
+/* 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 <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <set_eugid.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <mbox_open.h>
+#include <defer.h>
+#include <sent.h>
+#include <mail_params.h>
+
+#ifndef EDQUOT
+#define EDQUOT EFBIG
+#endif
+
+/* Application-specific. */
+
+#include "virtual.h"
+
+#define YES 1
+#define NO 0
+
+/* deliver_mailbox_file - deliver to recipient mailbox */
+
+static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ char *myname = "deliver_mailbox_file";
+ VSTRING *why;
+ MBOX *mp;
+ int mail_copy_status;
+ int deliver_status;
+ int copy_flags;
+ long end;
+ struct stat st;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Initialize. Assume the operation will fail. Set the delivered
+ * attribute to reflect the final recipient.
+ */
+ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
+ msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
+ state.msg_attr.delivered = state.msg_attr.recipient;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ why = vstring_alloc(100);
+
+ /*
+ * Lock the mailbox and open/create the mailbox file.
+ *
+ * Write the file as the recipient, so that file quota work.
+ */
+ copy_flags = MAIL_COPY_MBOX;
+
+ set_eugid(usr_attr.uid, usr_attr.gid);
+ mp = mbox_open(usr_attr.mailbox, O_APPEND | O_WRONLY | O_CREAT,
+ S_IRUSR | S_IWUSR, &st, -1, -1,
+ virtual_mbox_lock_mask, why);
+ if (mp != 0) {
+ if (S_ISREG(st.st_mode) == 0) {
+ vstream_fclose(mp->fp);
+ vstring_sprintf(why, "destination is not a regular file");
+ errno = 0;
+ } else {
+ end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
+ mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
+ copy_flags, "\n", why);
+ }
+ mbox_release(mp);
+ }
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * As the mail system, bounce, defer delivery, or report success.
+ */
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ deliver_status = (errno == EDQUOT || errno == EFBIG ?
+ bounce_append : defer_append)
+ (BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "mailbox %s: %s", usr_attr.mailbox, vstring_str(why));
+ } else {
+ deliver_status = sent(SENT_ATTR(state.msg_attr), "mailbox");
+ }
+ vstring_free(why);
+ return (deliver_status);
+}
+
+/* deliver_mailbox - deliver to recipient mailbox */
+
+int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
+{
+ char *myname = "deliver_mailbox";
+ const char *mailbox_res;
+ const char *uid_res;
+ const char *gid_res;
+ long n;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Sanity check.
+ */
+ if (*var_virt_mailbox_base != '/')
+ msg_fatal("do not specify relative pathname: %s = %s",
+ VAR_VIRT_MAILBOX_BASE, var_virt_mailbox_base);
+
+ /*
+ * Look up the mailbox location. Bounce if not found, defer in case of
+ * trouble.
+ */
+ mailbox_res = maps_find(virtual_mailbox_maps, state.msg_attr.user,
+ DICT_FLAG_FIXED);
+ if (mailbox_res == 0) {
+ if (dict_errno == 0)
+ return (NO);
+
+ *statusp = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "%s: lookup %s: %m",
+ virtual_mailbox_maps->title, state.msg_attr.user);
+ return (YES);
+ }
+ usr_attr.mailbox = concatenate(var_virt_mailbox_base, "/",
+ mailbox_res, (char *) 0);
+
+#define RETURN(res) { myfree(usr_attr.mailbox); return (res); }
+
+ /*
+ * Look up the mailbox owner rights. Defer in case of trouble.
+ */
+ if ((uid_res = maps_find(virtual_uid_maps, state.msg_attr.user,
+ DICT_FLAG_FIXED)) == 0) {
+ *statusp = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "recipient %s: uid not found in %s",
+ state.msg_attr.user, virtual_uid_maps->title);
+ RETURN(YES);
+ }
+ if ((n = atol(uid_res)) < var_virt_minimum_uid) {
+ *statusp = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "recipient %s: bad uid %s in %s",
+ state.msg_attr.user, uid_res, virtual_uid_maps->title);
+ RETURN(YES);
+ }
+ usr_attr.uid = (uid_t) n;
+
+ /*
+ * Look up the mailbox group rights. Defer in case of trouble.
+ */
+ if ((gid_res = maps_find(virtual_gid_maps, state.msg_attr.user,
+ DICT_FLAG_FIXED)) == 0) {
+ *statusp = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "recipient %s: gid not found in %s",
+ state.msg_attr.user, virtual_gid_maps->title);
+ RETURN(YES);
+ }
+ if ((n = atol(gid_res)) <= 0) {
+ *statusp = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "recipient %s: bad gid %s in %s",
+ state.msg_attr.user, gid_res, virtual_gid_maps->title);
+ RETURN(YES);
+ }
+ usr_attr.gid = (gid_t) n;
+
+ if (msg_verbose)
+ msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
+ myname, state.level, usr_attr.mailbox,
+ (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
+
+ /*
+ * Deliver to mailbox or to external command.
+ */
+#define LAST_CHAR(s) (s[strlen(s) - 1])
+
+ if (LAST_CHAR(usr_attr.mailbox) == '/')
+ *statusp = deliver_maildir(state, usr_attr);
+ else
+ *statusp = deliver_mailbox_file(state, usr_attr);
+
+ /*
+ * Cleanup.
+ */
+ RETURN(YES);
+}
diff --git a/gnu/dist/postfix/src/virtual/maildir.c b/gnu/dist/postfix/src/virtual/maildir.c
new file mode 100644
index 00000000000..2db4130020a
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/maildir.c
@@ -0,0 +1,176 @@
+/*++
+/* NAME
+/* maildir 3
+/* SUMMARY
+/* delivery to maildir
+/* SYNOPSIS
+/* #include "virtual.h"
+/*
+/* int deliver_maildir(state, usr_attr)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* DESCRIPTION
+/* deliver_maildir() delivers a message to a qmail-style maildir.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* .IP usr_attr
+/* Attributes describing user rights and environment information.
+/* DIAGNOSTICS
+/* deliver_maildir() always succeeds or it bounces the message.
+/* SEE ALSO
+/* bounce(3)
+/* 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 <errno.h>
+
+#ifndef EDQUOT
+#define EDQUOT EFBIG
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <make_dirs.h>
+#include <set_eugid.h>
+#include <get_hostname.h>
+#include <sane_fsops.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <bounce.h>
+#include <defer.h>
+#include <sent.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include "virtual.h"
+
+/* deliver_maildir - delivery to maildir-style mailbox */
+
+int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ char *myname = "deliver_maildir";
+ char *newdir;
+ char *tmpdir;
+ char *curdir;
+ char *tmpfile;
+ char *newfile;
+ VSTRING *why;
+ VSTRING *buf;
+ VSTREAM *dst;
+ int mail_copy_status;
+ int deliver_status;
+ int copy_flags;
+ static int count;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Initialize. Assume the operation will fail. Set the delivered
+ * attribute to reflect the final recipient.
+ */
+ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
+ msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
+ state.msg_attr.delivered = state.msg_attr.recipient;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ buf = vstring_alloc(100);
+ why = vstring_alloc(100);
+
+ copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_DELIVERED;
+
+ newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0);
+ tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0);
+ curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0);
+
+ /*
+ * Create and write the file as the recipient, so that file quota work.
+ * Create any missing directories on the fly. The file name is chosen
+ * according to ftp://koobera.math.uic.edu/www/proto/maildir.html:
+ *
+ * "A unique name has three pieces, separated by dots. On the left is the
+ * result of time(). On the right is the result of gethostname(). In the
+ * middle is something that doesn't repeat within one second on a single
+ * host. I fork a new process for each delivery, so I just use the
+ * process ID. If you're delivering several messages from one process,
+ * use starttime.pid_count.host, where starttime is the time that your
+ * process started, and count is the number of messages you've
+ * delivered."
+ */
+#define STR vstring_str
+
+ set_eugid(usr_attr.uid, usr_attr.gid);
+ vstring_sprintf(buf, "%ld.%d_%d.%s", (long) var_starttime,
+ var_pid, count++, get_hostname());
+ tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
+ newfile = concatenate(newdir, STR(buf), (char *) 0);
+ if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
+ && (errno != ENOENT
+ || make_dirs(tmpdir, 0700) < 0
+ || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
+ vstring_sprintf(why, "create %s: %m", tmpfile);
+ } else {
+ if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
+ dst, copy_flags, "\n", why)) == 0) {
+ if (sane_link(tmpfile, newfile) < 0
+ && (errno != ENOENT
+ || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
+ || sane_link(tmpfile, newfile) < 0)) {
+ vstring_sprintf(why, "link to %s: %m", newfile);
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+ }
+ }
+ if (unlink(tmpfile) < 0)
+ msg_warn("remove %s: %m", tmpfile);
+ }
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * The maildir location is controlled by the mail administrator. If
+ * delivery fails, try again later. We would just bounce when the maildir
+ * location possibly under user control.
+ */
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ deliver_status = (errno == EDQUOT || errno == EFBIG ?
+ bounce_append : defer_append)
+ (BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "maildir delivery failed: %s", vstring_str(why));
+
+ } else {
+ deliver_status = sent(SENT_ATTR(state.msg_attr), "maildir");
+ }
+ vstring_free(buf);
+ vstring_free(why);
+ myfree(newdir);
+ myfree(tmpdir);
+ myfree(curdir);
+ myfree(tmpfile);
+ myfree(newfile);
+ return (deliver_status);
+}
diff --git a/gnu/dist/postfix/src/virtual/recipient.c b/gnu/dist/postfix/src/virtual/recipient.c
new file mode 100644
index 00000000000..725108f5be7
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/recipient.c
@@ -0,0 +1,93 @@
+/*++
+/* NAME
+/* recipient 3
+/* SUMMARY
+/* deliver to one local recipient
+/* SYNOPSIS
+/* #include "virtual.h"
+/*
+/* int deliver_recipient(state, usr_attr)
+/* LOCAL_STATE state;
+/* USER_ATTR *usr_attr;
+/* DESCRIPTION
+/* deliver_recipient() delivers a message to a local recipient.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, sender, and more.
+/* .IP usr_attr
+/* Attributes describing user rights and mailbox location.
+/* DIAGNOSTICS
+/* deliver_recipient() returns non-zero when delivery should be
+/* tried again.
+/* SEE ALSO
+/* mailbox(3) delivery to UNIX-style mailbox
+/* maildir(3) delivery to qmail-style maildir
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <bounce.h>
+
+/* Application-specific. */
+
+#include "virtual.h"
+
+/* deliver_recipient - deliver one local recipient */
+
+int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ char *myname = "deliver_recipient";
+ int rcpt_stat;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Set up the recipient-specific attributes. The recipient's lookup
+ * handle is the full address.
+ */
+ if (state.msg_attr.delivered == 0)
+ state.msg_attr.delivered = state.msg_attr.recipient;
+ state.msg_attr.user = mystrdup(state.msg_attr.recipient);
+ lowercase(state.msg_attr.user);
+
+ /*
+ * Deliver
+ */
+ if (msg_verbose)
+ deliver_attr_dump(&state.msg_attr);
+
+ if (deliver_mailbox(state, usr_attr, &rcpt_stat) == 0)
+ rcpt_stat = deliver_unknown(state);
+
+ /*
+ * Cleanup.
+ */
+ myfree(state.msg_attr.user);
+
+ return (rcpt_stat);
+}
diff --git a/gnu/dist/postfix/src/virtual/unknown.c b/gnu/dist/postfix/src/virtual/unknown.c
new file mode 100644
index 00000000000..8989046e0d1
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/unknown.c
@@ -0,0 +1,64 @@
+/*++
+/* NAME
+/* unknown 3
+/* SUMMARY
+/* delivery of unknown recipients
+/* SYNOPSIS
+/* #include "virtual.h"
+/*
+/* int deliver_unknown(state)
+/* LOCAL_STATE state;
+/* DESCRIPTION
+/* deliver_unknown() delivers a message for unknown recipients.
+/* .PP
+/* Arguments:
+/* .IP state
+/* Message delivery attributes (sender, recipient etc.).
+/* .IP usr_attr
+/* Attributes describing user rights and mailbox location.
+/* DIAGNOSTICS
+/* The result status is non-zero when delivery should be tried again.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* Global library. */
+
+#include <bounce.h>
+
+/* Application-specific. */
+
+#include "virtual.h"
+
+/* deliver_unknown - delivery for unknown recipients */
+
+int deliver_unknown(LOCAL_STATE state)
+{
+ char *myname = "deliver_unknown";
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ return (bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
+ "unknown user: \"%s\"", state.msg_attr.user));
+
+}
diff --git a/gnu/dist/postfix/src/virtual/virtual.c b/gnu/dist/postfix/src/virtual/virtual.c
new file mode 100644
index 00000000000..7968dcc2856
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/virtual.c
@@ -0,0 +1,414 @@
+/*++
+/* NAME
+/* virtual 8
+/* SUMMARY
+/* Postfix virtual domain mail delivery agent
+/* SYNOPSIS
+/* \fBvirtual\fR [generic Postfix daemon options]
+/* DESCRIPTION
+/* The \fBvirtual\fR delivery agent is designed for virtual mail
+/* hosting services. Originally based on the Postfix local delivery
+/* agent, this agent looks up recipients with map lookups of their
+/* full recipient address, instead of using hard-coded unix password
+/* file lookups of the address local part only.
+/*
+/* This delivery agent only delivers mail. Other features such as
+/* mail forwarding, out-of-office notifications, etc., must be
+/* configured via virtual maps or via similar lookup mechanisms.
+/* MAILBOX LOCATION
+/* .ad
+/* .fi
+/* The mailbox location is controlled by the \fBvirtual_mailbox_base\fR
+/* and \fBvirtual_mailbox_maps\fR configuration parameters (see below).
+/* The \fBvirtual_mailbox_maps\fR table is indexed by the full recipient
+/* address.
+/*
+/* The mailbox pathname is constructed as follows:
+/*
+/* .ti +2
+/* \fB$virtual_mailbox_base/$virtual_mailbox_maps(\fIrecipient\fB)\fR
+/*
+/* where \fIrecipient\fR is the full recipient address.
+/* UNIX MAILBOX FORMAT
+/* .ad
+/* .fi
+/* When the mailbox location does not end in \fB/\fR, the message
+/* is delivered in UNIX mailbox format. This format stores multiple
+/* messages in one textfile.
+/*
+/* The \fBvirtual\fR delivery agent prepends a "\fBFrom \fIsender
+/* time_stamp\fR" envelope header to each message, prepends a
+/* \fBDelivered-To:\fR message header with the envelope recipient
+/* address, prepends a \fBReturn-Path:\fR message header with the
+/* envelope sender address, prepends a \fB>\fR character to lines
+/* beginning with "\fBFrom \fR", and appends an empty line.
+/*
+/* The mailbox is locked for exclusive access while delivery is in
+/* progress. In case of problems, an attempt is made to truncate the
+/* mailbox to its original length.
+/* QMAIL MAILDIR FORMAT
+/* .ad
+/* .fi
+/* When the mailbox location ends in \fB/\fR, the message is delivered
+/* in qmail \fBmaildir\fR format. This format stores one message per file.
+/*
+/* The \fBvirtual\fR delivery agent daemon prepends a \fBDelivered-To:\fR
+/* message header with the envelope recipient address and prepends a
+/* \fBReturn-Path:\fR message header with the envelope sender address.
+/*
+/* By definition, \fBmaildir\fR format does not require file locking
+/* during mail delivery or retrieval.
+/* MAILBOX OWNERSHIP
+/* .ad
+/* .fi
+/* Mailbox ownership is controlled by the \fBvirtual_uid_maps\fR
+/* and \fBvirtual_gid_maps\fR lookup tables, which are indexed
+/* with the full recipient address. Each table provides
+/* a string with the numerical user and group ID, respectively.
+/*
+/* The \fBvirtual_minimum_uid\fR parameter imposes a lower bound on
+/* numerical user ID values that may be specified in any
+/* \fBvirtual_owner_maps\fR or \fBvirtual_uid_maps\fR.
+/* SECURITY
+/* .ad
+/* .fi
+/* The virtual delivery agent is not security sensitive, provided
+/* that the lookup tables with recipient user/group ID information are
+/* adequately protected. This program is not designed to run chrooted.
+/* STANDARDS
+/* RFC 822 (ARPA Internet Text Messages)
+/* DIAGNOSTICS
+/* Mail bounces when the recipient has no mailbox or when the
+/* recipient is over disk quota. In all other cases, mail for
+/* an existing recipient is deferred and a warning is logged.
+/*
+/* Problems and transactions are logged to \fBsyslogd\fR(8).
+/* Corrupted message files are marked so that the queue
+/* manager can move them to the \fBcorrupt\fR queue afterwards.
+/*
+/* Depending on the setting of the \fBnotify_classes\fR parameter,
+/* the postmaster is notified of bounces and of other trouble.
+/* BUGS
+/* This delivery agent silently ignores address extensions.
+/*
+/* Postfix should have lookup tables that can return multiple result
+/* attributes. In order to avoid the inconvenience of maintaining
+/* three tables, use an LDAP or MYSQL database.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* The following \fBmain.cf\fR parameters are especially relevant to
+/* this program. See the Postfix \fBmain.cf\fR file for syntax details
+/* and for default values. Use the \fBpostfix reload\fR command after
+/* a configuration change.
+/* .SH Mailbox delivery
+/* .ad
+/* .fi
+/* .IP \fBvirtual_mailbox_base\fR
+/* Specifies a path that is prepended to all mailbox or maildir paths.
+/* This is a safety measure to ensure that an out of control map in
+/* \fBvirtual_mailbox_maps\fR doesn't litter the filesystem with mailboxes.
+/* While it could be set to "/", this setting isn't recommended.
+/* .IP "\fBvirtual_mailbox_maps\fR (regexp maps disallowed)"
+/* Recipients are looked up in these maps to determine the path to
+/* their mailbox or maildir. If the returned path ends in a slash
+/* ("/"), maildir-style delivery is carried out, otherwise the
+/* path is assumed to specify a UNIX-style mailbox file.
+/*
+/* Note that \fBvirtual_mailbox_base\fR is unconditionally prepended
+/* to this path.
+/* .IP \fBvirtual_minimum_uid\fR
+/* Specifies a minimum uid that will be accepted as a return from
+/* a \fBvirtual_owner_maps\fR or \fBvirtual_uid_maps\fR lookup.
+/* Returned values less than this will be rejected, and the message
+/* will be deferred.
+/* .IP "\fBvirtual_uid_maps\fR (regexp maps disallowed)"
+/* Recipients are looked up in these maps to determine the user ID to be
+/* used when writing to the target mailbox.
+/* .IP "\fBvirtual_gid_maps\fR (regexp maps disallowed)"
+/* Recipients are looked up in these maps to determine the group ID to be
+/* used when writing to the target mailbox.
+/* .SH "Locking controls"
+/* .ad
+/* .fi
+/* .IP \fBvirtual_mailbox_lock\fR
+/* How to lock UNIX-style mailboxes: one or more of \fBflock\fR,
+/* \fBfcntl\fR or \fBdotlock\fR. The \fBdotlock\fR method requires
+/* that the recipient UID or GID has write access to the parent
+/* directory of the mailbox file.
+/*
+/* This setting is ignored with \fBmaildir\fR style delivery,
+/* because such deliveries are safe without explicit locks.
+/*
+/* Use the command \fBpostconf -l\fR to find out what locking methods
+/* are available on your system.
+/* .IP \fBdeliver_lock_attempts\fR
+/* Limit the number of attempts to acquire an exclusive lock
+/* on a UNIX-style mailbox file.
+/* .IP \fBdeliver_lock_delay\fR
+/* Time (default: seconds) between successive attempts to acquire
+/* an exclusive lock on a UNIX-style mailbox file. The actual delay
+/* is slightly randomized.
+/* .IP \fBstale_lock_time\fR
+/* Limit the time after which a stale lockfile is removed (applicable
+/* to UNIX-style mailboxes only).
+/* .SH "Resource controls"
+/* .ad
+/* .fi
+/* .IP \fBvirtual_destination_concurrency_limit\fR
+/* Limit the number of parallel deliveries to the same domain
+/* via the \fBvirtual\fR delivery agent.
+/* The default limit is taken from the
+/* \fBdefault_destination_concurrency_limit\fR parameter.
+/* The limit is enforced by the Postfix queue manager.
+/* .IP \fBvirtual_destination_recipient_limit\fR
+/* Limit the number of recipients per message delivery
+/* via the \fBvirtual\fR delivery agent.
+/* The default limit is taken from the
+/* \fBdefault_destination_recipient_limit\fR parameter.
+/* The limit is enforced by the Postfix queue manager.
+/* .IP \fBvirtual_mailbox_limit\fR
+/* The maximal size in bytes of a mailbox or maildir file.
+/* Set to zero to disable the limit.
+/* HISTORY
+/* .ad
+/* .fi
+/* This agent was originally based on the Postfix local delivery
+/* agent. Modifications mainly consisted of removing code that either
+/* was not applicable or that was not safe in this context: aliases,
+/* ~user/.forward files, delivery to "|command" or to /file/name.
+/*
+/* The \fBDelivered-To:\fR header appears in the \fBqmail\fR system
+/* by Daniel Bernstein.
+/*
+/* The \fBmaildir\fR structure appears in the \fBqmail\fR system
+/* by Daniel Bernstein.
+/* SEE ALSO
+/* bounce(8) non-delivery status reports
+/* syslogd(8) system logging
+/* qmgr(8) queue manager
+/* 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
+/*
+/* Andrew McNamara
+/* andrewm@connect.com.au
+/* connect.com.au Pty. Ltd.
+/* Level 3, 213 Miller St
+/* North Sydney 2060, NSW, Australia
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <iostuff.h>
+#include <set_eugid.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <mail_params.h>
+#include <mail_conf.h>
+#include <mail_params.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+#include "virtual.h"
+
+ /*
+ * Tunable parameters.
+ */
+char *var_virt_mailbox_maps;
+char *var_virt_uid_maps;
+char *var_virt_gid_maps;
+int var_virt_minimum_uid;
+char *var_virt_mailbox_base;
+char *var_virt_mailbox_lock;
+int var_virt_mailbox_limit;
+
+ /*
+ * Mappings.
+ */
+MAPS *virtual_mailbox_maps;
+MAPS *virtual_uid_maps;
+MAPS *virtual_gid_maps;
+
+ /*
+ * Bit masks.
+ */
+int virtual_mbox_lock_mask;
+
+/* local_deliver - deliver message with extreme prejudice */
+
+static int local_deliver(DELIVER_REQUEST *rqst, char *service)
+{
+ char *myname = "local_deliver";
+ RECIPIENT *rcpt_end = rqst->rcpt_list.info + rqst->rcpt_list.len;
+ RECIPIENT *rcpt;
+ int rcpt_stat;
+ int msg_stat;
+ LOCAL_STATE state;
+ USER_ATTR usr_attr;
+
+ if (msg_verbose)
+ msg_info("local_deliver: %s from %s", rqst->queue_id, rqst->sender);
+
+ /*
+ * Initialize the delivery attributes that are not recipient specific.
+ */
+ state.level = 0;
+ deliver_attr_init(&state.msg_attr);
+ state.msg_attr.queue_name = rqst->queue_name;
+ state.msg_attr.queue_id = rqst->queue_id;
+ state.msg_attr.fp = rqst->fp;
+ state.msg_attr.offset = rqst->data_offset;
+ state.msg_attr.sender = rqst->sender;
+ state.msg_attr.relay = service;
+ state.msg_attr.arrival_time = rqst->arrival_time;
+ RESET_USER_ATTR(usr_attr, state.level);
+ state.request = rqst;
+
+ /*
+ * Iterate over each recipient named in the delivery request. When the
+ * mail delivery status for a given recipient is definite (i.e. bounced
+ * or delivered), update the message queue file and cross off the
+ * recipient. Update the per-message delivery status.
+ */
+ for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
+ state.msg_attr.recipient = rcpt->address;
+ rcpt_stat = deliver_recipient(state, usr_attr);
+ if (rcpt_stat == 0)
+ deliver_completed(state.msg_attr.fp, rcpt->offset);
+ msg_stat |= rcpt_stat;
+ }
+
+ return (msg_stat);
+}
+
+/* local_service - perform service for client */
+
+static void local_service(VSTREAM *stream, char *service, char **argv)
+{
+ DELIVER_REQUEST *request;
+ int status;
+
+ /*
+ * Sanity check. This service takes no command-line arguments.
+ */
+ if (argv[0])
+ msg_fatal("unexpected command-line argument: %s", argv[0]);
+
+ /*
+ * This routine runs whenever a client connects to the UNIX-domain socket
+ * that is dedicated to local mail delivery service. What we see below is
+ * a little protocol to (1) tell the client that we are ready, (2) read a
+ * delivery request from the client, and (3) report the completion status
+ * of that request.
+ */
+ if ((request = deliver_request_read(stream)) != 0) {
+ status = local_deliver(request, service);
+ deliver_request_done(stream, request, status);
+ }
+}
+
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(char *unused_name, char **unused_argv)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
+}
+
+/* post_init - post-jail initialization */
+
+static void post_init(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * Drop privileges most of the time.
+ */
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ virtual_mailbox_maps =
+ maps_create(VAR_VIRT_MAILBOX_MAPS, var_virt_mailbox_maps,
+ DICT_FLAG_LOCK);
+
+ virtual_uid_maps =
+ maps_create(VAR_VIRT_UID_MAPS, var_virt_uid_maps, DICT_FLAG_LOCK);
+
+ virtual_gid_maps =
+ maps_create(VAR_VIRT_GID_MAPS, var_virt_gid_maps, DICT_FLAG_LOCK);
+
+ virtual_mbox_lock_mask = mbox_lock_mask(var_virt_mailbox_lock);
+}
+
+/* pre_init - pre-jail initialization */
+
+static void pre_init(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * Reset the file size limit from the message size limit to the mailbox
+ * size limit.
+ *
+ * We can't have mailbox size limit smaller than the message size limit,
+ * because that prohibits the delivery agent from updating the queue
+ * file.
+ */
+ if (var_virt_mailbox_limit) {
+ if (var_virt_mailbox_limit < var_message_limit)
+ msg_fatal("main.cf configuration error: %s is smaller than %s",
+ VAR_VIRT_MAILBOX_LIMIT, VAR_MESSAGE_LIMIT);
+ set_file_limit(var_virt_mailbox_limit);
+ }
+}
+
+/* main - pass control to the single-threaded skeleton */
+
+int main(int argc, char **argv)
+{
+ static CONFIG_INT_TABLE int_table[] = {
+ VAR_VIRT_MINUID, DEF_VIRT_MINUID, &var_virt_minimum_uid, 1, 0,
+ VAR_VIRT_MAILBOX_LIMIT, DEF_VIRT_MAILBOX_LIMIT, &var_virt_mailbox_limit, 0, 0,
+ 0,
+ };
+ static CONFIG_STR_TABLE str_table[] = {
+ VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
+ VAR_VIRT_UID_MAPS, DEF_VIRT_UID_MAPS, &var_virt_uid_maps, 0, 0,
+ VAR_VIRT_GID_MAPS, DEF_VIRT_GID_MAPS, &var_virt_gid_maps, 0, 0,
+ VAR_VIRT_MAILBOX_BASE, DEF_VIRT_MAILBOX_BASE, &var_virt_mailbox_base, 0, 0,
+ VAR_VIRT_MAILBOX_LOCK, DEF_VIRT_MAILBOX_LOCK, &var_virt_mailbox_lock, 1, 0,
+ 0,
+ };
+
+ single_server_main(argc, argv, local_service,
+ MAIL_SERVER_INT_TABLE, int_table,
+ MAIL_SERVER_STR_TABLE, str_table,
+ MAIL_SERVER_PRE_INIT, pre_init,
+ MAIL_SERVER_POST_INIT, post_init,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ 0);
+}
diff --git a/gnu/dist/postfix/src/virtual/virtual.h b/gnu/dist/postfix/src/virtual/virtual.h
new file mode 100644
index 00000000000..1443bc8d04f
--- /dev/null
+++ b/gnu/dist/postfix/src/virtual/virtual.h
@@ -0,0 +1,134 @@
+/*++
+/* NAME
+/* virtual 3h
+/* SUMMARY
+/* virtual mail delivery
+/* SYNOPSIS
+/* #include "virtual.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#include <unistd.h>
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+ * Global library.
+ */
+#include <deliver_request.h>
+#include <maps.h>
+#include <mbox_conf.h>
+
+ /*
+ * Mappings.
+ */
+extern MAPS *virtual_mailbox_maps;
+extern MAPS *virtual_uid_maps;
+extern MAPS *virtual_gid_maps;
+
+ /*
+ * User attributes: these control the privileges for delivery to external
+ * commands, external files, or mailboxes, and the initial environment of
+ * external commands.
+ */
+typedef struct USER_ATTR {
+ uid_t uid; /* file/command access */
+ gid_t gid; /* file/command access */
+ char *mailbox; /* mailbox file or directory */
+} USER_ATTR;
+
+ /*
+ * Critical macros. Not for obscurity, but to ensure consistency.
+ */
+#define RESET_USER_ATTR(usr_attr, level) { \
+ usr_attr.uid = 0; usr_attr.gid = 0; usr_attr.mailbox = 0; \
+ if (msg_verbose) \
+ msg_info("%s[%d]: reset user_attr", myname, level); \
+ }
+
+ /*
+ * The delivery attributes are inherited from files, from aliases, and from
+ * whatnot. Some of the information is changed on the fly. DELIVER_ATTR
+ * structres are therefore passed by value, so there is no need to undo
+ * changes.
+ */
+typedef struct DELIVER_ATTR {
+ int level; /* recursion level */
+ VSTREAM *fp; /* open queue file */
+ char *queue_name; /* mail queue id */
+ char *queue_id; /* mail queue id */
+ long offset; /* data offset */
+ char *sender; /* taken from envelope */
+ char *recipient; /* taken from resolver */
+ char *user; /* recipient lookup handle */
+ char *delivered; /* for loop detection */
+ char *relay; /* relay host */
+ long arrival_time; /* arrival time */
+} DELIVER_ATTR;
+
+extern void deliver_attr_init(DELIVER_ATTR *);
+extern void deliver_attr_dump(DELIVER_ATTR *);
+
+#define FEATURE_NODELIVERED (1<<0) /* no delivered-to */
+
+ /*
+ * Rather than schlepping around dozens of arguments, here is one that has
+ * all. Well, almost. The user attributes are just a bit too sensitive, so
+ * they are passed around separately.
+ */
+typedef struct LOCAL_STATE {
+ int level; /* nesting level, for logging */
+ DELIVER_ATTR msg_attr; /* message attributes */
+ DELIVER_REQUEST *request; /* as from queue manager */
+} LOCAL_STATE;
+
+ /*
+ * Bundle up some often-user attributes.
+ */
+#define BOUNCE_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \
+ attr.arrival_time
+#define SENT_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \
+ attr.arrival_time
+#define COPY_ATTR(attr) attr.sender, attr.delivered, attr.fp
+
+#define MSG_LOG_STATE(m, p) \
+ msg_info("%s[%d]: recip %s deliver %s", m, \
+ p.level, \
+ p.msg_attr.recipient ? p.msg_attr.recipient : "", \
+ p.msg_attr.delivered ? p.msg_attr.delivered : "")
+
+ /*
+ * "inner" nodes of the delivery graph.
+ */
+extern int deliver_recipient(LOCAL_STATE, USER_ATTR);
+
+ /*
+ * "leaf" nodes of the delivery graph.
+ */
+extern int deliver_mailbox(LOCAL_STATE, USER_ATTR, int *);
+extern int deliver_file(LOCAL_STATE, USER_ATTR, char *);
+extern int deliver_maildir(LOCAL_STATE, USER_ATTR);
+extern int deliver_unknown(LOCAL_STATE);
+
+ /*
+ * Mailbox lock protocol.
+ */
+extern int virtual_mbox_lock_mask;
+
+/* 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
+/*--*/