#include <sys_defs.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <msg.h>
#include <scan_dir.h>
#include <vstring.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <stringops.h>
#include <mymalloc.h>
#include <htable.h>
#include <mail_queue.h>
#include <mail_open_ok.h>
#include <mail_proto.h>
#include <mail_date.h>
#include <mail_params.h>
#include <mail_version.h>
#include <mail_scan_dir.h>
#include <mail_conf.h>
#include <record.h>
#include <rec_type.h>
#include <quote_822_local.h>
#include <mail_addr.h>
#include <bounce_log.h>
#include <mail_server.h>
int var_dup_filter_limit;
char *var_empty_addr;
static void showq_reasons(VSTREAM *, BOUNCE_LOG *, RCPT_BUF *, DSN_BUF *,
HTABLE *);
#define STR(x) vstring_str(x)
static void showq_report(VSTREAM *client, char *queue, char *id,
VSTREAM *qfile, long size, time_t mtime)
{
VSTRING *buf = vstring_alloc(100);
VSTRING *printable_quoted_addr = vstring_alloc(100);
int rec_type;
time_t arrival_time = 0;
char *start;
long msg_size = size;
BOUNCE_LOG *logfile;
HTABLE *dup_filter = 0;
RCPT_BUF *rcpt_buf = 0;
DSN_BUF *dsn_buf = 0;
int sender_seen = 0;
int msg_size_ok = 0;
#define SHOWQ_CLEANUP_AND_RETURN { \
if (sender_seen > 0) \
attr_print(client, ATTR_FLAG_NONE, ATTR_TYPE_END); \
vstring_free(buf); \
vstring_free(printable_quoted_addr); \
if (rcpt_buf) \
rcpb_free(rcpt_buf); \
if (dsn_buf) \
dsb_free(dsn_buf); \
if (dup_filter) \
htable_free(dup_filter, (void (*) (void *)) 0); \
}
while (!vstream_ferror(client) && (rec_type = rec_get(qfile, buf, 0)) > 0) {
start = vstring_str(buf);
if (msg_verbose)
msg_info("record %c %s", rec_type, printable(start, '?'));
switch (rec_type) {
case REC_TYPE_TIME:
if (arrival_time == 0)
arrival_time = atol(start);
break;
case REC_TYPE_SIZE:
if (msg_size_ok == 0) {
msg_size_ok = (start[strspn(start, "0123456789 ")] == 0
&& (msg_size = atol(start)) >= 0);
if (msg_size_ok == 0) {
msg_warn("%s: malformed size record: %.100s "
"-- using file size instead",
id, printable(start, '?'));
msg_size = size;
}
}
break;
case REC_TYPE_FROM:
if (*start == 0)
start = var_empty_addr;
quote_822_local(printable_quoted_addr, start);
printable(STR(printable_quoted_addr), '?');
if (sender_seen++ > 0) {
msg_warn("%s: duplicate sender address: %s "
"-- skipping remainder of this file",
id, STR(printable_quoted_addr));
SHOWQ_CLEANUP_AND_RETURN;
}
attr_print(client, ATTR_FLAG_MORE,
SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
SEND_ATTR_LONG(MAIL_ATTR_TIME, arrival_time > 0 ?
arrival_time : mtime),
SEND_ATTR_LONG(MAIL_ATTR_SIZE, msg_size),
SEND_ATTR_STR(MAIL_ATTR_SENDER,
STR(printable_quoted_addr)),
ATTR_TYPE_END);
break;
case REC_TYPE_RCPT:
if (sender_seen == 0) {
msg_warn("%s: missing sender address: %s "
"-- skipping remainder of this file",
id, STR(printable_quoted_addr));
SHOWQ_CLEANUP_AND_RETURN;
}
if (*start == 0)
start = var_empty_addr;
quote_822_local(printable_quoted_addr, start);
printable(STR(printable_quoted_addr), '?');
if (dup_filter == 0
|| htable_locate(dup_filter, STR(printable_quoted_addr)) == 0)
attr_print(client, ATTR_FLAG_MORE,
SEND_ATTR_STR(MAIL_ATTR_RECIP,
STR(printable_quoted_addr)),
SEND_ATTR_STR(MAIL_ATTR_WHY, ""),
ATTR_TYPE_END);
break;
case REC_TYPE_MESG:
if (msg_size_ok && vstream_fseek(qfile, msg_size, SEEK_CUR) < 0)
msg_fatal("seek file %s: %m", VSTREAM_PATH(qfile));
break;
case REC_TYPE_END:
break;
}
if (rec_type == REC_TYPE_FROM
&& (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) {
if (dup_filter != 0)
msg_panic("showq_report: attempt to reuse duplicate filter");
dup_filter = htable_create(var_dup_filter_limit);
if (rcpt_buf == 0)
rcpt_buf = rcpb_create();
if (dsn_buf == 0)
dsn_buf = dsb_create();
showq_reasons(client, logfile, rcpt_buf, dsn_buf, dup_filter);
if (bounce_log_close(logfile))
msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id);
}
}
SHOWQ_CLEANUP_AND_RETURN;
}
static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf,
DSN_BUF *dsn_buf, HTABLE *dup_filter)
{
RECIPIENT *rcpt = &rcpt_buf->rcpt;
DSN *dsn = &dsn_buf->dsn;
while (bounce_log_read(bp, rcpt_buf, dsn_buf) != 0) {
if (var_dup_filter_limit == 0
|| dup_filter->used < var_dup_filter_limit)
if (htable_locate(dup_filter, rcpt->address) == 0)
htable_enter(dup_filter, rcpt->address, (void *) 0);
attr_print(client, ATTR_FLAG_MORE,
SEND_ATTR_STR(MAIL_ATTR_RECIP, rcpt->address),
SEND_ATTR_STR(MAIL_ATTR_WHY, dsn->reason),
ATTR_TYPE_END);
}
}
static void showq_service(VSTREAM *client, char *unused_service, char **argv)
{
VSTREAM *qfile;
const char *path;
int status;
char *id;
struct stat st;
struct queue_info {
char *name;
char *(*scan_next) (SCAN_DIR *);
};
struct queue_info *qp;
static struct queue_info queue_info[] = {
MAIL_QUEUE_MAILDROP, scan_dir_next,
MAIL_QUEUE_ACTIVE, mail_scan_dir_next,
MAIL_QUEUE_INCOMING, mail_scan_dir_next,
MAIL_QUEUE_DEFERRED, mail_scan_dir_next,
MAIL_QUEUE_HOLD, mail_scan_dir_next,
0,
};
if (argv[0])
msg_fatal("unexpected command-line argument: %s", argv[0]);
for (qp = queue_info; qp->name != 0; qp++) {
SCAN_DIR *scan = scan_dir_open(qp->name);
char *saved_id = 0;
while ((id = qp->scan_next(scan)) != 0) {
if (saved_id) {
if (strcmp(saved_id, id) == 0) {
msg_warn("readdir loop on queue %s id %s", qp->name, id);
break;
}
myfree(saved_id);
}
saved_id = mystrdup(id);
status = mail_open_ok(qp->name, id, &st, &path);
if (status == MAIL_OPEN_YES) {
if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
showq_report(client, qp->name, id, qfile, (long) st.st_size,
st.st_mtime);
if (vstream_fclose(qfile))
msg_warn("close file %s %s: %m", qp->name, id);
} else if (errno != ENOENT) {
msg_warn("open %s %s: %m", qp->name, id);
}
}
vstream_fflush(client);
}
if (saved_id)
myfree(saved_id);
scan_dir_close(scan);
}
attr_print(client, ATTR_FLAG_NONE, ATTR_TYPE_END);
}
MAIL_VERSION_STAMP_DECLARE;
int main(int argc, char **argv)
{
static const CONFIG_INT_TABLE int_table[] = {
VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
0,
};
CONFIG_STR_TABLE str_table[] = {
VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
0,
};
MAIL_VERSION_STAMP_ALLOCATE;
single_server_main(argc, argv, showq_service,
CA_MAIL_SERVER_INT_TABLE(int_table),
CA_MAIL_SERVER_STR_TABLE(str_table),
0);
}