1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include "strerr.h"
4 #include "getln.h"
5 #include "substdio.h"
6 #include "stralloc.h"
7 #include "subfd.h"
8 #include "readwrite.h"
9 #include "sig.h"
10 #include "timeoutread.h"
11 #include "timeoutwrite.h"
12 #include "fd.h"
13 #include "fork.h"
14 #include "exit.h"
15 #include "open.h"
16 #include "wait.h"
17 #include "str.h"
18 #include "fmt.h"
19 #include "env.h"
20
21 #define FATAL "serialqmtp: fatal: "
22
23 char *remoteip;
24
25 char *prefix;
26 unsigned int prefixlen;
27
28 char netbuf[2048];
29 substdio ssnet; /* in child: write 7; in parent: read 6 */
30
31 stralloc line = {0};
32 stralloc fn = {0};
33 int match;
34
35
36 /* ------------------------------------------------------------------- CHILD */
37
safewrite(fd,buf,len)38 int safewrite(fd,buf,len) int fd; char *buf; int len;
39 {
40 int w;
41 w = timeoutwrite(73,fd,buf,len);
42 if (w <= 0) _exit(31);
43 return w;
44 }
45
46 char num[FMT_ULONG];
47 char num2[FMT_ULONG];
48 stralloc recipient = {0};
49 stralloc sender = {0};
50
51 char inbuf[2048];
52
doit(fd)53 void doit(fd)
54 int fd;
55 {
56 struct stat st;
57 unsigned long len;
58 int len2;
59 char *x;
60 int n;
61 substdio ssin;
62
63 if (fstat(fd,&st) == -1) _exit(35);
64 len = 0;
65
66 substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
67
68 if (getln(&ssin,&line,&match,'\n') == -1) _exit(32);
69 len += line.len;
70 if (!match) return;
71 if (!stralloc_starts(&line,"Return-Path: <")) return;
72 if (line.s[line.len - 2] != '>') return;
73 if (line.s[line.len - 1] != '\n') return;
74 if (!stralloc_copyb(&sender,line.s + 14,line.len - 16)) _exit(36);
75
76 if (getln(&ssin,&line,&match,'\n') == -1) _exit(32);
77 len += line.len;
78 if (!match) return;
79 if (!stralloc_starts(&line,"Delivered-To: ")) return;
80 if (line.s[line.len - 1] != '\n') return;
81 if (!stralloc_copyb(&recipient,line.s + 14,line.len - 15)) _exit(36);
82
83 if (!stralloc_starts(&recipient,prefix)) return;
84
85 if (st.st_size < len) return; /* okay, who's the wise guy? */
86 len = st.st_size + 1 - len;
87
88 if (substdio_putflush(subfdoutsmall,fn.s,fn.len) == -1) _exit(33);
89 /* must occur before writes to net, to avoid deadlock */
90
91 substdio_put(&ssnet,num,fmt_ulong(num,len));
92 substdio_put(&ssnet,":\n",2);
93 --len; /* for the \n */
94 while (len > 0) {
95 n = substdio_feed(&ssin);
96 if (n <= 0) _exit(32); /* wise guy again */
97 x = substdio_PEEK(&ssin);
98 substdio_put(&ssnet,x,n);
99 substdio_SEEK(&ssin,n);
100 len -= n;
101 }
102 substdio_put(&ssnet,",",1);
103
104 len = sender.len;
105 substdio_put(&ssnet,num,fmt_ulong(num,len));
106 substdio_put(&ssnet,":",1);
107 substdio_put(&ssnet,sender.s,sender.len);
108 substdio_put(&ssnet,",",1);
109
110 len = recipient.len - prefixlen;
111 len2 = fmt_ulong(num2,len);
112 len += len2 + 2;
113 substdio_put(&ssnet,num,fmt_ulong(num,len));
114 substdio_put(&ssnet,":",1);
115 substdio_put(&ssnet,num2,len2);
116 substdio_put(&ssnet,":",1);
117 substdio_put(&ssnet,recipient.s + prefixlen,recipient.len - prefixlen);
118 substdio_put(&ssnet,",,",2);
119 substdio_flush(&ssnet);
120
121 return;
122 }
123
child()124 void child() /* reading from original stdin, writing to parent */
125 {
126 int fd;
127
128 substdio_fdbuf(&ssnet,safewrite,7,netbuf,sizeof netbuf);
129
130 for (;;) {
131 if (getln(subfdinsmall,&fn,&match,'\0') == -1) _exit(34);
132 if (!match) return;
133 fd = open_read(fn.s);
134 if (fd == -1) _exit(35);
135 doit(fd);
136 close(fd);
137 }
138 }
139
140
141 /* ------------------------------------------------------------------ PARENT */
142
die_proto()143 void die_proto() { strerr_die2x(111,FATAL,"remote protocol violation"); }
die_nomem()144 void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
die_output()145 void die_output() { strerr_die2sys(111,FATAL,"unable to write output: "); }
146
saferead(fd,buf,len)147 int saferead(fd,buf,len) int fd; char *buf; int len;
148 {
149 int r;
150 r = timeoutread(3600,fd,buf,len);
151 if (r == 0) strerr_die2x(111,FATAL,"network read error: end of file");
152 if (r < 0) strerr_die2sys(111,FATAL,"network read error: ");
153 return r;
154 }
155
parent()156 void parent() /* reading from child, writing to original stdout */
157 {
158 unsigned int len;
159 unsigned char ch;
160
161 substdio_fdbuf(&ssnet,saferead,6,netbuf,sizeof netbuf);
162
163 for (;;) {
164 if (getln(subfdinsmall,&fn,&match,'\0') == -1)
165 strerr_die2sys(111,FATAL,"unable to read from child: ");
166 if (!match) return;
167
168 len = 0;
169 for (;;) {
170 substdio_get(&ssnet,&ch,1);
171 if (ch == ':') break;
172 if (len > 200000000) die_proto();
173 if (ch - '0' > 9) die_proto();
174 len = 10 * len + (ch - '0');
175 }
176 if (!len) die_proto();
177 substdio_get(&ssnet,&ch,1); --len;
178 if ((ch != 'Z') && (ch != 'D') && (ch != 'K')) die_proto();
179
180 if (!stralloc_copyb(&line,&ch,1)) die_nomem();
181
182 if (remoteip) {
183 if (!stralloc_cats(&line,remoteip)) die_nomem();
184 if (!stralloc_cats(&line," said: ")) die_nomem();
185 }
186
187 while (len > 0) {
188 substdio_get(&ssnet,&ch,1);
189 if (line.len < 2000) if (!stralloc_append(&line,&ch)) die_nomem();
190 --len;
191 }
192
193 for (len = 0;len < line.len;++len) {
194 ch = line.s[len];
195 if ((ch < 32) || (ch > 126)) line.s[len] = '?';
196 }
197 if (!stralloc_append(&line,"\n")) die_nomem();
198
199 if (substdio_put(subfdoutsmall,fn.s,fn.len) == -1) die_output();
200 if (substdio_put(subfdoutsmall,line.s,line.len) == -1) die_output();
201 if (substdio_flush(subfdoutsmall) == -1) die_output();
202
203 substdio_get(&ssnet,&ch,1);
204 if (ch != ',') die_proto();
205 }
206 }
207
208
209 /* -------------------------------------------------------------------- MAIN */
210
211 int pic2p[2];
212
main(argc,argv)213 void main(argc,argv)
214 int argc;
215 char **argv;
216 {
217 int wstat;
218 int pid;
219
220 sig_pipeignore();
221
222 remoteip = env_get("TCPREMOTEIP");
223
224 prefix = argv[1];
225 if (!prefix)
226 strerr_die1x(100,"serialqmtp: usage: serialqmtp prefix");
227 prefixlen = str_len(prefix);
228
229 if (pipe(pic2p) == -1)
230 strerr_die2sys(111,FATAL,"unable to create pipe: ");
231
232 pid = fork();
233 if (pid == -1)
234 strerr_die2sys(111,FATAL,"unable to fork: ");
235
236 if (!pid) {
237 close(pic2p[0]);
238 fd_move(1,pic2p[1]);
239 child();
240 _exit(0);
241 }
242
243 close(pic2p[1]);
244 fd_move(0,pic2p[0]);
245 parent();
246
247 if (wait_pid(&wstat,pid) == -1)
248 strerr_die2sys(111,FATAL,"unable to get child status: ");
249 if (wait_crashed(wstat))
250 strerr_die2x(111,FATAL,"child crashed");
251 switch(wait_exitcode(wstat)) {
252 case 0: _exit(0);
253 case 31: strerr_die2x(111,FATAL,"unable to write to network");
254 case 32: strerr_die2x(111,FATAL,"unable to read file");
255 case 34: strerr_die2x(111,FATAL,"unable to read input");
256 case 35: strerr_die2x(111,FATAL,"unable to open file");
257 case 36: die_nomem();
258 }
259 strerr_die2x(111,FATAL,"internal error");
260 }
261