]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-20011014
authorWietse Venema <wietse@porcupine.org>
Sun, 14 Oct 2001 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:27:28 +0000 (06:27 +0000)
43 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/conf/sample-local.cf
postfix/src/bounce/bounce.c
postfix/src/cleanup/cleanup.c
postfix/src/flush/flush.c
postfix/src/global/Makefile.in
postfix/src/global/abounce.c
postfix/src/global/bounce.c
postfix/src/global/defer.c
postfix/src/global/deliver_pass.c
postfix/src/global/deliver_request.c
postfix/src/global/flush_clnt.c
postfix/src/global/mail_command_client.c [moved from postfix/src/global/mail_command_write.c with 66% similarity]
postfix/src/global/mail_command_server.c [moved from postfix/src/global/mail_command_read.c with 61% similarity]
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/global/mail_stream.c
postfix/src/global/mail_version.h
postfix/src/global/post_mail.c
postfix/src/global/resolve_clnt.c
postfix/src/global/rewrite_clnt.c
postfix/src/local/command.c
postfix/src/local/forward.c
postfix/src/local/local.c
postfix/src/local/local.h
postfix/src/local/mailbox.c
postfix/src/nqmgr/qmgr_deliver.c
postfix/src/pickup/pickup.c
postfix/src/postdrop/postdrop.c
postfix/src/qmgr/qmgr_deliver.c
postfix/src/qmqpd/qmqpd.c
postfix/src/smtpd/smtpd.c
postfix/src/trivial-rewrite/resolve.c
postfix/src/trivial-rewrite/rewrite.c
postfix/src/trivial-rewrite/trivial-rewrite.c
postfix/src/util/Makefile.in
postfix/src/util/attr.c
postfix/src/util/attr.h
postfix/src/util/attr_io.h [deleted file]
postfix/src/util/attr_print.c
postfix/src/util/attr_scan.c
postfix/src/util/attr_table.c [new file with mode: 0644]

index 5e1656aa2218ad97cb05395db04c4e3783fa8fb1..2cec9fdb27bc97486c4f718ca2f4ff07967c22ed 100644 (file)
@@ -1,6 +1,7 @@
 -TABOUNCE
 -TALIAS_TOKEN
 -TARGV
+-TATTR_TABLE
 -TBH_TABLE
 -TBINATTR
 -TBINATTR_INFO
index 7a37c64a1e3bc7c8914db759af35784ab52a06ec..827a7fba102aee0fb1d06cf1cfb9d63acda29af0 100644 (file)
@@ -5487,6 +5487,11 @@ Apologies for any names omitted.
        Bugfix: there was a minute memory leak when an smtpd access
        restriction is misconfigured. File: smtpd/smtpd_check.c.
 
+20011010-14
+
+       Replaced the internal protocols by (name,value) attribute
+       lists. This is more extensible.
+
 Open problems:
 
        Minor: The $process_id_directory setting is not used anywhere
index eab49fd8776ecda6309204a9109f75c9c398062d..69f87dda1833938335d4ee4fe78d4f6e506d72e0 100644 (file)
@@ -161,6 +161,13 @@ home_mailbox =
 # mailbox_command = /some/where/procmail -a "$EXTENSION"
 mailbox_command = 
 
+# The mailbox_command_maps allows you to specify a per-user mailbox
+# command. The maps are keyed by username (not including the domain).
+# Specify one or more maps. If this feature is used then every user
+# must have a matching entry.
+#
+# mailbox_command_maps = hash:/etc/postfix/mailbox_commands
+
 # The mailbox_transport specifies the optional transport in master.cf
 # to use after processing aliases and .forward files. This parameter
 # has precedence over the mailbox_command, fallback_transport and
index 75a62032eebae4881c25cae941ff60dff4234f85..8cfcf7380e4c665e47f0a763bbeca5d299d23c77 100644 (file)
@@ -142,8 +142,11 @@ 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, "%d %s %s %s",
+                           ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
+                           ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
+                           ATTR_TYPE_STR, MAIL_ATTR_WHY, why, 0) != 4) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -178,8 +181,11 @@ 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_FLAG_MISSING,
+                           ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
+                        ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, 0) != 4) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -206,22 +212,25 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, int flush)
      * Execute the request.
      */
     return (bounce_notify_service(service_name, STR(queue_name),
-                                     STR(queue_id), STR(sender), 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";
+    char   *myname = "bounce_verp_proto";
     int     flags;
 
     /*
      * Read and validate the client request.
      */
-    if (mail_command_read(client, "%d %s %s %s %s",
-                         &flags, queue_name, queue_id,
-                         sender, verp_delims) != 5) {
+    if (attr_scan(client, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+                 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, 0) != 5) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -287,7 +296,8 @@ 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_MISSING | ATTR_FLAG_EXTRA | ATTR_FLAG_MORE,
+                 ATTR_TYPE_NUM, MAIL_ATTR_NREQ, &command, 0) != 1) {
        msg_warn("malformed request");
        status = -1;
     } else if (command == BOUNCE_CMD_VERP) {
@@ -307,7 +317,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);
 
     /*
index 01a67f09f6366bfb6a5a348113929d13e0878c18..1802c1a86cb215cb921d04f89aa755ae854f3dc0 100644 (file)
@@ -191,8 +191,11 @@ 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_MISSING | ATTR_FLAG_EXTRA,
+                 ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags) != 1) {
        state->errs |= CLEANUP_STAT_BAD;
        flags = 0;
     }
@@ -230,7 +233,10 @@ 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_close(state),
+              ATTR_TYPE_STR, MAIL_ATTR_WHY, "",
+              ATTR_TYPE_END);
 
     /*
      * Cleanup.
index 5fd7316c3fed99ecadebec9cf6556054f8b79ac1..ffabf3329406e60533af09b569f1f392707b32e7 100644 (file)
@@ -540,31 +540,48 @@ 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 (attr_scan(client_stream, ATTR_FLAG_MORE | ATTR_FLAG_EXTRA | ATTR_FLAG_MISSING,
+                 ATTR_TYPE_STR, MAIL_ATTR_REQ, request,
+                 ATTR_TYPE_END) == 1) {
        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_MISSING | ATTR_FLAG_EXTRA,
+                    ATTR_TYPE_STR, MAIL_ATTR_SITE, site, ATTR_FLAG_MISSING,
+                         ATTR_TYPE_STR, MAIL_ATTR_SITE, 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_MISSING | ATTR_FLAG_EXTRA,
+                         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);
index 97e35b60fa4ebb398c435e3b03e5450212fac975..b96837dc843627ffd2a4da8f10869a8f180bd1b3 100644 (file)
@@ -4,11 +4,11 @@ SRCS  = been_here.c bounce.c canon_addr.c cleanup_strerror.c clnt_stream.c \
        deliver_flock.c deliver_pass.c deliver_request.c domain_list.c \
        dot_lockfile.c dot_lockfile_as.c ext_prop.c file_id.c \
        header_opts.c is_header.c mail_addr.c mail_addr_crunch.c \
-       mail_addr_find.c mail_addr_map.c mail_command_read.c \
-       mail_command_write.c mail_conf.c mail_conf_bool.c mail_conf_int.c \
+       mail_addr_find.c mail_addr_map.c mail_command_server.c \
+       mail_command_client.c mail_conf.c mail_conf_bool.c mail_conf_int.c \
        mail_conf_raw.c mail_conf_str.c mail_connect.c mail_copy.c \
        mail_date.c mail_error.c mail_flush.c mail_open_ok.c mail_params.c \
-       mail_pathname.c mail_print.c mail_queue.c mail_run.c mail_scan.c \
+       mail_pathname.c mail_queue.c mail_run.c \
        mail_scan_dir.c mail_stream.c mail_task.c mail_trigger.c maps.c \
        mark_corrupt.c mkmap_db.c mkmap_dbm.c mkmap_open.c mynetworks.c \
        mypwd.c namadr_list.c off_cvt.c opened.c own_inet_addr.c \
@@ -25,11 +25,11 @@ OBJS        = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
        deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
        dot_lockfile.o dot_lockfile_as.o ext_prop.o file_id.o \
        header_opts.o is_header.o mail_addr.o mail_addr_crunch.o \
-       mail_addr_find.o mail_addr_map.o mail_command_read.o \
-       mail_command_write.o mail_conf.o mail_conf_bool.o mail_conf_int.o \
+       mail_addr_find.o mail_addr_map.o mail_command_server.o \
+       mail_command_client.o mail_conf.o mail_conf_bool.o mail_conf_int.o \
        mail_conf_raw.o mail_conf_str.o mail_connect.o mail_copy.o \
        mail_date.o mail_error.o mail_flush.o mail_open_ok.o mail_params.o \
-       mail_pathname.o mail_print.o mail_queue.o mail_run.o mail_scan.o \
+       mail_pathname.o mail_queue.o mail_run.o \
        mail_scan_dir.o mail_stream.o mail_task.o mail_trigger.o maps.o \
        mark_corrupt.o mkmap_db.o mkmap_dbm.o mkmap_open.o mynetworks.o \
        mypwd.o namadr_list.o off_cvt.o opened.o own_inet_addr.o \
@@ -254,6 +254,8 @@ abounce.o: ../../include/vstream.h
 abounce.o: ../../include/vbuf.h
 abounce.o: mail_proto.h
 abounce.o: ../../include/iostuff.h
+abounce.o: ../../include/attr.h
+abounce.o: ../../include/htable.h
 abounce.o: abounce.h
 abounce.o: bounce.h
 been_here.o: been_here.c
@@ -274,6 +276,8 @@ bounce.o: mail_params.h
 bounce.o: mail_proto.h
 bounce.o: ../../include/vstream.h
 bounce.o: ../../include/iostuff.h
+bounce.o: ../../include/attr.h
+bounce.o: ../../include/htable.h
 bounce.o: defer.h
 bounce.o: bounce.h
 bounce_log.o: bounce_log.c
@@ -308,6 +312,8 @@ clnt_stream.o: ../../include/vbuf.h
 clnt_stream.o: ../../include/events.h
 clnt_stream.o: ../../include/iostuff.h
 clnt_stream.o: mail_proto.h
+clnt_stream.o: ../../include/attr.h
+clnt_stream.o: ../../include/htable.h
 clnt_stream.o: mail_params.h
 clnt_stream.o: clnt_stream.h
 debug_peer.o: debug_peer.c
@@ -331,6 +337,8 @@ defer.o: mail_queue.h
 defer.o: ../../include/vstream.h
 defer.o: mail_proto.h
 defer.o: ../../include/iostuff.h
+defer.o: ../../include/attr.h
+defer.o: ../../include/htable.h
 defer.o: flush_clnt.h
 defer.o: bounce.h
 defer.o: defer.h
@@ -365,6 +373,8 @@ deliver_pass.o: deliver_request.h
 deliver_pass.o: recipient_list.h
 deliver_pass.o: mail_proto.h
 deliver_pass.o: ../../include/iostuff.h
+deliver_pass.o: ../../include/attr.h
+deliver_pass.o: ../../include/htable.h
 deliver_request.o: deliver_request.c
 deliver_request.o: ../../include/sys_defs.h
 deliver_request.o: ../../include/msg.h
@@ -376,6 +386,8 @@ deliver_request.o: ../../include/iostuff.h
 deliver_request.o: ../../include/myflock.h
 deliver_request.o: mail_queue.h
 deliver_request.o: mail_proto.h
+deliver_request.o: ../../include/attr.h
+deliver_request.o: ../../include/htable.h
 deliver_request.o: mail_open_ok.h
 deliver_request.o: recipient_list.h
 deliver_request.o: deliver_request.h
@@ -419,6 +431,8 @@ flush_clnt.o: ../../include/vstream.h
 flush_clnt.o: ../../include/vbuf.h
 flush_clnt.o: mail_proto.h
 flush_clnt.o: ../../include/iostuff.h
+flush_clnt.o: ../../include/attr.h
+flush_clnt.o: ../../include/htable.h
 flush_clnt.o: mail_flush.h
 flush_clnt.o: flush_clnt.h
 flush_clnt.o: mail_params.h
@@ -477,19 +491,22 @@ mail_addr_map.o: mail_addr_find.h
 mail_addr_map.o: maps.h
 mail_addr_map.o: mail_addr_crunch.h
 mail_addr_map.o: mail_addr_map.h
-mail_command_read.o: mail_command_read.c
-mail_command_read.o: ../../include/sys_defs.h
-mail_command_read.o: ../../include/vstring.h
-mail_command_read.o: ../../include/vbuf.h
-mail_command_read.o: ../../include/vstream.h
-mail_command_read.o: mail_proto.h
-mail_command_read.o: ../../include/iostuff.h
-mail_command_write.o: mail_command_write.c
-mail_command_write.o: ../../include/sys_defs.h
-mail_command_write.o: ../../include/vstream.h
-mail_command_write.o: ../../include/vbuf.h
-mail_command_write.o: mail_proto.h
-mail_command_write.o: ../../include/iostuff.h
+mail_command_client.o: mail_command_client.c
+mail_command_client.o: ../../include/sys_defs.h
+mail_command_client.o: ../../include/vstream.h
+mail_command_client.o: ../../include/vbuf.h
+mail_command_client.o: mail_proto.h
+mail_command_client.o: ../../include/iostuff.h
+mail_command_client.o: ../../include/attr.h
+mail_command_client.o: ../../include/htable.h
+mail_command_server.o: mail_command_server.c
+mail_command_server.o: ../../include/sys_defs.h
+mail_command_server.o: ../../include/vstream.h
+mail_command_server.o: ../../include/vbuf.h
+mail_command_server.o: mail_proto.h
+mail_command_server.o: ../../include/iostuff.h
+mail_command_server.o: ../../include/attr.h
+mail_command_server.o: ../../include/htable.h
 mail_conf.o: mail_conf.c
 mail_conf.o: ../../include/sys_defs.h
 mail_conf.o: ../../include/msg.h
@@ -553,6 +570,8 @@ mail_connect.o: ../../include/iostuff.h
 mail_connect.o: ../../include/mymalloc.h
 mail_connect.o: timed_ipc.h
 mail_connect.o: mail_proto.h
+mail_connect.o: ../../include/attr.h
+mail_connect.o: ../../include/htable.h
 mail_copy.o: mail_copy.c
 mail_copy.o: ../../include/sys_defs.h
 mail_copy.o: ../../include/msg.h
@@ -585,6 +604,8 @@ mail_flush.o: mail_proto.h
 mail_flush.o: ../../include/vstream.h
 mail_flush.o: ../../include/vbuf.h
 mail_flush.o: ../../include/iostuff.h
+mail_flush.o: ../../include/attr.h
+mail_flush.o: ../../include/htable.h
 mail_flush.o: mail_flush.h
 mail_open_ok.o: mail_open_ok.c
 mail_open_ok.o: ../../include/sys_defs.h
@@ -610,6 +631,8 @@ mail_params.o: mail_version.h
 mail_params.o: mail_proto.h
 mail_params.o: ../../include/vstream.h
 mail_params.o: ../../include/iostuff.h
+mail_params.o: ../../include/attr.h
+mail_params.o: ../../include/htable.h
 mail_params.o: verp_sender.h
 mail_params.o: mail_params.h
 mail_pathname.o: mail_pathname.c
@@ -620,6 +643,8 @@ mail_pathname.o: ../../include/vbuf.h
 mail_pathname.o: mail_proto.h
 mail_pathname.o: ../../include/vstream.h
 mail_pathname.o: ../../include/iostuff.h
+mail_pathname.o: ../../include/attr.h
+mail_pathname.o: ../../include/htable.h
 mail_print.o: mail_print.c
 mail_print.o: ../../include/sys_defs.h
 mail_print.o: ../../include/msg.h
@@ -628,6 +653,8 @@ mail_print.o: ../../include/vstream.h
 mail_print.o: ../../include/vbuf.h
 mail_print.o: mail_proto.h
 mail_print.o: ../../include/iostuff.h
+mail_print.o: ../../include/attr.h
+mail_print.o: ../../include/htable.h
 mail_queue.o: mail_queue.c
 mail_queue.o: ../../include/sys_defs.h
 mail_queue.o: ../../include/msg.h
@@ -663,6 +690,8 @@ mail_scan.o: ../../include/vstring_vstream.h
 mail_scan.o: ../../include/mymalloc.h
 mail_scan.o: mail_proto.h
 mail_scan.o: ../../include/iostuff.h
+mail_scan.o: ../../include/attr.h
+mail_scan.o: ../../include/htable.h
 mail_scan_dir.o: mail_scan_dir.c
 mail_scan_dir.o: ../../include/sys_defs.h
 mail_scan_dir.o: ../../include/scan_dir.h
@@ -679,6 +708,8 @@ mail_stream.o: ../../include/argv.h
 mail_stream.o: cleanup_user.h
 mail_stream.o: mail_proto.h
 mail_stream.o: ../../include/iostuff.h
+mail_stream.o: ../../include/attr.h
+mail_stream.o: ../../include/htable.h
 mail_stream.o: mail_queue.h
 mail_stream.o: opened.h
 mail_stream.o: mail_params.h
@@ -701,6 +732,8 @@ mail_trigger.o: mail_params.h
 mail_trigger.o: mail_proto.h
 mail_trigger.o: ../../include/vstream.h
 mail_trigger.o: ../../include/vbuf.h
+mail_trigger.o: ../../include/attr.h
+mail_trigger.o: ../../include/htable.h
 mail_version.o: mail_version.c
 maps.o: maps.c
 maps.o: ../../include/sys_defs.h
@@ -860,6 +893,8 @@ post_mail.o: record.h
 post_mail.o: rec_type.h
 post_mail.o: mail_proto.h
 post_mail.o: ../../include/iostuff.h
+post_mail.o: ../../include/attr.h
+post_mail.o: ../../include/htable.h
 post_mail.o: cleanup_user.h
 post_mail.o: post_mail.h
 post_mail.o: mail_date.h
@@ -927,6 +962,8 @@ resolve_clnt.o: ../../include/vstring_vstream.h
 resolve_clnt.o: ../../include/events.h
 resolve_clnt.o: ../../include/iostuff.h
 resolve_clnt.o: mail_proto.h
+resolve_clnt.o: ../../include/attr.h
+resolve_clnt.o: ../../include/htable.h
 resolve_clnt.o: mail_params.h
 resolve_clnt.o: clnt_stream.h
 resolve_clnt.o: resolve_clnt.h
@@ -949,6 +986,8 @@ rewrite_clnt.o: ../../include/events.h
 rewrite_clnt.o: ../../include/iostuff.h
 rewrite_clnt.o: quote_822_local.h
 rewrite_clnt.o: mail_proto.h
+rewrite_clnt.o: ../../include/attr.h
+rewrite_clnt.o: ../../include/htable.h
 rewrite_clnt.o: mail_params.h
 rewrite_clnt.o: clnt_stream.h
 rewrite_clnt.o: rewrite_clnt.h
index 6f23020812e59d55b58fae3e27e5f96ea4db8ab0..f688864fa123b68783516dadb566be43b1fcde03 100644 (file)
@@ -171,7 +171,9 @@ 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_MISSING | ATTR_FLAG_EXTRA,
+                              ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                              ATTR_TYPE_END) == 1 ? status : -1);
 }
 
 /* abounce_request_verp - suspend pseudo thread until server reply event */
@@ -197,8 +199,14 @@ static void abounce_request_verp(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 %s", command,
-                  flags, queue, id, sender, verp, 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_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 {
@@ -251,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 {
index 75b07298d877b7e50faa59b863ff84a1adf4b5bd..044c7b8e48f5aa2ad12120c650da1f8843a45dff 100644 (file)
@@ -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_REQ, 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_REQ, 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);
index 934b289cba92284591ae1445fddd2cdda17bf80d..6eaa2bd8ebdd27ec1fcee0f701b1816b0a32163a 100644 (file)
@@ -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_REQ, 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));
@@ -175,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_REQ, 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);
@@ -190,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_REQ, 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);
index 2deec8c3c2adfb4afe753d542b7078e9bb2a1818..cd2d7e72a4ff400323c5863f813925855fb09504 100644 (file)
@@ -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_MISSING | ATTR_FLAG_EXTRA,
+                 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_NUM, MAIL_ATTR_OFFSET, request->data_offset,
+              ATTR_TYPE_NUM, 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_NUM, MAIL_ATTR_TIME, request->arrival_time,
+              ATTR_TYPE_NUM, 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_MISSING | ATTR_FLAG_EXTRA,
+                 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;
     }
index 3cd4dd8481284b395ffed4229275e8844d1efae3..fecf7e4ba61c22669e627d4f9d68efbc14a26d63 100644 (file)
@@ -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
@@ -118,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");
@@ -138,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");
@@ -187,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_MISSING | ATTR_FLAG_EXTRA | 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_NUM, MAIL_ATTR_OFFSET, &request->data_offset,
+                 ATTR_TYPE_NUM, 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_NUM, 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)
@@ -205,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_EXTRA,
+                     ATTR_TYPE_NUM, 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_EXTRA,
+                     ATTR_TYPE_STR, MAIL_ATTR_RECIP, address,
+                     ATTR_TYPE_END) != 1)
            return (-1);
        recipient_list_add(&request->rcpt_list, offset, vstring_str(address));
     }
index 09e92fe8cb7de8368c3a9698fea66142388df8f2..56852b1b0992261063de6472c775cf203ea94d1c 100644 (file)
@@ -100,8 +100,9 @@ int     flush_purge(void)
     if (*var_fflush_domains == 0)
        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_PRIVATE, 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);
@@ -125,8 +126,9 @@ int     flush_refresh(void)
     if (*var_fflush_domains == 0)
        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_PRIVATE, 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);
@@ -150,8 +152,10 @@ int     flush_send(const char *site)
     if (*var_fflush_domains == 0)
        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_PRIVATE, 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);
@@ -175,8 +179,11 @@ int     flush_add(const char *site, const char *queue_id)
     if (*var_fflush_domains == 0)
        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_PRIVATE, 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,
similarity index 66%
rename from postfix/src/global/mail_command_write.c
rename to postfix/src/global/mail_command_client.c
index e6e85578809b5d5cdbebc05d9b2a8305449ddde7..cdb1422d0107a69f06658411c6b8420d1e3321e0 100644 (file)
@@ -1,15 +1,16 @@
 /*++
 /* NAME
-/*     mail_command_write 3
+/*     mail_command_client 3
 /* SUMMARY
 /*     single-command client
 /* SYNOPSIS
 /*     #include <mail_proto.h>
 /*
-/*     int     mail_command_write(class, name, format, ...)
+/*     int     mail_command_client(class, name, type, attr, ...)
 /*     const char *class;
 /*     const char *name;
-/*     const char *format;
+/*     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
 /*     Service type: MAIL_CLASS_PUBLIC or MAIL_CLASS_PRIVATE
 /* .IP name
 /*     Service name (master.cf).
-/* .IP format
-/*     Format string understood by mail_print(3).
+/* .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
-/*     mail_command_read(3), server interface
-/*     mail_proto(5h), client-server protocol
+/*     attr_print(3), send attributes over byte stream
+/*     mail_command_server(3), server interface
+/*     mail_proto(3h), client-server protocol
 /* LICENSE
 /* .ad
 /* .fi
 
 /* Global library. */
 
-#include "mail_proto.h"
+#include <mail_proto.h>
 
-/* mail_command_write - single-command transaction with completion status */
+/* mail_command_client - single-command transaction with completion status */
 
-int     mail_command_write(const char *class, const char *name,
-                                  const char *fmt,...)
+int     mail_command_client(const char *class, const char *name,...)
 {
     va_list ap;
     VSTREAM *stream;
@@ -69,13 +70,12 @@ int     mail_command_write(const char *class, const char *name,
      */
     if ((stream = mail_connect(class, name, BLOCKING)) == 0)
        return (-1);
-    va_start(ap, fmt);
-    status = mail_vprint(stream, fmt, ap);
+    va_start(ap, name);
+    status = attr_vprint(stream, ATTR_FLAG_NONE, ap);
     va_end(ap);
     if (status != 0
-       || mail_print(stream, "%s", MAIL_EOF) != 0
-       || vstream_fflush(stream) != 0
-       || mail_scan(stream, "%d", &status) != 1)
+       || attr_scan(stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+                    ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status, 0) != 1)
        status = -1;
     (void) vstream_fclose(stream);
     return (status);
similarity index 61%
rename from postfix/src/global/mail_command_read.c
rename to postfix/src/global/mail_command_server.c
index 74c7def61e9b948be4c7f1c49e0be4373fd6179c..9565b55fb4ca225e632d53c3c9c36c74ac7eabb3 100644 (file)
@@ -1,14 +1,15 @@
 /*++
 /* NAME
-/*     mail_command_read 3
+/*     mail_command_server 3
 /* SUMMARY
 /*     single-command server
 /* SYNOPSIS
 /*     #include <mail_proto.h>
 /*
-/*     int     mail_command_read(stream, format, ...)
+/*     int     mail_command_server(stream, type, name, ...)
 /*     VSTREAM *stream;
-/*     char    *format;
+/*     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
 /*     Arguments:
 /* .IP stream
 /*     Server endpoint.
-/* .IP format
-/*     Format string understood by mail_print(3) and mail_scan(3).
+/* .IP "type, name, ..."
+/*     Attribute list as defined in attr_scan(3).
 /* DIAGNOSTICS
 /*     Fatal: out of memory.
 /* SEE ALSO
-/*     mail_scan(3)
-/*     mail_command_write(3) client interface
+/*     attr_scan(3)
+/*     mail_command_client(3) client interface
+/*     mail_proto(3h), client-server protocol
+#include <mail_proto.h>
 /* LICENSE
 /* .ad
 /* .fi
 
 /* Utility library. */
 
-#include <vstring.h>
 #include <vstream.h>
 
 /* Global library. */
 
 #include "mail_proto.h"
 
-/* mail_command_read - read single-command request */
+/* mail_command_server - read single-command request */
 
-int     mail_command_read(VSTREAM *stream, char *fmt,...)
+int     mail_command_server(VSTREAM *stream,...)
 {
-    VSTRING *eof = vstring_alloc(10);
     va_list ap;
     int     count;
 
-    va_start(ap, fmt);
-    count = mail_vscan(stream, fmt, ap);
+    va_start(ap, stream);
+    count = attr_vscan(stream, ATTR_FLAG_MISSING, ap);
     va_end(ap);
-    if (mail_scan(stream, "%s", eof) != 1 || strcmp(vstring_str(eof), MAIL_EOF))
-       count = -1;
-    vstring_free(eof);
     return (count);
 }
index 36aef3c73d244e8e7291f81a23cbc5d5e886664e..21b8ef1850607b59a8e211808f552649179a4caa 100644 (file)
@@ -373,6 +373,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;
index 78b76c864ec569f549b12847264cfd8787369e3e..fe09056ad9445c5633219d1a574684c6bbdab182 100644 (file)
@@ -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
@@ -83,8 +84,8 @@ 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 *);
 
@@ -94,6 +95,31 @@ extern char *mail_pathname(const char *, const char *);
 extern int mail_vprint(VSTREAM *, const char *, va_list);
 extern int mail_vscan(VSTREAM *, const char *, va_list);
 
+ /*
+  * Attribute names.
+  */
+#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
 /* .fi
index e30537dfff69f02dbf0abd872603971936cf45e6..b624e7ff35535aa3d09d0ae8d6cf2873e59d0f15 100644 (file)
@@ -168,7 +168,8 @@ static int mail_stream_finish_ipc(MAIL_STREAM * info)
     /*
      * Receive the peer's completion status.
      */
-    if (mail_scan(info->stream, "%d", &status) != 1)
+    if (attr_scan(info->stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+                 ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status, 0) != 1)
        status = CLEANUP_STAT_WRITE;
 
     /*
@@ -218,7 +219,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 {
@@ -264,7 +266,8 @@ MAIL_STREAM *mail_stream_command(const char *command)
     }
     argv_free(export_env);
 
-    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 {
index 584ed2da153b5e32d1c43146403ebf0881d274b6..0a2b83bd487b21a859b04744639c41b13e53ffa7 100644 (file)
@@ -15,7 +15,7 @@
   * Version of this program.
   */
 #define VAR_MAIL_VERSION       "mail_version"
-#define DEF_MAIL_VERSION       "Snapshot-20011010"
+#define DEF_MAIL_VERSION       "Snapshot-20011014"
 extern char *var_mail_version;
 
 /* LICENSE
index 91f5a608ef9896848a2f198473e19dddfe843b90..52ed8716c057befabc01b8b83a77dcbaa9a9ade1 100644 (file)
@@ -139,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_MISSING | ATTR_FLAG_EXTRA,
+                 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);
 
     /*
@@ -232,7 +236,9 @@ 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_FLAG_EXTRA,
+                                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                                                ATTR_TYPE_END) != 1)
            status = CLEANUP_STAT_WRITE;
     }
     (void) vstream_fclose(cleanup);
index ff27504a627684f376961e20ab42dd7acecbd245..112e2866a5f04808a657522b7b8ae7670bde6a3e 100644 (file)
@@ -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_MISSING | ATTR_FLAG_EXTRA,
+                      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 {
index ba9049ad3b6d849f5a088ea078ec7e89daacc3ee..9c32c84f657bbfd399ea19eec86c87b0baaf6a28 100644 (file)
@@ -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_MISSING | ATTR_FLAG_EXTRA,
+                            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 {
index 23fffdf2ff36217a54bd84bfd05a8cd900979fce..b062de344a19390f2594dfb05d7eb6f9bb6f3735 100644 (file)
@@ -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;
@@ -112,6 +112,7 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command)
      * Do we permit mail to shell commands? Allow delivery via mailbox_command.
      */
     if (command != var_mailbox_command
+       && command != map_command               /* XXX */
        && (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"));
index 91f239348d620f171a133c2107f54cc315b95070..742eb478fe208db8f1ddeecfc3851d944e159116 100644 (file)
@@ -125,7 +125,9 @@ static FORWARD_INFO *forward_open(char *sender)
     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_MISSING | ATTR_FLAG_EXTRA,
+                 ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, buffer,
+                 ATTR_TYPE_END) != 1) {
        vstream_fclose(cleanup);
        return (0);
     }
@@ -133,7 +135,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 +237,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_FLAG_EXTRA,
+                        ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                        ATTR_TYPE_END) != 1)
            status = 1;
 
     /*
index 931b2c98f3dc37ac1e7067a0711765c6c2ab7fad..9cf8307f176d1844f72a0320f47fddadf0c3863f 100644 (file)
 /*     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 +449,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 +699,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,
     };
index 4843179e0d513fd0363765cb983837df5378568b..c37bbf1aa605169881b66b16557acbf18c2ce72f 100644 (file)
@@ -152,12 +152,14 @@ 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 *);
 extern int deliver_unknown(LOCAL_STATE, USER_ATTR);
 
+extern const char *map_command;                /* XXX */
+
  /*
   * Restrictions on delivery to sensitive destinations.
   */
index e49c9137ce15164654150ded543716d2adf18890..760b64306c52caef233a1bee6e683eb3133bebdf 100644 (file)
@@ -72,6 +72,7 @@
 #include <mail_params.h>
 #include <deliver_pass.h>
 #include <mbox_open.h>
+#include <maps.h>
 
 #ifndef EDQUOT
 #define EDQUOT EFBIG
@@ -85,6 +86,8 @@
 #define YES    1
 #define NO     0
 
+const char *map_command;               /* XXX */
+
 /* deliver_mailbox_file - deliver to recipient mailbox */
 
 static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
@@ -233,6 +236,7 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
     int     status;
     struct mypasswd *mbox_pwd;
     char   *path;
+    static MAPS *cmd_maps;
 
     /*
      * Make verbose logging easier to understand.
@@ -280,9 +284,21 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
      */
 #define LAST_CHAR(s) (s[strlen(s) - 1])
 
-    if (*var_mailbox_command)
+    if (*var_mailbox_cmd_maps) {
+       if (cmd_maps == 0)
+           cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps,
+                                  DICT_FLAG_LOCK);
+       if ((map_command = maps_find(cmd_maps, state.msg_attr.user,
+                                DICT_FLAG_FIXED)) != 0) {
+           status = deliver_command(state, usr_attr, map_command);
+       } else {
+           msg_warn("user %s not found in %s",
+                    state.msg_attr.user, var_mailbox_cmd_maps);
+           return (NO);
+       }
+    } 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);
index d8e513122669821e5b516c4f2896d2978fe31d38..b58910b41aa5abc8034317be7d453bd520caac86 100644 (file)
@@ -92,7 +92,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_MISSING | ATTR_FLAG_EXTRA,
+                        ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+                        ATTR_TYPE_END) != 1) {
        msg_warn("%s: malformed response", VSTREAM_PATH(stream));
        return (DELIVER_STAT_CRASH);
     } else {
@@ -109,7 +111,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_MISSING | ATTR_FLAG_EXTRA,
+                        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 {
@@ -127,6 +132,8 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
     char   *cp;
     VSTRING *sender_buf = 0;
     char   *sender;
+    int     flags;
+    char   *nexthop;
 
     /*
      * If variable envelope return path is requested, change prefix+@origin
@@ -137,7 +144,7 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
        sender = message->sender;
     } else {
        sender_buf = vstring_alloc(100);
-       verp_sender(sender_buf, message->verp_delims, 
+       verp_sender(sender_buf, message->verp_delims,
                    message->sender, list.info->address);
        sender = vstring_str(sender_buf);
     }
@@ -148,19 +155,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, 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_NUM, MAIL_ATTR_OFFSET, message->data_offset,
+              ATTR_TYPE_NUM, 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_NUM, 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_NUM, 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);
@@ -220,7 +240,7 @@ 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");
+       qmgr_transport_throttle(transport, "unknown mail transport error -- see a previous warning/fatal/panic record for the problem description");
        qmgr_defer_transport(transport, transport->reason);
     }
 
index 38d798e39cccfeedc6f385b521da72153349a1af..03b7df23b7e6c28374c3f03ec9dcf78b7e0e2a2d 100644 (file)
@@ -295,7 +295,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_FLAG_EXTRA,
+                 ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+                 ATTR_TYPE_END) != 1)
        return (cleanup_service_error(info, CLEANUP_STAT_WRITE));
 
     /*
@@ -365,8 +367,12 @@ static int pickup_file(PICKUP_INFO *info)
 
     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) {
+    if (attr_scan(cleanup, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+                 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));
index 3ed603451d9101d9732986415f933d59f8b2c32b..8a8e974ea73ff4dc52031bad0ac3d7ceb9af9e28 100644 (file)
@@ -241,7 +241,9 @@ int     main(int argc, char **argv)
      */
     dst = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC,
                           MAIL_SERVICE_PICKUP);
-    mail_print(VSTREAM_OUT, "%s", dst->id);
+    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));
 
@@ -302,7 +304,9 @@ 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_END);
     vstream_fflush(VSTREAM_OUT);
     exit(status);
 }
index 3614690cae0405580cb462f70ab500780db9fcb6..2b4e613b98ac3b6c64904f7818cb8c56fc497ea4 100644 (file)
@@ -87,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_MISSING | ATTR_FLAG_EXTRA,
+                        ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
+                        ATTR_TYPE_END) != 1) {
        msg_warn("%s: malformed response", VSTREAM_PATH(stream));
        return (DELIVER_STAT_CRASH);
     } else {
@@ -104,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_MISSING | ATTR_FLAG_EXTRA,
+                        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 {
@@ -122,6 +127,8 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
     char   *cp;
     VSTRING *sender_buf = 0;
     char   *sender;
+    int     flags;
+    char   *nexthop;
 
     /*
      * If variable envelope return path is requested, change prefix+@origin
@@ -132,7 +139,7 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
        sender = message->sender;
     } else {
        sender_buf = vstring_alloc(100);
-       verp_sender(sender_buf, message->verp_delims, 
+       verp_sender(sender_buf, message->verp_delims,
                    message->sender, list.info->address);
        sender = vstring_str(sender_buf);
     }
@@ -143,19 +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, 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_NUM, MAIL_ATTR_OFFSET, message->data_offset,
+              ATTR_TYPE_NUM, 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_NUM, 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_NUM, 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);
@@ -215,7 +235,7 @@ 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");
+       qmgr_transport_throttle(transport, "unknown mail transport error -- see a previous warning/fatal/panic record for the problem description");
        qmgr_defer_transport(transport, transport->reason);
     }
 
index 5117d399cd02e9eaada382322ac73f0d37366670..85a12284787a87041515e86fa99c54de2121cd08 100644 (file)
@@ -181,7 +181,9 @@ static void qmqpd_open_file(QMQPD_STATE *state)
      */
     state->dest = mail_stream_service(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP);
     if (state->dest == 0
-       || mail_print(state->dest->stream, "%d", CLEANUP_FLAG_FILTER) != 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_PRIVATE, MAIL_SERVICE_CLEANUP);
     state->cleanup = state->dest->stream;
index 58fcdbe29507c1e53e2deb144d0e3ff563dbfa12..67f61bead533f07944c45d43d298e8f01853bdb9 100644 (file)
@@ -515,7 +515,9 @@ static void mail_open_stream(SMTPD_STATE *state)
        state->dest = mail_stream_service(MAIL_CLASS_PRIVATE,
                                          MAIL_SERVICE_CLEANUP);
        if (state->dest == 0
-        || mail_print(state->dest->stream, "%d", CLEANUP_FLAG_FILTER) != 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_PRIVATE, MAIL_SERVICE_CLEANUP);
     }
index bd99fa61ae3c0584c6ec13b191853e2ebc98cd1e..0ad2931b91b9a33638ab42013461d5396f37ea36 100644 (file)
@@ -257,7 +257,9 @@ int     resolve_proto(VSTREAM *stream)
 {
     int     flags;
 
-    if (mail_scan(stream, "%s", query) != 1)
+    if (attr_scan(stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+                 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");
index 80ab11c252b4d8a57dda9e28230e04143b4c878e..1e165b7b77252cb9c20d7a2266d1c4600dd3dd2c 100644 (file)
@@ -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_MISSING | ATTR_FLAG_EXTRA,
+                 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");
index 61624ada5e49942ff501c43ff656d48ae996fbbe..c753c00e8e40398fbc1d9940cbcc2cd9dca61804 100644 (file)
@@ -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)
@@ -181,7 +181,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_MISSING | ATTR_FLAG_EXTRA | 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) {
index bf8ecebdaac701929bab998e7fbe6b5474cf0b4d..f3fab48e7b76d59fa162388fa970225675cf67c8 100644 (file)
@@ -24,7 +24,7 @@ SRCS  = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \
        sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
        hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
        sane_socketpair.c myrand.c netstring.c ctable.c attr_print.c intv.c \
-       attr_scan.c
+       attr_scan.c attr_table.c
 OBJS   = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
        close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
        dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
@@ -50,7 +50,7 @@ OBJS  = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
        sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
        hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
        sane_socketpair.o myrand.o netstring.o ctable.o attr_print.o intv.o \
-       attr_scan.o
+       attr_scan.o attr_table.o
 HDRS   = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
        dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
        dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
@@ -67,7 +67,7 @@ HDRS  = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
        dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \
        watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \
        sane_time.h sane_socketpair.h myrand.h netstring.h ctable.h \
-       attr_io.h intv.h
+       intv.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 WARN   = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -84,7 +84,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        mystrtok sigdelay translit valid_hostname vstream_popen \
        vstring vstring_vstream doze select_bug stream_test mac_expand \
        watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \
-       inet_addr_list attr_print attr_scan
+       inet_addr_list attr_print attr_scan attr_table
 
 LIB_DIR        = ../../lib
 INC_DIR        = ../../include
@@ -298,6 +298,11 @@ attr_scan: $(LIB) $@.o
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
+attr_table: $(LIB) $@.o
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
        set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -369,24 +374,43 @@ argv_split.o: vbuf.h
 argv_split.o: argv.h
 attr.o: attr.c
 attr.o: sys_defs.h
+attr.o: msg.h
 attr.o: mymalloc.h
 attr.o: htable.h
 attr.o: attr.h
+attr.o: vstream.h
+attr.o: vbuf.h
 attr_print.o: attr_print.c
 attr_print.o: sys_defs.h
+attr_print.o: msg.h
+attr_print.o: mymalloc.h
 attr_print.o: vstream.h
 attr_print.o: vbuf.h
-attr_print.o: msg.h
-attr_print.o: attr_io.h
+attr_print.o: htable.h
+attr_print.o: attr.h
 attr_scan.o: attr_scan.c
 attr_scan.o: sys_defs.h
+attr_scan.o: msg.h
+attr_scan.o: mymalloc.h
 attr_scan.o: vstream.h
 attr_scan.o: vbuf.h
 attr_scan.o: vstring.h
-attr_scan.o: msg.h
 attr_scan.o: argv.h
 attr_scan.o: intv.h
-attr_scan.o: attr_io.h
+attr_scan.o: attr.h
+attr_scan.o: htable.h
+attr_table.o: attr_table.c
+attr_table.o: sys_defs.h
+attr_table.o: msg.h
+attr_table.o: htable.h
+attr_table.o: mymalloc.h
+attr_table.o: vstring.h
+attr_table.o: vbuf.h
+attr_table.o: vstream.h
+attr_table.o: vstring_vstream.h
+attr_table.o: argv.h
+attr_table.o: intv.h
+attr_table.o: attr.h
 basename.o: basename.c
 basename.o: sys_defs.h
 basename.o: stringops.h
index 30327454380bd46ba99e938776ac79dad992b54a..a8f9aa3b323a9ad109eeeb8f022bab0c6a8bfc49 100644 (file)
@@ -2,38 +2,60 @@
 /* NAME
 /*     attr 3
 /* SUMMARY
-/*     attribute list manager
+/*     simple attribute list manager
 /* SYNOPSIS
-/*     #include <htable.h>
 /*     #include <attr.h>
 /*
-/*     void    attr_enter(attr, name, value)
-/*     HTABLE  *attr;
+/*     void    attr_enter(table, flags, name, value, ..., (char *) 0)
+/*     HTABLE  *table;
+/*     int     flags;
 /*     const char *name;
 /*     const char *value;
 /*
-/*     const char *attr_find(attr, name)
-/*     HTABLE  *attr;
+/*     int     attr_find(table, flags, name, value, ..., (char *) 0)
+/*     HTABLE  *table;
+/*     int     flags;
 /*     const char *name;
-/*
-/*     void    attr_free(attr)
-/*     HTABLE  *attr;
+/*     const char **vptr;
 /* DESCRIPTION
 /*     This module maintains open attribute lists of string-valued
 /*     names and values. The module is built on top of the generic
 /*     htable(3) hash table manager.
 /*
-/*     attr_enter() adds a new attribute or updates an existing one.
-/*     Both the name and the value are copied.
+/*     attr_enter() adds or updates zero or more attribute-value pairs.
+/*     Both the name and the value are copied. The argument list is
+/*     terminated with a null character pointer.
 /*
-/*     attr_find() looks up the named attribute. It returns the
-/*     corresponding value if one is found, a null pointer otherwise.
+/*     attr_find() looks up zero or more named attribute values. The
+/*     result value is the number of attributes found; the search
+/*     stops at the first error. The values are not copied out; it
+/*     is up to the caller to save copies of values where needed.
 /*
-/*     attr_free() destroys the named attribute list and makes its
-/*     memory available for reuse.
-/* BUGS
-/*     This module cannot store null pointer values. If that is a
-/*     problem, use the raw hash table management routines instead.
+/*     Arguments:
+/* .IP table
+/*     Pointer to hash table.
+/* .IP flags
+/*     Bit-wise OR of the following:
+/* .RS
+/* .IP ATTR_FLAG_MISSING
+/*      Log a warning when attr_find() cannot find an attribute. The
+/*     search always terminates when a request cannot be satisfied.
+/* .IP ATTR_FLAG_EXTRA
+/*      Log a warning and return immediately when attr_enter() finds
+/*     that a specified attribute already exists in the table.
+/*      By default, attr_enter() replaces an existing attribute value
+/*     by the specified one.
+/* .IP ATTR_FLAG_NONE
+/*      For convenience, this value requests none of the above.
+/* .RE
+/* .IP name
+/*     Attribute name. Specify a null character pointer to terminate
+/*     the argument list.
+/* .IP value
+/*     Attribute value. attr_enter() makes a copy.
+/* .IP vptr
+/*     Pointer to character pointer. attr_find() makes no copy of the
+/*     value.
 /* LICENSE
 /* .ad
 /* .fi
 
 /* System library. */
 
-#include "sys_defs.h"
+#include <sys_defs.h>
+#include <stdarg.h>
 
 /* Utility library. */
 
-#include "mymalloc.h"
-#include "htable.h"
-#include "attr.h"
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <attr.h>
 
-/* attr_enter - add or replace attribute */
+/* attr_enter - add or update attribute values */
 
-void    attr_enter(HTABLE *attr, const char *name, const char *value)
+void    attr_enter(HTABLE *table, int flags,...)
 {
+    const char *myname = "attr_enter";
     HTABLE_INFO *ht;
+    va_list ap;
+    const char *name;
+    const char *value;
 
-    if ((ht = htable_locate(attr, name)) != 0) {/* replace attribute */
-       myfree(ht->value);
-       ht->value = mystrdup(value);
-    } else {                                   /* add attribute */
-       (void) htable_enter(attr, name, mystrdup(value));
+    va_start(ap, flags);
+    while ((name = va_arg(ap, char *)) != 0) {
+       value = va_arg(ap, char *);
+       if ((ht = htable_locate(table, name)) != 0) {   /* replace */
+           if ((flags & ATTR_FLAG_EXTRA) != 0) {
+               msg_warn("%s: duplicate attribute %s in table", myname, name);
+               break;
+           }
+           myfree(ht->value);
+           ht->value = mystrdup(value);
+       } else {                                /* add attribute */
+           (void) htable_enter(table, name, mystrdup(value));
+       }
     }
+    va_end(ap);
 }
 
-/* attr_free - destroy attribute list */
+/* attr_enter - look up attribute values */
 
-void    attr_free(HTABLE *attr)
+int     attr_find(HTABLE *table, int flags,...)
 {
-    htable_free(attr, myfree);
-}
+    const char *myname = "attr_find";
+    va_list ap;
+    const char *name;
+    const char **vptr;
+    int     attr_count;
 
+    va_start(ap, flags);
+    for (attr_count = 0; (name = va_arg(ap, char *)) != 0; attr_count++) {
+       vptr = va_arg(ap, const char **);
+       if ((*vptr = htable_find(table, name)) == 0) {
+           if ((flags & ATTR_FLAG_MISSING) != 0)
+               msg_warn("%s: missing attribute %s in table", myname, name);
+           break;
+       }
+    }
+    return (attr_count);
+}
index e81381ed3374a4bb98296851c248ecc360f5a4e9..4e5febec50a4f6a898f89e7017b1e622ff770b6e 100644 (file)
@@ -1,23 +1,87 @@
-#ifndef _ATTR_H_INCLUDED_
-#define _ATTR_H_INCLUDED_
+#ifndef _ATTR_PRINT_H_INCLUDED_
+#define _ATTR_PRINT_H_INCLUDED_
 
 /*++
 /* NAME
 /*     attr 3h
 /* SUMMARY
-/*     attribute list manager
+/*     attribute list manipulations
 /* SYNOPSIS
-/*     #include <attr.h>
-/* DESCRIPTION
-/* .nf
+/*     #include "attr.h"
+ DESCRIPTION
+ .nf
+
+ /*
+  * System library.
+  */
+#include <stdarg.h>
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+#include <htable.h>
 
  /*
   * External interface.
   */
-extern void attr_enter(HTABLE *, const char *, const char *);
-extern void attr_free(HTABLE *);
+#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_NUM_ARRAY    3       /* Unsigned integer sequence */
+#define ATTR_TYPE_STR_ARRAY    4       /* Character string sequence */
+#define ATTR_TYPE_HASH         5       /* Hash table */
+
+#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_ALL          (07)
+
+ /*
+  * attr_print.c.
+  */
+extern int attr_print(VSTREAM *, int,...);
+extern int attr_vprint(VSTREAM *, int, va_list);
+
+ /*
+  * attr_scan.c.
+  */
+extern int attr_scan(VSTREAM *, int,...);
+extern int attr_vscan(VSTREAM *, int, va_list);
 
-#define attr_find(table, name) htable_find((table), (name))
+ /*
+  * attr_table.c.
+  */
+typedef HTABLE ATTR_TABLE;
+
+extern ATTR_TABLE *attr_table_create(int);
+extern void attr_table_free(ATTR_TABLE *);
+extern int attr_table_read(ATTR_TABLE *, int, VSTREAM *);
+extern int attr_table_get(ATTR_TABLE *, int,...);
+extern int attr_table_vget(ATTR_TABLE *, int, va_list);
+
+ /*
+  * attr.c.
+  */
+extern void attr_enter(HTABLE *, int,...);
+extern int attr_find(HTABLE *, int,...);
+
+ /*
+  * 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_NUM_ARRAY    "number_array"
+#define ATTR_NAME_STR_ARRAY    "string_array"
+#endif
+
+ /*
+  * Testing.
+  */
+#define BASE64_DECODE(buf, str, len)   vstring_strncpy((buf), (str), (len))
 
 /* LICENSE
 /* .ad
diff --git a/postfix/src/util/attr_io.h b/postfix/src/util/attr_io.h
deleted file mode 100644 (file)
index 44c0bd3..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef _ATTR_PRINT_H_INCLUDED_
-#define _ATTR_PRINT_H_INCLUDED_
-
-/*++
-/* NAME
-/*     attr_type 3h
-/* SUMMARY
-/*     attribute list I/O
-/* SYNOPSIS
-/*     #include "attr_type.h"
- DESCRIPTION
- .nf
-
- /*
-  * System library.
-  */
-#include <stdarg.h>
-
- /*
-  * Utility library.
-  */
-#include <vstream.h>
-
- /*
-  * External interface.
-  */
-#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_NUM_ARRAY    3       /* Unsigned integer sequence */
-#define ATTR_TYPE_STR_ARRAY    4       /* Character string sequence */
-
-#define ATTR_FLAG_MISSING      (1<<0)  /* Flag missing attribute */
-#define ATTR_FLAG_EXTRA                (1<<1)  /* Flag spurious attribute */
-
-extern int attr_print(VSTREAM *,...);
-extern int attr_vprint(VSTREAM *, va_list);
-extern int attr_scan(VSTREAM *, int,...);
-extern int attr_vscan(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_NUM_ARRAY    "number_array"
-#define ATTR_NAME_STR_ARRAY    "string_array"
-#endif
-
-/* 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
index 226a5cbf5fcaf24d4407894b59440971b22eceb8..15fecbcacc42a4e4f87409a0d79b7065cfa16353 100644 (file)
@@ -4,18 +4,27 @@
 /* SUMMARY
 /*     send attributes over byte stream
 /* SYNOPSIS
-/*     #include <attr_io.h>
+/*     #include <attr.h>
 /*
-/*     int     attr_print(fp, type, name, ...)
+/*     int     attr_print(fp, flags, type, name, ...)
 /*     VSTREAM fp;
+/*     int     flags;
 /*     int     type;
 /*     char    *name;
+/*
+/*     int     attr_vprint(fp, flags, ap)
+/*     VSTREAM fp;
+/*     int     flags;
+/*     va_list ap;
 /* DESCRIPTION
 /*     attr_print() 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_scan(). The stream
 /*     is not flushed.
 /*
+/*     attr_vprint() 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_print() argument list. This routine satisfies the formatting
 /*     rules as outlined in attr_scan(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_print() 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
@@ -38,6 +57,9 @@
 /*     This argument is followed by an attribute name, an integer array
 /*     element count, and a pointer to a null-terminated array of
 /*     null-terminated strings.
+/* .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
 
 /* Utility library. */
 
-#include <vstream.h>
 #include <msg.h>
-#include <attr_io.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <htable.h>
+#include <attr.h>
 
 /* attr_fprintf - encode attribute information on the fly */
 
@@ -83,7 +107,7 @@ static void PRINTFLIKE(2, 3) attr_fprintf(VSTREAM *fp, const char *format,...)
 
 /* attr_vprint - send attribute list to stream */
 
-int     attr_vprint(VSTREAM *fp, va_list ap)
+int     attr_vprint(VSTREAM *fp, int flags, va_list ap)
 {
     const char *myname = "attr_print";
     int     attr_type;
@@ -94,53 +118,89 @@ int     attr_vprint(VSTREAM *fp, va_list ap)
     unsigned *ip_val;
     int     count_val;
     int     i;
+    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) {
-       attr_name = va_arg(ap, char *);
-       attr_fprintf(fp, "%s", attr_name);
        switch (attr_type) {
        case ATTR_TYPE_NUM:
+           attr_name = va_arg(ap, char *);
+           attr_fprintf(fp, "%s", attr_name);
            int_val = va_arg(ap, int);
            attr_fprintf(fp, ":%u", (unsigned) int_val);
+           if (msg_verbose)
+               msg_info("send attr name %s value %u", attr_name, int_val);
            break;
        case ATTR_TYPE_STR:
+           attr_name = va_arg(ap, char *);
+           attr_fprintf(fp, "%s", attr_name);
            str_val = va_arg(ap, char *);
            attr_fprintf(fp, ":%s", str_val);
+           if (msg_verbose)
+               msg_info("send attr name %s value %s", attr_name, str_val);
            break;
        case ATTR_TYPE_NUM_ARRAY:
+           attr_name = va_arg(ap, char *);
+           attr_fprintf(fp, "%s", attr_name);
            ip_val = va_arg(ap, int *);
            count_val = va_arg(ap, int);
            for (i = 0; i < count_val; i++)
                attr_fprintf(fp, ":%u", (unsigned) *ip_val++);
+           if (msg_verbose)
+               msg_info("send attr name %s values %d", attr_name, count_val);
            break;
        case ATTR_TYPE_STR_ARRAY:
+           attr_name = va_arg(ap, char *);
+           attr_fprintf(fp, "%s", attr_name);
            cpp_val = va_arg(ap, char **);
            count_val = va_arg(ap, int);
            for (i = 0; i < count_val; i++) {
                str_val = *cpp_val++;
                attr_fprintf(fp, ":%s", str_val);
            }
+           if (msg_verbose)
+               msg_info("send attr name %s values %d", attr_name, count_val);
+           break;
+       case ATTR_TYPE_HASH:
+           ht_info_list = htable_list(va_arg(ap, HTABLE *));
+           for (ht = ht_info_list; *ht; ht++) {
+               attr_fprintf(fp, "%s:%s", ht[0]->key, ht[0]->value);
+               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);
     }
-    VSTREAM_PUTC('\n', fp);
+    if ((flags & ATTR_FLAG_MORE) == 0)
+       VSTREAM_PUTC('\n', fp);
     return (vstream_ferror(fp));
 }
 
-int     attr_print(VSTREAM *fp,...)
+int     attr_print(VSTREAM *fp, int flags,...)
 {
     va_list ap;
     int     ret;
 
-    va_start(ap, fp);
-    ret = attr_vprint(fp, ap);
+    va_start(ap, flags);
+    ret = attr_vprint(fp, flags, ap);
     va_end(ap);
     return (ret);
 }
@@ -157,9 +217,21 @@ int     main(int argc, char **argv)
 {
     static int int_array[] = {0, 1, 2, 3, 4, 5, 6, 7};
     static char *str_array[] = {"a", "b", "c", "d", "e", "f", "g", "h"};
+    HTABLE *table = htable_create(1);
 
     msg_vstream_init(argv[0], VSTREAM_ERR);
-    attr_print(VSTREAM_OUT,
+    htable_enter(table, "foo-name", "foo-value");
+    htable_enter(table, "bar-name", "bar-value");
+    attr_print(VSTREAM_OUT, ATTR_FLAG_NONE,
+              ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+              ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+              ATTR_TYPE_NUM_ARRAY, ATTR_NAME_NUM_ARRAY,
+              int_array, sizeof(int_array) / sizeof(int_array[0]),
+              ATTR_TYPE_STR_ARRAY, ATTR_NAME_STR_ARRAY,
+              str_array, sizeof(str_array) / sizeof(str_array[0]),
+              ATTR_TYPE_HASH, table,
+              ATTR_TYPE_END);
+    attr_print(VSTREAM_OUT, ATTR_FLAG_NONE,
               ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
               ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
               ATTR_TYPE_NUM_ARRAY, ATTR_NAME_NUM_ARRAY,
index c40d906650977bd8dce9f7bc2a606efc25f18f7b..392fee766d6b3fff62294a3f1177fd7640b37e5b 100644 (file)
@@ -4,18 +4,26 @@
 /* SUMMARY
 /*     recover attributes from byte stream
 /* SYNOPSIS
-/*     #include <attr_io.h>
+/*     #include <attr.h>
 /*
 /*     int     attr_scan(fp, flags, type, name, ...)
 /*     VSTREAM fp;
 /*     int     flags;
 /*     int     type;
 /*     char    *name;
+/*
+/*     int     attr_vscan(fp, flags, ap)
+/*     VSTREAM fp;
+/*     int     flags;
+/*     va_list ap;
 /* DESCRIPTION
 /*     attr_scan() takes zero or more (name, value) scalar or array
 /*     attribute arguments, and recovers the attribute values from the
 /*     byte stream that was generated by attr_print().
 /*
+/*     attr_vscan() 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:
 /*     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.
+/*     contains an attribute that was not requested. This includes multiple
+/*     instances of the same attribute when only one instance is requested.
+/* .IP ATTR_FLAG_MORE
+/*     After recovering the requested attributes, leave the input stream
+/*     in a state that is usable for more recovery operations on the
+/*     same input attribute list.
+/*     By default, attr_scan() skips forward past the attribute list
+/*     terminator.
+/* .IP ATTR_FLAG_NONE
+/*     For convenience, this value requests none of the above.
 /* .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 pointer.
-/*     This is used for recovering an integer attribute value.
+/*     This is used for recovering one simple integer attribute value.
 /* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
 /*     This argument is followed by an attribute name and a VSTRING pointer.
-/*     This is used for recovering a string attribute value.
+/*     This is used for recovering one simple string attribute value.
 /* .IP "ATTR_TYPE_NUM_ARRAY (char *, INTV *)"
 /*     This argument is followed by an attribute name and an INTV pointer.
 /*     This is used for recovering an integer array attribute value.
+/*     Values from the input stream are appended to the array.
 /* .IP "ATTR_TYPE_NUM_ARRAY (char *, ARGV *)"
 /*     This argument is followed by an attribute name and an ARGV pointer.
 /*     This is used for recovering a string array attribute value.
+/*     Values from the input stream are appended to the array.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/*     All further attributes are stored into the given hash table as simple
+/*     string-valued attributes, under keys equal to the attribute name.
+/*     Values from the input stream are added to the hash table, but existing
+/*     hash table entries are not replaced.
+/* .sp
+/*     N.B. This must be followed by an ATTR_TYPE_END argument.
 /* .IP ATTR_TYPE_END
 /*     This terminates the requested attribute list.
 /* .RE
 /* DIAGNOSTICS
 /*     The result value is the number of attributes that were successfully
 /*     recovered from the input stream (an array-valued attribute counts
-/*     as one attribute).
+/*     as one attribute; a hash table counts as the number of entries in
+/*     the table).
 /*
 /*     Panic: interface violation. All system call errors are fatal.
 /* SEE ALSO
 
 /* Utility library. */
 
+#include <msg.h>
+#include <mymalloc.h>
 #include <vstream.h>
 #include <vstring.h>
-#include <msg.h>
 #include <argv.h>
 #include <intv.h>
-#include <attr_io.h>
+#include <attr.h>
 
 /* Application specific. */
 
-extern int var_line_limit;             /* XXX */
-
 #define STR(x) vstring_str(x)
 #define LEN(x) VSTRING_LEN(x)
 
@@ -128,7 +154,8 @@ extern int var_line_limit;          /* XXX */
 
 static int attr_scan_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
 {
-    VSTRING *base64_buf = 0;
+    static VSTRING *base64_buf = 0;
+    extern int var_line_limit;         /* XXX */
     int     limit = var_line_limit * 5 / 4;
     int     ch;
 
@@ -142,15 +169,19 @@ static int attr_scan_string(VSTREAM *fp, VSTRING *plain_buf, const char *context
                     VSTREAM_PATH(fp), context);
            return (-1);
        }
+       VSTRING_ADDCH(base64_buf, ch);
        if (LEN(base64_buf) > limit) {
            msg_warn("string length > %d characters from %s while reading %s",
                     limit, VSTREAM_PATH(fp), context);
            return (-1);
        }
-       VSTRING_ADDCH(base64_buf, ch);
     }
     VSTRING_TERMINATE(base64_buf);
-    vstring_strcpy(plain_buf, STR(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));
     return (ch);
@@ -180,35 +211,62 @@ int     attr_vscan(VSTREAM *fp, int flags, va_list ap)
 {
     const char *myname = "attr_scan";
     static VSTRING *str_buf = 0;
-    int     wanted_type;
+    static VSTRING *name_buf = 0;
+    int     wanted_type = -1;
     char   *wanted_name;
     int    *number;
     VSTRING *string;
     INTV   *number_array;
     ARGV   *string_array;
+    HTABLE *hash_table;
     unsigned num_val;
     int     ch;
-    int     conversions = 0;
+    int     conversions;
+
+    /*
+     * Sanity check.
+     */
+    if (flags & ~ATTR_FLAG_ALL)
+       msg_panic("%s: bad flags: 0x%x",
+                 myname, flags);
 
     /*
      * Initialize.
      */
-    if (str_buf == 0)
+    if (str_buf == 0) {
        str_buf = vstring_alloc(10);
+       name_buf = vstring_alloc(10);
+    }
 
     /*
      * Iterate over all (type, name, value) triples.
      */
-    for (;;) {
+    for (conversions = 0; /* void */ ; conversions++) {
 
        /*
-        * Determine the next attribute name on the caller's wish list.
+        * 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 explicitly named attributes.
         */
-       wanted_type = va_arg(ap, int);
-       if (wanted_type == ATTR_TYPE_END) {
-           wanted_name = "attribute list terminator";
-       } else {
-           wanted_name = va_arg(ap, char *);
+       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 = "attribute list terminator";
+           } else if (wanted_type == ATTR_TYPE_HASH) {
+               wanted_name = "any attribute name";
+               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 *);
+           }
        }
 
        /*
@@ -221,30 +279,32 @@ int     attr_vscan(VSTREAM *fp, int flags, va_list ap)
             * early is OK if the caller is prepared to deal with missing
             * inputs.
             */
-           if ((ch = attr_scan_string(fp, str_buf,
+           if ((ch = attr_scan_string(fp, name_buf,
                                       "attribute name")) == VSTREAM_EOF)
                return (conversions);
-           if (ch == '\n' && LEN(str_buf) == 0) {
+           if (ch == '\n' && LEN(name_buf) == 0) {
                if (wanted_type == ATTR_TYPE_END
-                   || (flags & ATTR_FLAG_MISSING) == 0)
+                   || wanted_type == ATTR_TYPE_HASH)
                    return (conversions);
-               msg_warn("missing attribute %s in input from %s",
-                        wanted_name, VSTREAM_PATH(fp));
+               if ((flags & ATTR_FLAG_MISSING) != 0)
+                   msg_warn("missing attribute %s in input from %s",
+                            wanted_name, VSTREAM_PATH(fp));
                return (conversions);
            }
            if (msg_verbose)
                msg_info("want attribute %s, found attribute: %s",
-                        wanted_name, STR(str_buf));
+                        wanted_name, STR(name_buf));
 
            /*
             * See if the caller asks for this attribute.
             */
-           if (wanted_type != ATTR_TYPE_END
-               && strcmp(wanted_name, STR(str_buf)) == 0)
+           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(str_buf), VSTREAM_PATH(fp));
+                        STR(name_buf), VSTREAM_PATH(fp));
                return (conversions);
            }
 
@@ -257,32 +317,68 @@ int     attr_vscan(VSTREAM *fp, int flags, va_list ap)
 
        /*
         * Do the requested conversion. If the target attribute is a
-        * non-array type, do not allow the sender to send a multi-valued
-        * attribute. If the target attribute is an array type, allow the
-        * sender to send a zero-element array.
+        * 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 (conversions);
+           }
            number = va_arg(ap, int *);
            if ((ch = attr_scan_number(fp, number, str_buf,
                                       "attribute value")) < 0)
                return (conversions);
            if (ch != '\n') {
-               msg_warn("too many values for attribute %s from %s",
-                        wanted_name, VSTREAM_PATH(fp));
+               msg_warn("too many values for number attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
                return (conversions);
            }
            break;
        case ATTR_TYPE_STR:
+           if (ch != ':') {
+               msg_warn("missing value for string attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (conversions);
+           }
            string = va_arg(ap, VSTRING *);
            if ((ch = attr_scan_string(fp, string, "attribute value")) < 0)
                return (conversions);
            if (ch != '\n') {
-               msg_warn("too many values for attribute %s from %s",
-                        wanted_name, VSTREAM_PATH(fp));
+               msg_warn("too many values for string attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
                return (conversions);
            }
            break;
+       case ATTR_TYPE_HASH:
+           if (ch != ':') {
+               msg_warn("missing value for string attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (conversions);
+           }
+           if ((ch = attr_scan_string(fp, str_buf, "attribute value")) < 0)
+               return (conversions);
+           if (ch != '\n') {
+               msg_warn("too many values for string attribute %s from %s",
+                        STR(name_buf), VSTREAM_PATH(fp));
+               return (conversions);
+           }
+           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;
        case ATTR_TYPE_NUM_ARRAY:
            number_array = va_arg(ap, INTV *);
            while (ch != '\n') {
@@ -303,7 +399,6 @@ int     attr_vscan(VSTREAM *fp, int flags, va_list ap)
        default:
            msg_panic("%s: unknown type code: %d", myname, wanted_type);
        }
-       conversions++;
     }
 }
 
@@ -335,12 +430,40 @@ int     main(int unused_argc, char **used_argv)
     INTV   *intv = intv_alloc(1);
     ARGV   *argv = argv_alloc(1);
     VSTRING *str_val = vstring_alloc(1);
+    HTABLE *table = htable_create(1);
+    HTABLE_INFO **ht_info_list;
+    HTABLE_INFO **ht;
     int     int_val;
     int     ret;
     int     i;
 
     msg_verbose = 1;
     msg_vstream_init(used_argv[0], VSTREAM_ERR);
+    if ((ret = attr_scan(VSTREAM_IN,
+                        ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+                        ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+                        ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+                        ATTR_TYPE_NUM_ARRAY, ATTR_NAME_NUM_ARRAY, intv,
+                        ATTR_TYPE_STR_ARRAY, ATTR_NAME_STR_ARRAY, argv,
+                        ATTR_TYPE_HASH, table,
+                        ATTR_TYPE_END)) > 4) {
+       vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+       vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+       vstream_printf("%s", ATTR_NAME_NUM_ARRAY);
+       for (i = 0; i < intv->intc; i++)
+           vstream_printf(" %d", intv->intv[i]);
+       vstream_printf("\n");
+       vstream_printf("%s", ATTR_NAME_STR_ARRAY);
+       for (i = 0; i < argv->argc; i++)
+           vstream_printf(" %s", argv->argv[i]);
+       vstream_printf("\n");
+       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_scan(VSTREAM_IN,
                         ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
                         ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
@@ -358,11 +481,21 @@ int     main(int unused_argc, char **used_argv)
        for (i = 0; i < argv->argc; i++)
            vstream_printf(" %s", argv->argv[i]);
        vstream_printf("\n");
+       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");
+
+    intv_free(intv);
+    argv_free(argv);
+    vstring_free(str_val);
+    htable_free(table, myfree);
+
     return (0);
 }
 
diff --git a/postfix/src/util/attr_table.c b/postfix/src/util/attr_table.c
new file mode 100644 (file)
index 0000000..2d69c81
--- /dev/null
@@ -0,0 +1,439 @@
+/*++
+/* NAME
+/*     attr_table 3
+/* SUMMARY
+/*     recover attributes from byte stream
+/* SYNOPSIS
+/*     #include <attr.h>
+/*
+/*     ATTR_TABLE *attr_table_create(size)
+/*     int     size;
+/*
+/*     void    attr_table_free(attr)
+/*     ATTR_TABLE *attr;
+/*
+/*     int     attr_table_read(attr, flags, fp)
+/*     ATTR_TABLE *attr;
+/*     int     flags;
+/*     VSTREAM fp;
+/*
+/*     int     attr_table_get(attr, flags, type, name, ...)
+/*     ATTR_TABLE *attr;
+/*     int     flags;
+/*     int     type;
+/*     char    *name;
+/* DESCRIPTION
+/*     This module provides an alternative process for recovering
+/*     attribute lists from a byte stream. The process involves the
+/*     storage in an intermediate attribute table that is subsequently
+/*     queried. This procedure gives more control to the application,
+/*     at the cost of complexity and of memory resources.
+/*
+/*     attr_table_create() creates an empty table for storage of
+/*     the intermediate result from attr_table_read().
+/*
+/*     attr_table_free() destroys its argument.
+/*
+/*     attr_table_read() reads an attribute list from a byte stream
+/*     and stores the intermediate result into a table that can be
+/*     queried with attr_table_get().
+/*
+/*     attr_table_get() takes zero or more (name, value) scalar or array
+/*     attribute arguments, and recovers the attribute values from the
+/*     intermediate attribute table produced by attr_table_read().
+/*
+/*     Arguments:
+/* .IP fp
+/*     Stream to recover the 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.
+/*     This flag has no effect with attr_table_read().
+/* .IP ATTR_FLAG_EXTRA
+/*     Log a warning and stop attribute recovery when the input stream
+/*     contains multiple instances of an attribute.
+/*     This flag has no effect with attr_table_get().
+/* .IP ATTR_FLAG_NONE
+/*     For convenience, this value requests none of the above.
+/* .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 pointer.
+/*     This is used for recovering an integer attribute value.
+/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
+/*     This argument is followed by an attribute name and a VSTRING pointer.
+/*     This is used for recovering a string attribute value.
+/* .IP "ATTR_TYPE_NUM_ARRAY (char *, INTV *)"
+/*     This argument is followed by an attribute name and an INTV pointer.
+/*     This is used for recovering an integer array attribute value.
+/* .IP "ATTR_TYPE_NUM_ARRAY (char *, ARGV *)"
+/*     This argument is followed by an attribute name and an ARGV pointer.
+/*     This is used for recovering a string array attribute value.
+/* .IP ATTR_TYPE_END
+/*     This terminates the requested attribute list.
+/* .RE
+/* DIAGNOSTICS
+/*     The result value from attr_table_read() and from attr_table_get()
+/*     is the number of attributes that were successfully recovered from
+/*     the input stream (an array-valued attribute counts as one attribute).
+/*
+/*     Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/*     attr_print(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 <htable.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <argv.h>
+#include <intv.h>
+#include <attr.h>
+
+/* Application-specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+static VSTRING *base64_buf;
+static VSTRING *plain_buf;
+
+/* attr_table_create - create attribute table */
+
+ATTR_TABLE *attr_table_create(int size)
+{
+    return (htable_create(size));
+}
+
+/* attr_table_free - destroy attribute table */
+
+void    attr_table_free(ATTR_TABLE *table)
+{
+    htable_free(table, myfree);
+}
+
+/* attr_table_read - read attribute stream into table */
+
+int     attr_table_read(ATTR_TABLE *table, int flags, VSTREAM *stream)
+{
+    char   *attr_value;
+    int     attr_count;
+    int     ch;
+
+    if (base64_buf == 0) {
+       base64_buf = vstring_alloc(10);
+       plain_buf = vstring_alloc(10);
+    }
+    for (attr_count = 0; /* void */ ; attr_count++) {
+
+       /*
+        * Unexpected end-of-file is always an error.
+        */
+       if ((ch = vstring_get_nonl(base64_buf, stream)) == VSTREAM_EOF) {
+           msg_warn("unexpected EOF while reading attributes from %s",
+                    VSTREAM_PATH(stream));
+           return (attr_count);
+       }
+
+       /*
+        * A legitimate end of attribute list is OK.
+        */
+       if (LEN(base64_buf) == 0)
+           return (attr_count);
+
+       /*
+        * Split into name and value, but keep the ':' separator so that we
+        * can distinguish between no value or a zero-length value; decode
+        * the name but keep the value in one piece, so that we can process
+        * array-valued attributes.
+        */
+       if ((attr_value = strchr(STR(base64_buf), ':')) != 0)
+           *attr_value = 0;
+       if (BASE64_DECODE(plain_buf, STR(base64_buf), attr_value ?
+                  (attr_value - STR(base64_buf)) : LEN(base64_buf)) == 0) {
+           msg_warn("malformed base64 data from %s: %.100s",
+                    VSTREAM_PATH(stream), STR(base64_buf));
+           return (attr_count);
+       }
+       if (attr_value != 0)
+           *attr_value = ':';
+       else
+           attr_value = "";
+
+       /*
+        * Stop if there are multiple instances of the same attribute name
+        * and extra attributes are to be treated as an error. We can
+        * remember only one instance.
+        */
+       if (htable_locate(table, STR(plain_buf)) != 0) {
+           if (flags & ATTR_FLAG_EXTRA) {
+               msg_warn("multiple instances of attribute %s from %s",
+                        STR(plain_buf), VSTREAM_PATH(stream));
+               return (attr_count);
+           }
+       } else {
+           htable_enter(table, STR(plain_buf), mystrdup(attr_value));
+       }
+    }
+}
+
+/* attr_conv_string - convert attribute value field to string */
+
+static int attr_conv_string(char **src, VSTRING *plain_buf,
+                                   const char *attr_name)
+{
+    char   *myname = "attr_table_get";
+    extern int var_line_limit;
+    int     limit = var_line_limit * 5 / 4;
+    char   *cp = *src;
+    int     len = 0;
+    int     ch;
+
+    for (;;) {
+       if ((ch = *(unsigned char *) cp) == 0)
+           break;
+       cp++;
+       if (ch == ':')
+           break;
+       len++;
+       if (len > limit) {
+           msg_warn("%s: string length > %d characters in attribute %s",
+                    myname, limit, attr_name);
+           return (-1);
+       }
+    }
+    if (BASE64_DECODE(plain_buf, *src, len) == 0) {
+       msg_warn("%s: malformed base64 data in attribute %s: %.*s",
+                myname, attr_name, len > 100 ? 100 : len, *src);
+       return (-1);
+    }
+    if (msg_verbose)
+       msg_info("%s: name %s value %s", myname, attr_name, STR(plain_buf));
+
+    *src = cp;
+    return (ch);
+}
+
+/* attr_conv_number - convert attribute value field to number */
+
+static int attr_conv_number(char **src, unsigned *dst, VSTRING *plain_buf,
+                                   const char *attr_name)
+{
+    char   *myname = "attr_table_get";
+    char    junk = 0;
+    int     ch;
+
+    if ((ch = attr_conv_string(src, plain_buf, attr_name)) < 0)
+       return (-1);
+    if (sscanf(STR(plain_buf), "%u%c", dst, &junk) != 1 || junk != 0) {
+       msg_warn("%s: malformed numerical data in attribute %s: %.100s",
+                myname, attr_name, STR(plain_buf));
+       return (-1);
+    }
+    return (ch);
+}
+
+/* attr_table_vget - recover attributes from table */
+
+int     attr_table_vget(ATTR_TABLE *attr, int flags, va_list ap)
+{
+    char   *myname = "attr_table_get";
+    int     attr_count;
+    char   *attr_name;
+    char   *attr_value;
+    int     attr_type;
+    int    *number;
+    VSTRING *string;
+    INTV   *number_array;
+    ARGV   *string_array;
+    unsigned num_val;
+    int     ch;
+
+    if (base64_buf == 0) {
+       base64_buf = vstring_alloc(10);
+       plain_buf = vstring_alloc(10);
+    }
+
+    /*
+     * Iterate over all (type, name, value) triples.
+     */
+    for (attr_count = 0; /* void */ ; attr_count++) {
+
+       /*
+        * Determine the next attribute name on the caller's wish list.
+        */
+       attr_type = va_arg(ap, int);
+       if (attr_type == ATTR_TYPE_END)
+           return (attr_count);
+       attr_name = va_arg(ap, char *);
+
+       /*
+        * Look up the attribute value. Peel off, but keep, the separator
+        * between name and value.
+        */
+       if ((attr_value = htable_find(attr, attr_name)) == 0) {
+           if (attr_type != ATTR_TYPE_END
+               && (flags & ATTR_FLAG_MISSING) != 0)
+               msg_warn("%s: missing attribute %s", myname, attr_name);
+           return (attr_count);
+       }
+       if ((ch = *(unsigned char *) attr_value) != 0)
+           attr_value++;
+
+       /*
+        * 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 (attr_type) {
+       case ATTR_TYPE_NUM:
+           if (ch != ':') {
+               msg_warn("%s: missing value for attribute %s",
+                        myname, attr_name);
+               return (attr_count);
+           }
+           number = va_arg(ap, int *);
+           if ((ch = attr_conv_number(&attr_value, number, plain_buf, attr_name)) < 0)
+               return (attr_count);
+           if (ch != '\0') {
+               msg_warn("%s: too many values for attribute %s",
+                        myname, attr_name);
+               return (attr_count);
+           }
+           break;
+       case ATTR_TYPE_STR:
+           if (ch != ':') {
+               msg_warn("%s: missing value for attribute %s",
+                        myname, attr_name);
+               return (attr_count);
+           }
+           string = va_arg(ap, VSTRING *);
+           if ((ch = attr_conv_string(&attr_value, string, attr_name)) < 0)
+               return (attr_count);
+           if (ch != '\0') {
+               msg_warn("%s: too many values for attribute %s",
+                        myname, attr_name);
+               return (attr_count);
+           }
+           break;
+       case ATTR_TYPE_NUM_ARRAY:
+           number_array = va_arg(ap, INTV *);
+           while (ch != '\0') {
+               if ((ch = attr_conv_number(&attr_value, &num_val, plain_buf, attr_name)) < 0)
+                   return (attr_count);
+               intv_add(number_array, 1, num_val);
+           }
+           break;
+       case ATTR_TYPE_STR_ARRAY:
+           string_array = va_arg(ap, ARGV *);
+           while (ch != '\0') {
+               if ((ch = attr_conv_string(&attr_value, plain_buf, attr_name)) < 0)
+                   return (attr_count);
+               argv_add(string_array, STR(plain_buf), (char *) 0);
+           }
+           break;
+       default:
+           msg_panic("%s: unknown type code: %d", myname, attr_type);
+       }
+    }
+}
+
+/* attr_table_get - recover attributes from table */
+
+int     attr_table_get(ATTR_TABLE *attr, int flags,...)
+{
+    va_list ap;
+    int     ret;
+
+    va_start(ap, flags);
+    ret = attr_table_vget(attr, flags, ap);
+    va_end(ap);
+
+    return (ret);
+}
+
+#ifdef TEST
+
+ /*
+  * Proof of concept test program.  Mirror image of the attr_scan test
+  * program.
+  */
+#include <msg_vstream.h>
+
+int     var_line_limit = 2048;
+
+int     main(int unused_argc, char **used_argv)
+{
+    ATTR_TABLE *attr;
+    INTV   *intv = intv_alloc(1);
+    ARGV   *argv = argv_alloc(1);
+    VSTRING *str_val = vstring_alloc(1);
+    int     int_val;
+    int     ret;
+    int     i;
+
+    msg_verbose = 1;
+    msg_vstream_init(used_argv[0], VSTREAM_ERR);
+    attr = attr_table_create(1);
+    if (attr_table_read(attr, ATTR_FLAG_EXTRA, VSTREAM_IN) > 0
+       && (ret = attr_table_get(attr,
+                                ATTR_FLAG_MISSING,
+                                ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+                                ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+                            ATTR_TYPE_NUM_ARRAY, ATTR_NAME_NUM_ARRAY, intv,
+                            ATTR_TYPE_STR_ARRAY, ATTR_NAME_STR_ARRAY, argv,
+                                ATTR_TYPE_END)) == 4) {
+       vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+       vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+       vstream_printf("%s", ATTR_NAME_NUM_ARRAY);
+       for (i = 0; i < intv->intc; i++)
+           vstream_printf(" %d", intv->intv[i]);
+       vstream_printf("\n");
+       vstream_printf("%s", ATTR_NAME_STR_ARRAY);
+       for (i = 0; i < argv->argc; i++)
+           vstream_printf(" %s", argv->argv[i]);
+       vstream_printf("\n");
+    } else {
+       vstream_printf("return: %d\n", ret);
+    }
+    if (vstream_fflush(VSTREAM_OUT) != 0)
+       msg_fatal("write error: %m");
+
+    attr_table_free(attr);
+    intv_free(intv);
+    argv_free(argv);
+    vstring_free(str_val);
+
+    return (0);
+}
+
+#endif