#include <sysdeps.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <misc/misc.h>
#include <msg/msg.h>
#include <unix/sig.h>
#include "mailfront.h"
#include "conf_qmail.c"
static RESPONSE(no_write,451,"4.3.0 Writing data to qmail-queue failed.");
static RESPONSE(no_pipe,451,"4.3.0 Could not open pipe to qmail-queue.");
static RESPONSE(no_fork,451,"4.3.0 Could not start qmail-queue.");
static RESPONSE(no_chdir,451,"4.3.0 Could not change to the qmail directory.");
static RESPONSE(qq_crashed,451,"4.3.0 qmail-queue crashed.");
static str buffer;
static unsigned long databytes;
static const char* qqargs[2] = { 0, 0 };
static int qqpid = -1;
static int qqepipe[2] = { -1, -1 };
static int qqmpipe[2] = { -1, -1 };
static void close_qqpipe(void)
{
if (qqepipe[0] != -1) close(qqepipe[0]);
if (qqepipe[1] != -1) close(qqepipe[1]);
if (qqmpipe[0] != -1) close(qqmpipe[0]);
if (qqmpipe[1] != -1) close(qqmpipe[1]);
qqepipe[0] = qqepipe[1] = qqmpipe[0] = qqmpipe[1] = -1;
}
static const response* reset(void)
{
close_qqpipe();
str_truncate(&buffer, 0);
return 0;
}
static const response* do_sender(str* sender)
{
if (!str_catc(&buffer, 'F') ||
!str_cat(&buffer, sender) ||
!str_catc(&buffer, 0)) return &resp_oom;
return 0;
}
static const response* do_recipient(str* recipient)
{
if (!str_catc(&buffer, 'T') ||
!str_cat(&buffer, recipient) ||
!str_catc(&buffer, 0)) return &resp_oom;
return 0;
}
static int start_qq(int msgfd, int envfd)
{
if ((qqpid = fork()) == -1) {
close_qqpipe();
return -1;
}
if (qqpid == 0) {
if (!session_exportenv()) exit(51);
if (dup2(msgfd, 0) == -1) exit(120);
if (dup2(envfd, 1) == -1) exit(120);
close_qqpipe();
execvp(qqargs[0], (char**)qqargs);
exit(120);
}
return 0;
}
static const response* data_start(int fd)
{
const char* qh;
qqargs[0] = session_getenv("QMAILQUEUE");
if (qqargs[0] == 0) qqargs[0] = "bin/qmail-queue";
if ((qh = session_getenv("QMAILHOME")) == 0)
qh = conf_qmail;
if (chdir(qh) == -1) return &resp_no_chdir;
sig_pipe_block();
if (pipe(qqepipe) == -1) return &resp_no_pipe;
if (session.fd < 0) {
if (pipe(qqmpipe) == -1) {
close_qqpipe();
return &resp_no_pipe;
}
if (start_qq(qqmpipe[0], qqepipe[0]) == -1)
return &resp_no_fork;
}
databytes = 0;
return 0;
(void)fd;
}
static int retry_write(int fd, const char* bytes, unsigned long len)
{
while (len) {
unsigned long written = write(fd, bytes, len);
if (written == (unsigned long)-1) return 0;
len -= written;
bytes += written;
}
return 1;
}
static const response* data_block(const char* bytes, unsigned long len)
{
if (session.fd < 0) {
if (!retry_write(qqmpipe[1], bytes, len))
return &resp_no_write;
databytes += len;
}
return 0;
}
static void parse_status(int status, response* resp)
{
char var[20];
const char* message;
resp->number = (status <= 40 && status >= 11) ? 554 : 451;
memcpy(var, "QQERRMSG_", 9);
strcpy(var+9, utoa(status));
if ((message = session_getenv(var)) == 0) {
switch (status) {
case 11: message = "5.1.3 Address too long."; break;
case 31: message = "5.3.0 Message refused."; break;
case 51: message = "4.3.0 Out of memory."; break;
case 52: message = "4.3.0 Timeout."; break;
case 53: message = "4.3.0 Write error (queue full?)."; break;
case 54: message = "4.3.0 Unable to read the message or envelope."; break;
case 55: message = "4.3.0 Unable to read a configuration file."; break;
case 56: message = "4.3.0 Network problem."; break;
case 61: message = "4.3.0 Problem with the qmail home directory."; break;
case 62: message = "4.3.0 Problem with the qmail queue directory."; break;
case 63: message = "4.3.0 Problem with queue/pid."; break;
case 64: message = "4.3.0 Problem with queue/mess."; break;
case 65: message = "4.3.0 Problem with queue/intd."; break;
case 66: message = "4.3.0 Problem with queue/todo."; break;
case 71: message = "4.3.0 Message refused by mail server."; break;
case 72: message = "4.3.0 Connection to mail server timed out."; break;
case 73: message = "4.3.0 Connection to mail server rejected."; break;
case 74: message = "4.3.0 Communication with mail server failed."; break;
case 81: message = "4.3.0 Internal qmail-queue bug."; break;
case 91: message = "4.3.0 Envelope format error."; break;
default:
message = (resp->number >= 500)
? "5.3.0 Message rejected by qmail-queue."
: "4.3.0 Temporary qmail-queue failure.";
}
}
resp->message = message;
}
static const response* message_end(int fd)
{
static response resp;
int status;
struct stat st;
if (session.fd < 0) {
close(qqmpipe[1]);
qqmpipe[1] = -1;
}
else {
if (lseek(session.fd, 0, SEEK_SET) != 0)
return &resp_internal;
if (fstat(session.fd, &st) != 0)
return &resp_internal;
databytes = st.st_size;
if (start_qq(session.fd, qqepipe[0]) == -1)
return &resp_no_fork;
}
if (!retry_write(qqepipe[1], buffer.s, buffer.len+1)) return &resp_no_write;
close_qqpipe();
if (waitpid(qqpid, &status, WUNTRACED) == -1) return &resp_qq_crashed;
if (!WIFEXITED(status)) return &resp_qq_crashed;
if ((status = WEXITSTATUS(status)) != 0)
parse_status(status, &resp);
else {
str_copys(&buffer, "2.6.0 Accepted message qp ");
str_catu(&buffer, qqpid);
str_cats(&buffer, " bytes ");
str_catu(&buffer, databytes);
msg1(buffer.s);
resp.number = 250;
resp.message = buffer.s;
}
return &resp;
(void)fd;
}
struct plugin backend = {
.version = PLUGIN_VERSION,
.reset = reset,
.sender = do_sender,
.recipient = do_recipient,
.data_start = data_start,
.data_block = data_block,
.message_end = message_end,
};
syntax highlighted by Code2HTML, v. 0.9.1