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