From: Wietse Venema Date: Sun, 14 Oct 2001 05:00:00 +0000 (-0500) Subject: snapshot-20011014 X-Git-Tag: v1.1.0~40 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f7a3806f8afa0162d5a581133ea4e103cd08f8d;p=thirdparty%2Fpostfix.git snapshot-20011014 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 5e1656aa2..2cec9fdb2 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -1,6 +1,7 @@ -TABOUNCE -TALIAS_TOKEN -TARGV +-TATTR_TABLE -TBH_TABLE -TBINATTR -TBINATTR_INFO diff --git a/postfix/HISTORY b/postfix/HISTORY index 7a37c64a1..827a7fba1 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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 diff --git a/postfix/conf/sample-local.cf b/postfix/conf/sample-local.cf index eab49fd87..69f87dda1 100644 --- a/postfix/conf/sample-local.cf +++ b/postfix/conf/sample-local.cf @@ -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 diff --git a/postfix/src/bounce/bounce.c b/postfix/src/bounce/bounce.c index 75a62032e..8cfcf7380 100644 --- a/postfix/src/bounce/bounce.c +++ b/postfix/src/bounce/bounce.c @@ -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); /* diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c index 01a67f09f..1802c1a86 100644 --- a/postfix/src/cleanup/cleanup.c +++ b/postfix/src/cleanup/cleanup.c @@ -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. diff --git a/postfix/src/flush/flush.c b/postfix/src/flush/flush.c index 5fd7316c3..ffabf3329 100644 --- a/postfix/src/flush/flush.c +++ b/postfix/src/flush/flush.c @@ -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); diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 97e35b60f..b96837dc8 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -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 diff --git a/postfix/src/global/abounce.c b/postfix/src/global/abounce.c index 6f2302081..f688864fa 100644 --- a/postfix/src/global/abounce.c +++ b/postfix/src/global/abounce.c @@ -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 { diff --git a/postfix/src/global/bounce.c b/postfix/src/global/bounce.c index 75b07298d..044c7b8e4 100644 --- a/postfix/src/global/bounce.c +++ b/postfix/src/global/bounce.c @@ -142,10 +142,14 @@ int vbounce_append(int flags, const char *id, const char *recipient, why = vstring_alloc(100); delay = time((time_t *) 0) - entry; vstring_vsprintf(why, fmt, ap); - if (mail_command_write(MAIL_CLASS_PRIVATE, var_soft_bounce ? - MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE, - "%d %d %s %s %s", BOUNCE_CMD_APPEND, - flags, id, recipient, vstring_str(why)) == 0) { + if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ? + MAIL_SERVICE_DEFER : MAIL_SERVICE_BOUNCE, + ATTR_TYPE_NUM, MAIL_ATTR_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); diff --git a/postfix/src/global/defer.c b/postfix/src/global/defer.c index 934b289cb..6eaa2bd8e 100644 --- a/postfix/src/global/defer.c +++ b/postfix/src/global/defer.c @@ -145,9 +145,13 @@ int vdefer_append(int flags, const char *id, const char *recipient, const char *rcpt_domain; vstring_vsprintf(why, fmt, ap); - if (mail_command_write(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER, - "%d %d %s %s %s", BOUNCE_CMD_APPEND, - flags, id, recipient, vstring_str(why)) != 0) + if (mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_DEFER, + ATTR_TYPE_NUM, MAIL_ATTR_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); diff --git a/postfix/src/global/deliver_pass.c b/postfix/src/global/deliver_pass.c index 2deec8c3c..cd2d7e72a 100644 --- a/postfix/src/global/deliver_pass.c +++ b/postfix/src/global/deliver_pass.c @@ -77,7 +77,9 @@ static int deliver_pass_initial_reply(VSTREAM *stream) { int stat; - if (mail_scan(stream, "%d", &stat) != 1) { + if (attr_scan(stream, ATTR_FLAG_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; } diff --git a/postfix/src/global/deliver_request.c b/postfix/src/global/deliver_request.c index 3cd4dd848..fecf7e4ba 100644 --- a/postfix/src/global/deliver_request.c +++ b/postfix/src/global/deliver_request.c @@ -67,7 +67,7 @@ /* Warnings: bad data sent by the client. Fatal errors: out of /* memory, queue file open errors. /* SEE ALSO -/* mail_scan(3) low-level intra-mail input routines +/* attr_scan(3) low-level intra-mail input routines /* LICENSE /* .ad /* .fi @@ -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)); } diff --git a/postfix/src/global/flush_clnt.c b/postfix/src/global/flush_clnt.c index 09e92fe8c..56852b1b0 100644 --- a/postfix/src/global/flush_clnt.c +++ b/postfix/src/global/flush_clnt.c @@ -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, diff --git a/postfix/src/global/mail_command_write.c b/postfix/src/global/mail_command_client.c similarity index 66% rename from postfix/src/global/mail_command_write.c rename to postfix/src/global/mail_command_client.c index e6e855788..cdb1422d0 100644 --- a/postfix/src/global/mail_command_write.c +++ b/postfix/src/global/mail_command_client.c @@ -1,15 +1,16 @@ /*++ /* NAME -/* mail_command_write 3 +/* mail_command_client 3 /* SUMMARY /* single-command client /* SYNOPSIS /* #include /* -/* 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 @@ -20,16 +21,17 @@ /* 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 @@ -53,12 +55,11 @@ /* Global library. */ -#include "mail_proto.h" +#include -/* 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); diff --git a/postfix/src/global/mail_command_read.c b/postfix/src/global/mail_command_server.c similarity index 61% rename from postfix/src/global/mail_command_read.c rename to postfix/src/global/mail_command_server.c index 74c7def61..9565b55fb 100644 --- a/postfix/src/global/mail_command_read.c +++ b/postfix/src/global/mail_command_server.c @@ -1,14 +1,15 @@ /*++ /* NAME -/* mail_command_read 3 +/* mail_command_server 3 /* SUMMARY /* single-command server /* SYNOPSIS /* #include /* -/* 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 @@ -17,13 +18,15 @@ /* 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 /* LICENSE /* .ad /* .fi @@ -44,26 +47,21 @@ /* Utility library. */ -#include #include /* 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); } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 36aef3c73..21b8ef185 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -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; diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h index 78b76c864..fe09056ad 100644 --- a/postfix/src/global/mail_proto.h +++ b/postfix/src/global/mail_proto.h @@ -21,6 +21,7 @@ */ #include #include +#include /* * 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 diff --git a/postfix/src/global/mail_stream.c b/postfix/src/global/mail_stream.c index e30537dff..b624e7ff3 100644 --- a/postfix/src/global/mail_stream.c +++ b/postfix/src/global/mail_stream.c @@ -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 { diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 584ed2da1..0a2b83bd4 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 diff --git a/postfix/src/global/post_mail.c b/postfix/src/global/post_mail.c index 91f5a608e..52ed8716c 100644 --- a/postfix/src/global/post_mail.c +++ b/postfix/src/global/post_mail.c @@ -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); diff --git a/postfix/src/global/resolve_clnt.c b/postfix/src/global/resolve_clnt.c index ff27504a6..112e2866a 100644 --- a/postfix/src/global/resolve_clnt.c +++ b/postfix/src/global/resolve_clnt.c @@ -156,13 +156,20 @@ void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) for (;;) { stream = clnt_stream_access(rewrite_clnt_stream); - if (mail_print(stream, "%s %s", RESOLVE_ADDR, addr) + if (attr_print(stream, ATTR_FLAG_NONE, + ATTR_TYPE_STR, MAIL_ATTR_REQ, RESOLVE_ADDR, + ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr, + ATTR_TYPE_END) || vstream_fflush(stream)) { if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("%s: bad write: %m", myname); - } else if (mail_scan(stream, "%s %s %s %d", - reply->transport, reply->nexthop, - reply->recipient, &reply->flags) != 4) { + } else if (attr_scan(stream, ATTR_FLAG_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 { diff --git a/postfix/src/global/rewrite_clnt.c b/postfix/src/global/rewrite_clnt.c index ba9049ad3..9c32c84f6 100644 --- a/postfix/src/global/rewrite_clnt.c +++ b/postfix/src/global/rewrite_clnt.c @@ -123,11 +123,17 @@ VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) for (;;) { stream = clnt_stream_access(rewrite_clnt_stream); - if (mail_print(stream, "%s %s %s", REWRITE_ADDR, rule, addr), + if (attr_print(stream, ATTR_FLAG_NONE, + ATTR_TYPE_STR, MAIL_ATTR_REQ, REWRITE_ADDR, + ATTR_TYPE_STR, MAIL_ATTR_RULE, rule, + ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr, + ATTR_TYPE_END), vstream_fflush(stream)) { if (msg_verbose || (errno != EPIPE && errno != ENOENT)) msg_warn("%s: bad write: %m", myname); - } else if (mail_scan(stream, "%s", result) != 1) { + } else if (attr_scan(stream, ATTR_FLAG_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 { diff --git a/postfix/src/local/command.c b/postfix/src/local/command.c index 23fffdf2f..b062de344 100644 --- a/postfix/src/local/command.c +++ b/postfix/src/local/command.c @@ -9,7 +9,7 @@ /* int deliver_command(state, usr_attr, command) /* LOCAL_STATE state; /* USER_ATTR exp_attr; -/* char *command; +/* const char *command; /* DESCRIPTION /* deliver_command() runs a command with a message as standard /* input. A limited amount of standard output and standard error @@ -79,7 +79,7 @@ /* deliver_command - deliver to shell command */ -int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command) +int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command) { char *myname = "deliver_command"; VSTRING *why; @@ -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")); diff --git a/postfix/src/local/forward.c b/postfix/src/local/forward.c index 91f239348..742eb478f 100644 --- a/postfix/src/local/forward.c +++ b/postfix/src/local/forward.c @@ -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; /* diff --git a/postfix/src/local/local.c b/postfix/src/local/local.c index 931b2c98f..9cf8307f1 100644 --- a/postfix/src/local/local.c +++ b/postfix/src/local/local.c @@ -307,6 +307,9 @@ /* 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, }; diff --git a/postfix/src/local/local.h b/postfix/src/local/local.h index 4843179e0..c37bbf1aa 100644 --- a/postfix/src/local/local.h +++ b/postfix/src/local/local.h @@ -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. */ diff --git a/postfix/src/local/mailbox.c b/postfix/src/local/mailbox.c index e49c9137c..760b64306 100644 --- a/postfix/src/local/mailbox.c +++ b/postfix/src/local/mailbox.c @@ -72,6 +72,7 @@ #include #include #include +#include #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); diff --git a/postfix/src/nqmgr/qmgr_deliver.c b/postfix/src/nqmgr/qmgr_deliver.c index d8e513122..b58910b41 100644 --- a/postfix/src/nqmgr/qmgr_deliver.c +++ b/postfix/src/nqmgr/qmgr_deliver.c @@ -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); } diff --git a/postfix/src/pickup/pickup.c b/postfix/src/pickup/pickup.c index 38d798e39..03b7df23b 100644 --- a/postfix/src/pickup/pickup.c +++ b/postfix/src/pickup/pickup.c @@ -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)); diff --git a/postfix/src/postdrop/postdrop.c b/postfix/src/postdrop/postdrop.c index 3ed603451..8a8e974ea 100644 --- a/postfix/src/postdrop/postdrop.c +++ b/postfix/src/postdrop/postdrop.c @@ -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); } diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c index 3614690ca..2b4e613b9 100644 --- a/postfix/src/qmgr/qmgr_deliver.c +++ b/postfix/src/qmgr/qmgr_deliver.c @@ -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); } diff --git a/postfix/src/qmqpd/qmqpd.c b/postfix/src/qmqpd/qmqpd.c index 5117d399c..85a122847 100644 --- a/postfix/src/qmqpd/qmqpd.c +++ b/postfix/src/qmqpd/qmqpd.c @@ -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; diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 58fcdbe29..67f61bead 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -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); } diff --git a/postfix/src/trivial-rewrite/resolve.c b/postfix/src/trivial-rewrite/resolve.c index bd99fa61a..0ad2931b9 100644 --- a/postfix/src/trivial-rewrite/resolve.c +++ b/postfix/src/trivial-rewrite/resolve.c @@ -257,7 +257,9 @@ int resolve_proto(VSTREAM *stream) { int flags; - if (mail_scan(stream, "%s", query) != 1) + if (attr_scan(stream, ATTR_FLAG_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"); diff --git a/postfix/src/trivial-rewrite/rewrite.c b/postfix/src/trivial-rewrite/rewrite.c index 80ab11c25..1e165b7b7 100644 --- a/postfix/src/trivial-rewrite/rewrite.c +++ b/postfix/src/trivial-rewrite/rewrite.c @@ -203,7 +203,10 @@ void rewrite_addr(char *ruleset, char *addr, VSTRING *result) int rewrite_proto(VSTREAM *stream) { - if (mail_scan(stream, "%s %s", ruleset, address) != 2) + if (attr_scan(stream, ATTR_FLAG_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"); diff --git a/postfix/src/trivial-rewrite/trivial-rewrite.c b/postfix/src/trivial-rewrite/trivial-rewrite.c index 61624ada5..c753c00e8 100644 --- a/postfix/src/trivial-rewrite/trivial-rewrite.c +++ b/postfix/src/trivial-rewrite/trivial-rewrite.c @@ -80,7 +80,7 @@ /* .fi /* .IP \fBlocal_transport\fR /* Where to deliver mail for destinations that match $\fBmydestination\fR -/* or $\fBinet_interfaces\fR. +/* or $\fBinet_interfaces\fR. /* The default transport is \fBlocal\fR. /* .sp /* Syntax is \fItransport\fR:\fInexthop\fR; see \fBtransport\fR(5) @@ -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) { diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index bf8ecebda..f3fab48e7 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -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 diff --git a/postfix/src/util/attr.c b/postfix/src/util/attr.c index 303274543..a8f9aa3b3 100644 --- a/postfix/src/util/attr.c +++ b/postfix/src/util/attr.c @@ -2,38 +2,60 @@ /* NAME /* attr 3 /* SUMMARY -/* attribute list manager +/* simple attribute list manager /* SYNOPSIS -/* #include /* #include /* -/* 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 @@ -47,32 +69,61 @@ /* System library. */ -#include "sys_defs.h" +#include +#include /* Utility library. */ -#include "mymalloc.h" -#include "htable.h" -#include "attr.h" +#include +#include +#include +#include -/* 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); +} diff --git a/postfix/src/util/attr.h b/postfix/src/util/attr.h index e81381ed3..4e5febec5 100644 --- a/postfix/src/util/attr.h +++ b/postfix/src/util/attr.h @@ -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 -/* DESCRIPTION -/* .nf +/* #include "attr.h" + DESCRIPTION + .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include /* * 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 index 44c0bd311..000000000 --- a/postfix/src/util/attr_io.h +++ /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 - - /* - * Utility library. - */ -#include - - /* - * 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 diff --git a/postfix/src/util/attr_print.c b/postfix/src/util/attr_print.c index 226a5cbf5..15fecbcac 100644 --- a/postfix/src/util/attr_print.c +++ b/postfix/src/util/attr_print.c @@ -4,18 +4,27 @@ /* SUMMARY /* send attributes over byte stream /* SYNOPSIS -/* #include +/* #include /* -/* 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). @@ -23,6 +32,16 @@ /* 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 @@ -66,9 +88,11 @@ /* Utility library. */ -#include #include -#include +#include +#include +#include +#include /* 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, diff --git a/postfix/src/util/attr_scan.c b/postfix/src/util/attr_scan.c index c40d90665..392fee766 100644 --- a/postfix/src/util/attr_scan.c +++ b/postfix/src/util/attr_scan.c @@ -4,18 +4,26 @@ /* SUMMARY /* recover attributes from byte stream /* SYNOPSIS -/* #include +/* #include /* /* 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: @@ -63,30 +71,49 @@ /* 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 @@ -110,17 +137,16 @@ /* Utility library. */ +#include +#include #include #include -#include #include #include -#include +#include /* 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 index 000000000..2d69c81ca --- /dev/null +++ b/postfix/src/util/attr_table.c @@ -0,0 +1,439 @@ +/*++ +/* NAME +/* attr_table 3 +/* SUMMARY +/* recover attributes from byte stream +/* SYNOPSIS +/* #include +/* +/* 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 +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 + +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