#include <sys_defs.h>
#include <msg.h>
#include <vstring.h>
#include <vstream.h>
#include <split_at.h>
#include <mymalloc.h>
#include <mail_params.h>
#include <deliver_pass.h>
#include <dsb_scan.h>
#include <defer.h>
#include <rcpt_print.h>
#define DELIVER_PASS_DEFER 1
#define DELIVER_PASS_UNKNOWN 2
static int deliver_pass_initial_reply(VSTREAM *stream)
{
int stat;
if (attr_scan(stream, ATTR_FLAG_STRICT,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, &stat,
ATTR_TYPE_END) != 1) {
msg_warn("%s: malformed response", VSTREAM_PATH(stream));
stat = -1;
}
return (stat);
}
static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
const char *nexthop,
RECIPIENT *rcpt)
{
int stat;
attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_INT, MAIL_ATTR_FLAGS, request->flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUE, request->queue_name,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, request->queue_id,
ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, request->data_offset,
ATTR_TYPE_LONG, MAIL_ATTR_SIZE, request->data_size,
ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
ATTR_TYPE_STR, MAIL_ATTR_ENCODING, request->encoding,
ATTR_TYPE_STR, MAIL_ATTR_SENDER, request->sender,
ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, request->dsn_envid,
ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, request->dsn_ret,
ATTR_TYPE_FUNC, msg_stats_print, (void *) &request->msg_stats,
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name,
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr,
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, request->client_port,
ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto,
ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, request->client_helo,
ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, request->sasl_method,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, request->sasl_sender,
ATTR_TYPE_STR, MAIL_ATTR_LOG_IDENT, request->log_ident,
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context,
ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, 1,
ATTR_TYPE_END);
attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
ATTR_TYPE_END);
if (vstream_fflush(stream)) {
msg_warn("%s: bad write: %m", VSTREAM_PATH(stream));
stat = -1;
} else {
stat = 0;
}
return (stat);
}
static int deliver_pass_final_reply(VSTREAM *stream, DSN_BUF *dsb)
{
int stat;
if (attr_scan(stream, ATTR_FLAG_STRICT,
ATTR_TYPE_FUNC, dsb_scan, (void *) dsb,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, &stat,
ATTR_TYPE_END) != 2) {
msg_warn("%s: malformed response", VSTREAM_PATH(stream));
return (DELIVER_PASS_UNKNOWN);
} else {
return (stat ? DELIVER_PASS_DEFER : 0);
}
}
int deliver_pass(const char *class, const char *service,
DELIVER_REQUEST *request,
RECIPIENT *rcpt)
{
VSTREAM *stream;
DSN_BUF *dsb;
DSN dsn;
int status;
char *saved_service;
char *transport;
char *nexthop;
transport = saved_service = mystrdup(service);
if ((nexthop = split_at(saved_service, ':')) == 0 || *nexthop == 0)
nexthop = request->nexthop;
if (*transport == 0)
msg_fatal("missing transport name in \"%s\"", service);
stream = mail_connect_wait(class, transport);
dsb = dsb_create();
if (deliver_pass_initial_reply(stream) != 0
|| deliver_pass_send_request(stream, request, nexthop, rcpt) != 0) {
(void) DSN_SIMPLE(&dsn, "4.3.0", "mail transport unavailable");
status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
rcpt, "none", &dsn);
} else if ((status = deliver_pass_final_reply(stream, dsb))
== DELIVER_PASS_UNKNOWN) {
(void) DSN_SIMPLE(&dsn, "4.3.0", "unknown mail transport error");
status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, &request->msg_stats,
rcpt, "none", &dsn);
}
vstream_fclose(stream);
dsb_free(dsb);
myfree(saved_service);
return (status);
}
int deliver_pass_all(const char *class, const char *service,
DELIVER_REQUEST *request)
{
RECIPIENT_LIST *list;
RECIPIENT *rcpt;
int status = 0;
list = &request->rcpt_list;
for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
status |= deliver_pass(class, service, request, rcpt);
return (status);
}