static int eval_command_status(int command_status, char *service, DELIVER_REQUEST *request, PIPE_ATTR *attr, DSN_BUF *why) { RECIPIENT *rcpt; int status; int result = 0; int n; /* * Depending on the result, bounce or defer the message, and mark the * recipient as done where appropriate. */ switch (command_status) { case PIPE_STAT_OK: dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ? "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY, "delivered via %s service", service); (void) DSN_FROM_DSN_BUF(why); for (n = 0; n < request->rcpt_list.len; n++) { rcpt = request->rcpt_list.info + n; status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, service, &why->dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(request->fp, rcpt->offset); result |= status; } break; case PIPE_STAT_BOUNCE: case PIPE_STAT_DEFER: (void) DSN_FROM_DSN_BUF(why); for (n = 0; n < request->rcpt_list.len; n++) { rcpt = request->rcpt_list.info + n; /* XXX Maybe encapsulate this with ndr_append(). */ status = (STR(why->status)[0] != '4' ? bounce_append : defer_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, service, &why->dsn); if (status == 0) deliver_completed(request->fp, rcpt->offset); result |= status; } break; case PIPE_STAT_CORRUPT: /* XXX DSN should we send something? */ result |= DEL_STAT_DEFER; break; default: msg_panic("eval_command_status: bad status %d", command_status); /* NOTREACHED */ } return (result); }
void smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name, SMTP_RESP *resp, const char *format,...) { DELIVER_REQUEST *request = state->request; SMTP_SESSION *session = state->session; DSN_BUF *why = state->why; int status; int soft_error; int soft_bounce_error; va_list ap; /* * Sanity check. */ if (SMTP_RCPT_ISMARKED(rcpt)) msg_panic("smtp_rcpt_fail: recipient <%s> is marked", rcpt->address); /* * Initialize. */ va_start(ap, format); vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); va_end(ap); soft_error = STR(why->status)[0] == '4'; soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); if (state->session && mta_name) smtp_check_code(state->session, resp->code); /* * Don't defer this recipient record just yet when this error qualifies * for trying other mail servers. Just log something informative to show * why we're skipping this recipient now. */ if ((soft_error || soft_bounce_error) && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { msg_info("%s: %s", request->queue_id, STR(why->reason)); SMTP_RCPT_KEEP(state, rcpt); } /* * Defer or bounce this recipient, and delete from the delivery request. * If the bounce fails, defer instead and do not qualify the recipient * for delivery to a backup server. * * Note: we may still make an SMTP connection to deliver other recipients * that did qualify for delivery to a backup server. */ else { (void) DSN_FROM_DSN_BUF(state->why); status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, session ? session->namaddrport : "none", &why->dsn); if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); state->status |= status; } }
void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt) { DELIVER_REQUEST *request = state->request; SMTP_SESSION *session = state->session; SMTP_ITERATOR *iter = state->iterator; DSN_BUF *why = state->why; const char *dsn_action = "relayed"; int status; /* * Assume this was intermediate delivery when the server announced DSN * support, and don't send a DSN "SUCCESS" notification. */ if (session->features & SMTP_FEATURE_DSN) rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS; /* * Assume this was final delivery when the LMTP server announced no DSN * support. In backwards compatibility mode, send a "relayed" instead of * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify" * the expression. The redundancy is for clarity. It is trivially * eliminated by the compiler. There is no need to sacrifice clarity for * the sake of "performance". */ if ((session->features & SMTP_FEATURE_DSN) == 0 && !smtp_mode && var_lmtp_assume_final != 0) dsn_action = "delivered"; /* * Report success and delete the recipient from the delivery request. * Defer if the success can't be reported. * * Note: the DSN action is ignored in case of address probes. */ dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, STR(iter->host), DSB_DTYPE_SMTP, resp->str, "%s", resp->str); status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, session->namaddrport, DSN_FROM_DSN_BUF(why)); if (status == 0) if (request->flags & DEL_REQ_FLAG_SUCCESS) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); state->status |= status; }
static void qmgr_deliver_update(int unused_event, char *context) { QMGR_ENTRY *entry = (QMGR_ENTRY *) context; QMGR_QUEUE *queue = entry->queue; QMGR_TRANSPORT *transport = queue->transport; QMGR_MESSAGE *message = entry->message; static DSN_BUF *dsb; int status; /* * Release the delivery agent from a "hot" queue entry. */ #define QMGR_DELIVER_RELEASE_AGENT(entry) do { \ event_disable_readwrite(vstream_fileno(entry->stream)); \ (void) vstream_fclose(entry->stream); \ entry->stream = 0; \ qmgr_deliver_concurrency--; \ } while (0) if (dsb == 0) dsb = dsb_create(); /* * The message transport has responded. Stop the watchdog timer. */ event_cancel_timer(qmgr_deliver_abort, context); /* * Retrieve the delivery agent status report. The numerical status code * indicates if delivery should be tried again. The reason text is sent * only when a site should be avoided for a while, so that the queue * manager can log why it does not even try to schedule delivery to the * affected recipients. */ status = qmgr_deliver_final_reply(entry->stream, dsb); /* * The mail delivery process failed for some reason (although delivery * may have been successful). Back off with this transport type for a * while. Dispose of queue entries for this transport that await * selection (the todo lists). Stay away from queue entries that have * been selected (the busy lists), or we would have dangling pointers. * The queue itself won't go away before we dispose of the current queue * entry. */ if (status == DELIVER_STAT_CRASH) { message->flags |= DELIVER_STAT_DEFER; #if 0 whatsup = concatenate("unknown ", transport->name, " mail transport error", (char *) 0); qmgr_transport_throttle(transport, DSN_SIMPLE(&dsb->dsn, "4.3.0", whatsup)); myfree(whatsup); #else qmgr_transport_throttle(transport, DSN_SIMPLE(&dsb->dsn, "4.3.0", "unknown mail transport error")); #endif msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description", transport->name); /* * Assume the worst and write a defer logfile record for each * recipient. This omission was already present in the first queue * manager implementation of 199703, and was fixed 200511. * * To avoid the synchronous qmgr_defer_recipient() operation for each * recipient of this queue entry, release the delivery process and * move the entry back to the todo queue. Let qmgr_defer_transport() * log the recipient asynchronously if possible, and get out of here. * Note: if asynchronous logging is not possible, * qmgr_defer_transport() eventually invokes qmgr_entry_done() and * the entry becomes a dangling pointer. */ QMGR_DELIVER_RELEASE_AGENT(entry); qmgr_entry_unselect(queue, entry); qmgr_defer_transport(transport, &dsb->dsn); return; } /* * This message must be tried again. * * If we have a problem talking to this site, back off with this site for a * while; dispose of queue entries for this site that await selection * (the todo list); stay away from queue entries that have been selected * (the busy list), or we would have dangling pointers. The queue itself * won't go away before we dispose of the current queue entry. * * XXX Caution: DSN_COPY() will panic on empty status or reason. */ #define SUSPENDED "delivery temporarily suspended: " if (status == DELIVER_STAT_DEFER) { message->flags |= DELIVER_STAT_DEFER; if (VSTRING_LEN(dsb->status)) { /* Sanitize the DSN status/reason from the delivery agent. */ if (!dsn_valid(vstring_str(dsb->status))) vstring_strcpy(dsb->status, "4.0.0"); if (VSTRING_LEN(dsb->reason) == 0) vstring_strcpy(dsb->reason, "unknown error"); vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1); if (QMGR_QUEUE_READY(queue)) { qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb)); if (QMGR_QUEUE_THROTTLED(queue)) qmgr_defer_todo(queue, &dsb->dsn); } } } /* * No problems detected. Mark the transport and queue as alive. The queue * itself won't go away before we dispose of the current queue entry. */ if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) { qmgr_transport_unthrottle(transport); qmgr_queue_unthrottle(queue); } /* * Release the delivery process, and give some other queue entry a chance * to be delivered. When all recipients for a message have been tried, * decide what to do next with this message: defer, bounce, delete. */ QMGR_DELIVER_RELEASE_AGENT(entry); qmgr_entry_done(entry, QMGR_QUEUE_BUSY); }
static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue) { DELIVER_REQUEST *request = state->request; SMTP_SESSION *session = state->session; DSN_BUF *why = state->why; RECIPIENT *rcpt; int status; int soft_error = (STR(why->status)[0] == '4'); int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); int nrcpt; /* * Don't defer the recipients just yet when this error qualifies them for * delivery to a backup server. Just log something informative to show * why we're skipping this host. */ if ((soft_error || soft_bounce_error) && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { msg_info("%s: %s", request->queue_id, STR(why->reason)); for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; if (SMTP_RCPT_ISMARKED(rcpt)) continue; SMTP_RCPT_KEEP(state, rcpt); } } /* * Defer or bounce all the remaining recipients, and delete them from the * delivery request. If a bounce fails, defer instead and do not qualify * the recipient for delivery to a backup server. */ else { /* * If we are still in the connection set-up phase, update the set-up * completion time here, otherwise the time spent in set-up latency * will be attributed as message transfer latency. * * All remaining recipients have failed at this point, so we update the * delivery completion time stamp so that multiple recipient status * records show the same delay values. */ if (request->msg_stats.conn_setup_done.tv_sec == 0) { GETTIMEOFDAY(&request->msg_stats.conn_setup_done); request->msg_stats.deliver_done = request->msg_stats.conn_setup_done; } else GETTIMEOFDAY(&request->msg_stats.deliver_done); (void) DSN_FROM_DSN_BUF(why); for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; if (SMTP_RCPT_ISMARKED(rcpt)) continue; status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, session ? session->namaddrport : "none", &why->dsn); if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); state->status |= status; } if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0 && throttle_queue && (soft_error || soft_bounce_error) && request->hop_status == 0) request->hop_status = DSN_COPY(&why->dsn); } /* * Don't cache this session. We can't talk to this server. */ if (throttle_queue && session) DONT_CACHE_BAD_SESSION; return (-1); }
static int bounce_one_proto(char *service_name, VSTREAM *client) { const char *myname = "bounce_one_proto"; int flags; int dsn_ret; /* * Read and validate the client request. */ if (mail_command_server(client, ATTR_TYPE_INT, 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_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret, ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf, ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf, ATTR_TYPE_END) != 9) { msg_warn("malformed request"); return (-1); } /* * Sanitize input. */ if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) { msg_warn("wrong service name \"%s\" for one-recipient bouncing", service_name); return (-1); } if (mail_queue_name_ok(STR(queue_name)) == 0) { msg_warn("malformed queue name: %s", printable(STR(queue_name), '?')); return (-1); } if (mail_queue_id_ok(STR(queue_id)) == 0) { msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } printable(STR(dsn_envid), '?'); VS_NEUTER(rcpt_buf->address); VS_NEUTER(rcpt_buf->orig_addr); VS_NEUTER(rcpt_buf->dsn_orcpt); VS_NEUTER(dsn_buf->status); VS_NEUTER(dsn_buf->action); VS_NEUTER(dsn_buf->reason); VS_NEUTER(dsn_buf->dtype); VS_NEUTER(dsn_buf->dtext); VS_NEUTER(dsn_buf->mtype); VS_NEUTER(dsn_buf->mname); (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf); (void) DSN_FROM_DSN_BUF(dsn_buf); /* * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and * RECIPIENT_FROM_RCPT_BUF(). */ if (msg_verbose) msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s", myname, flags, STR(queue_name), STR(queue_id), STR(encoding), STR(sender), STR(dsn_envid), dsn_ret, STR(rcpt_buf->orig_addr), STR(rcpt_buf->address), rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt), rcpt_buf->dsn_notify, STR(dsn_buf->status), STR(dsn_buf->action), STR(dsn_buf->reason)); /* * Execute the request. */ return (bounce_one_service(flags, STR(queue_name), STR(queue_id), STR(encoding), STR(sender), STR(dsn_envid), dsn_ret, rcpt_buf, dsn_buf, bounce_templates)); }
static int bounce_append_proto(char *service_name, VSTREAM *client) { const char *myname = "bounce_append_proto"; int flags; /* * Read and validate the client request. */ if (mail_command_server(client, ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &flags, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf, ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf, ATTR_TYPE_END) != 4) { msg_warn("malformed request"); return (-1); } /* * Sanitize input. */ if (mail_queue_id_ok(STR(queue_id)) == 0) { msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } VS_NEUTER(rcpt_buf->address); VS_NEUTER(rcpt_buf->orig_addr); VS_NEUTER(rcpt_buf->dsn_orcpt); VS_NEUTER(dsn_buf->status); VS_NEUTER(dsn_buf->action); VS_NEUTER(dsn_buf->reason); VS_NEUTER(dsn_buf->dtype); VS_NEUTER(dsn_buf->dtext); VS_NEUTER(dsn_buf->mtype); VS_NEUTER(dsn_buf->mname); (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf); (void) DSN_FROM_DSN_BUF(dsn_buf); /* * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and * RECIPIENT_FROM_RCPT_BUF(). */ if (msg_verbose) msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s", myname, flags, service_name, STR(queue_id), STR(rcpt_buf->orig_addr), STR(rcpt_buf->address), rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt), rcpt_buf->dsn_notify, STR(dsn_buf->status), STR(dsn_buf->action), STR(dsn_buf->reason)); /* * On request by the client, set up a trap to delete the log file in case * of errors. */ if (flags & BOUNCE_FLAG_CLEAN) bounce_cleanup_register(service_name, STR(queue_id)); /* * Execute the request. */ return (bounce_append_service(flags, service_name, STR(queue_id), &rcpt_buf->rcpt, &dsn_buf->dsn)); }
static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv) { const char *myname = "deliver_message"; static PIPE_PARAMS conf; static PIPE_ATTR attr; RECIPIENT_LIST *rcpt_list = &request->rcpt_list; DSN_BUF *why = dsb_create(); VSTRING *buf; ARGV *expanded_argv = 0; int deliver_status; int command_status; ARGV *export_env; const char *sender; #define DELIVER_MSG_CLEANUP() { \ dsb_free(why); \ if (expanded_argv) argv_free(expanded_argv); \ } if (msg_verbose) msg_info("%s: from <%s>", myname, request->sender); /* * Sanity checks. The get_service_params() and get_service_attr() * routines also do some sanity checks. Look up service attributes and * config information only once. This is safe since the information comes * from a trusted source, not from the delivery request. */ if (request->nexthop[0] == 0) msg_fatal("empty nexthop hostname"); if (rcpt_list->len <= 0) msg_fatal("recipient count: %d", rcpt_list->len); if (attr.command == 0) { get_service_params(&conf, service); get_service_attr(&attr, argv); } /* * The D flag cannot be specified for multi-recipient deliveries. */ if ((attr.flags & MAIL_COPY_DELIVERED) && (rcpt_list->len > 1)) { dsb_simple(why, "4.3.5", "mail system configuration error"); deliver_status = eval_command_status(PIPE_STAT_DEFER, service, request, &attr, why); msg_warn("pipe flag `D' requires %s_destination_recipient_limit = 1", service); DELIVER_MSG_CLEANUP(); return (deliver_status); } /* * The O flag cannot be specified for multi-recipient deliveries. */ if ((attr.flags & MAIL_COPY_ORIG_RCPT) && (rcpt_list->len > 1)) { dsb_simple(why, "4.3.5", "mail system configuration error"); deliver_status = eval_command_status(PIPE_STAT_DEFER, service, request, &attr, why); msg_warn("pipe flag `O' requires %s_destination_recipient_limit = 1", service); DELIVER_MSG_CLEANUP(); return (deliver_status); } /* * Check that this agent accepts messages this large. */ if (attr.size_limit != 0 && request->data_size > attr.size_limit) { if (msg_verbose) msg_info("%s: too big: size_limit = %ld, request->data_size = %ld", myname, (long) attr.size_limit, request->data_size); dsb_simple(why, "5.2.3", "message too large"); deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service, request, &attr, why); DELIVER_MSG_CLEANUP(); return (deliver_status); } /* * Don't deliver a trace-only request. */ if (DEL_REQ_TRACE_ONLY(request->flags)) { RECIPIENT *rcpt; int status; int n; deliver_status = 0; dsb_simple(why, "2.0.0", "delivers to command: %s", attr.command[0]); (void) DSN_FROM_DSN_BUF(why); for (n = 0; n < request->rcpt_list.len; n++) { rcpt = request->rcpt_list.info + n; status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, service, &why->dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(request->fp, rcpt->offset); deliver_status |= status; } DELIVER_MSG_CLEANUP(); return (deliver_status); } /* * Report mail delivery loops. By definition, this requires * single-recipient delivery. Don't silently lose recipients. */ if (attr.flags & MAIL_COPY_DELIVERED) { DELIVERED_HDR_INFO *info; RECIPIENT *rcpt; int loop_found; if (request->rcpt_list.len > 1) msg_panic("%s: delivered-to enabled with multi-recipient request", myname); info = delivered_hdr_init(request->fp, request->data_offset, FOLD_ADDR_ALL); rcpt = request->rcpt_list.info; loop_found = delivered_hdr_find(info, rcpt->address); delivered_hdr_free(info); if (loop_found) { dsb_simple(why, "5.4.6", "mail forwarding loop for %s", rcpt->address); deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service, request, &attr, why); DELIVER_MSG_CLEANUP(); return (deliver_status); } } /* * Deliver. Set the nexthop and sender variables, and expand the command * argument vector. Recipients will be expanded on the fly. XXX Rewrite * envelope and header addresses according to transport-specific * rewriting rules. */ if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0) msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp)); /* * A non-empty null sender replacement is subject to the 'q' flag. */ buf = vstring_alloc(10); sender = *request->sender ? request->sender : STR(attr.null_sender); if (*sender && (attr.flags & PIPE_OPT_QUOTE_LOCAL)) { quote_822_local(buf, sender); dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf)); } else dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, sender); if (attr.flags & PIPE_OPT_FOLD_HOST) { vstring_strcpy(buf, request->nexthop); lowercase(STR(buf)); dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, STR(buf)); } else dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop); vstring_sprintf(buf, "%ld", (long) request->data_size); dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf)); dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_ADDR, request->client_addr); dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_HELO, request->client_helo); dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_NAME, request->client_name); dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PORT, request->client_port); dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PROTO, request->client_proto); dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_METHOD, request->sasl_method); dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_USERNAME, request->sasl_username); dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_SENDER, request->sasl_sender); dict_update(PIPE_DICT_TABLE, PIPE_DICT_QUEUE_ID, request->queue_id); vstring_free(buf); if ((expanded_argv = expand_argv(service, attr.command, rcpt_list, attr.flags)) == 0) { dsb_simple(why, "4.3.5", "mail system configuration error"); deliver_status = eval_command_status(PIPE_STAT_DEFER, service, request, &attr, why); DELIVER_MSG_CLEANUP(); return (deliver_status); } export_env = argv_split(var_export_environ, ", \t\r\n"); command_status = pipe_command(request->fp, why, PIPE_CMD_UID, attr.uid, PIPE_CMD_GID, attr.gid, PIPE_CMD_SENDER, sender, PIPE_CMD_COPY_FLAGS, attr.flags, PIPE_CMD_ARGV, expanded_argv->argv, PIPE_CMD_TIME_LIMIT, conf.time_limit, PIPE_CMD_EOL, STR(attr.eol), PIPE_CMD_EXPORT, export_env->argv, PIPE_CMD_CWD, attr.exec_dir, PIPE_CMD_CHROOT, attr.chroot_dir, PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr, PIPE_CMD_DELIVERED, rcpt_list->info[0].address, PIPE_CMD_END); argv_free(export_env); deliver_status = eval_command_status(command_status, service, request, &attr, why); /* * Clean up. */ DELIVER_MSG_CLEANUP(); return (deliver_status); }
static int eval_command_status(int command_status, char *service, DELIVER_REQUEST *request, PIPE_ATTR *attr, DSN_BUF *why) { RECIPIENT *rcpt; int status; int result = 0; int n; char *saved_text; /* * Depending on the result, bounce or defer the message, and mark the * recipient as done where appropriate. */ switch (command_status) { case PIPE_STAT_OK: /* Save the command output before dsb_update() clobbers it. */ vstring_truncate(why->reason, trimblanks(STR(why->reason), VSTRING_LEN(why->reason)) - STR(why->reason)); if (VSTRING_LEN(why->reason) > 0) { VSTRING_TERMINATE(why->reason); saved_text = vstring_export(vstring_sprintf( vstring_alloc(VSTRING_LEN(why->reason)), " (%.100s)", STR(why->reason))); } else saved_text = mystrdup(""); /* uses shared R/O storage */ dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ? "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY, "delivered via %s service%s", service, saved_text); myfree(saved_text); (void) DSN_FROM_DSN_BUF(why); for (n = 0; n < request->rcpt_list.len; n++) { rcpt = request->rcpt_list.info + n; status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, service, &why->dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(request->fp, rcpt->offset); result |= status; } break; case PIPE_STAT_BOUNCE: case PIPE_STAT_DEFER: (void) DSN_FROM_DSN_BUF(why); for (n = 0; n < request->rcpt_list.len; n++) { rcpt = request->rcpt_list.info + n; /* XXX Maybe encapsulate this with ndr_append(). */ status = (STR(why->status)[0] != '4' ? bounce_append : defer_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, service, &why->dsn); if (status == 0) deliver_completed(request->fp, rcpt->offset); result |= status; } break; case PIPE_STAT_CORRUPT: /* XXX DSN should we send something? */ result |= DEL_STAT_DEFER; break; default: msg_panic("eval_command_status: bad status %d", command_status); /* NOTREACHED */ } return (result); }