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