1 #include <bglibs/sysdeps.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/stat.h>
5 #include <sys/wait.h>
6 #include <unistd.h>
7 #include <bglibs/misc.h>
8 #include <bglibs/msg.h>
9 #include <bglibs/sig.h>
10 #include "mailfront.h"
11 #include "conf_qmail.c"
12
13 static RESPONSE(no_write,451,"4.3.0 Writing data to qmail-queue failed.");
14 static RESPONSE(no_pipe,451,"4.3.0 Could not open pipe to qmail-queue.");
15 static RESPONSE(no_fork,451,"4.3.0 Could not start qmail-queue.");
16 static RESPONSE(no_chdir,451,"4.3.0 Could not change to the qmail directory.");
17 static RESPONSE(qq_crashed,451,"4.3.0 qmail-queue crashed.");
18
19 static str buffer;
20 static unsigned long databytes;
21
22 static const char* qqargs[2] = { 0, 0 };
23 static int qqpid = -1;
24 static int qqepipe[2] = { -1, -1 };
25 static int qqmpipe[2] = { -1, -1 };
26
close_qqpipe(void)27 static void close_qqpipe(void)
28 {
29 if (qqepipe[0] != -1) close(qqepipe[0]);
30 if (qqepipe[1] != -1) close(qqepipe[1]);
31 if (qqmpipe[0] != -1) close(qqmpipe[0]);
32 if (qqmpipe[1] != -1) close(qqmpipe[1]);
33 qqepipe[0] = qqepipe[1] = qqmpipe[0] = qqmpipe[1] = -1;
34 }
35
reset(void)36 static const response* reset(void)
37 {
38 close_qqpipe();
39 str_truncate(&buffer, 0);
40 return 0;
41 }
42
do_sender(str * sender,str * params)43 static const response* do_sender(str* sender, str* params)
44 {
45 if (!str_catc(&buffer, 'F') ||
46 !str_cat(&buffer, sender) ||
47 !str_catc(&buffer, 0)) return &resp_oom;
48 return 0;
49 (void)params;
50 }
51
do_recipient(str * recipient,str * params)52 static const response* do_recipient(str* recipient, str* params)
53 {
54 if (!str_catc(&buffer, 'T') ||
55 !str_cat(&buffer, recipient) ||
56 !str_catc(&buffer, 0)) return &resp_oom;
57 return 0;
58 (void)params;
59 }
60
start_qq(int msgfd,int envfd)61 static const response *start_qq(int msgfd, int envfd)
62 {
63 const char* qh;
64
65 qqargs[0] = session_getenv("QMAILQUEUE");
66 if (qqargs[0] == 0) qqargs[0] = "bin/qmail-queue";
67
68 if ((qh = session_getenv("QMAILHOME")) == 0)
69 qh = conf_qmail;
70 if (chdir(qh) == -1) return &resp_no_chdir;
71
72 if ((qqpid = fork()) == -1) {
73 close_qqpipe();
74 return &resp_no_fork;
75 }
76
77 if (qqpid == 0) {
78 if (!session_exportenv()) exit(51);
79 if (dup2(msgfd, 0) == -1) exit(120);
80 if (dup2(envfd, 1) == -1) exit(120);
81 close_qqpipe();
82 execvp(qqargs[0], (char**)qqargs);
83 exit(120);
84 }
85 return 0;
86 }
87
data_start(int fd)88 static const response* data_start(int fd)
89 {
90 const response* resp_qq;
91
92 sig_pipe_block();
93
94 if (pipe(qqepipe) == -1) return &resp_no_pipe;
95
96 if (fd < 0) {
97 if (pipe(qqmpipe) == -1) {
98 close_qqpipe();
99 return &resp_no_pipe;
100 }
101 resp_qq = start_qq(qqmpipe[0], qqepipe[0]);
102 if (resp_qq != 0) {
103 return resp_qq;
104 }
105 }
106
107 databytes = 0;
108 return 0;
109 (void)fd;
110 }
111
retry_write(int fd,const char * bytes,unsigned long len)112 static int retry_write(int fd, const char* bytes, unsigned long len)
113 {
114 while (len) {
115 unsigned long written = write(fd, bytes, len);
116 if (written == (unsigned long)-1) return 0;
117 len -= written;
118 bytes += written;
119 }
120 return 1;
121 }
122
data_block(const char * bytes,unsigned long len)123 static const response* data_block(const char* bytes, unsigned long len)
124 {
125 if (qqmpipe[1] >= 0) {
126 if (!retry_write(qqmpipe[1], bytes, len))
127 return &resp_no_write;
128 databytes += len;
129 }
130 return 0;
131 }
132
parse_status(int status,response * resp)133 static void parse_status(int status, response* resp)
134 {
135 char var[20];
136 const char* message;
137 resp->number = (status <= 40 && status >= 11) ? 554 : 451;
138 memcpy(var, "QQERRMSG_", 9);
139 strcpy(var+9, utoa(status));
140 if ((message = session_getenv(var)) == 0) {
141 switch (status) {
142 case 11: message = "5.1.3 Address too long."; break;
143 case 31: message = "5.3.0 Message refused."; break;
144 case 51: message = "4.3.0 Out of memory."; break;
145 case 52: message = "4.3.0 Timeout."; break;
146 case 53: message = "4.3.0 Write error (queue full?)."; break;
147 case 54: message = "4.3.0 Unable to read the message or envelope."; break;
148 case 55: message = "4.3.0 Unable to read a configuration file."; break;
149 case 56: message = "4.3.0 Network problem."; break;
150 case 61: message = "4.3.0 Problem with the qmail home directory."; break;
151 case 62: message = "4.3.0 Problem with the qmail queue directory."; break;
152 case 63: message = "4.3.0 Problem with queue/pid."; break;
153 case 64: message = "4.3.0 Problem with queue/mess."; break;
154 case 65: message = "4.3.0 Problem with queue/intd."; break;
155 case 66: message = "4.3.0 Problem with queue/todo."; break;
156 case 71: message = "4.3.0 Message refused by mail server."; break;
157 case 72: message = "4.3.0 Connection to mail server timed out."; break;
158 case 73: message = "4.3.0 Connection to mail server rejected."; break;
159 case 74: message = "4.3.0 Communication with mail server failed."; break;
160 case 81: message = "4.3.0 Internal qmail-queue bug."; break;
161 case 91: message = "4.3.0 Envelope format error."; break;
162 default:
163 message = (resp->number >= 500)
164 ? "5.3.0 Message rejected by qmail-queue."
165 : "4.3.0 Temporary qmail-queue failure.";
166 }
167 }
168 resp->message = message;
169 }
170
message_end(int fd)171 static const response* message_end(int fd)
172 {
173 static response resp;
174 const response* resp_qq;
175
176 int status;
177 struct stat st;
178
179 if (fd < 0) {
180 close(qqmpipe[1]);
181 qqmpipe[1] = -1;
182 }
183 else {
184 if (lseek(fd, 0, SEEK_SET) != 0)
185 return &resp_internal;
186 if (fstat(fd, &st) != 0)
187 return &resp_internal;
188 databytes = st.st_size;
189 resp_qq = start_qq(fd, qqepipe[0]);
190 if (resp_qq != 0) {
191 return resp_qq;
192 }
193 }
194 if (!retry_write(qqepipe[1], buffer.s, buffer.len+1)) return &resp_no_write;
195 close_qqpipe();
196 if (waitpid(qqpid, &status, WUNTRACED) == -1) return &resp_qq_crashed;
197 if (!WIFEXITED(status)) return &resp_qq_crashed;
198
199 if ((status = WEXITSTATUS(status)) != 0)
200 parse_status(status, &resp);
201 else {
202 str_copys(&buffer, "2.6.0 Accepted message qp ");
203 str_catu(&buffer, qqpid);
204 str_cats(&buffer, " bytes ");
205 str_catu(&buffer, databytes);
206 msg1(buffer.s);
207 resp.number = 250;
208 resp.message = buffer.s;
209 }
210 return &resp;
211 (void)fd;
212 }
213
214 struct plugin backend = {
215 .version = PLUGIN_VERSION,
216 .reset = reset,
217 .sender = do_sender,
218 .recipient = do_recipient,
219 .data_start = data_start,
220 .data_block = data_block,
221 .message_end = message_end,
222 };
223