1 #include "commands.h"
2 #include "sig.h"
3 #include "auto_qmail.h"
4 #include "qmail.h"
5 #include "readwrite.h"
6 #include "timeoutread.h"
7 #include "timeoutwrite.h"
8 #include "stralloc.h"
9 #include "substdio.h"
10 #include "config.h"
11 #include "env.h"
12 #include "exit.h"
13 #include "error.h"
14 #include "str.h"
15 #include "mess822.h"
16 #include "tai.h"
17 #include "caltime.h"
18 #include "cdb.h"
19
20 int timeout = 1200;
21
safewrite(fd,buf,len)22 int safewrite(fd,buf,len) int fd; char *buf; int len;
23 {
24 int r;
25 r = timeoutwrite(timeout,fd,buf,len);
26 if (r <= 0) _exit(1);
27 return r;
28 }
29
30 char ssoutbuf[512];
31 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
32
flush()33 void flush() { substdio_flush(&ssout); }
out(s)34 void out(s) char *s; { substdio_puts(&ssout,s); }
35
die_read()36 void die_read() { _exit(1); }
nomem()37 void nomem() { out("451 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
die_config()38 void die_config() { out("451 unable to read configuration (#4.3.0)\r\n"); flush(); _exit(1); }
smtp_quit()39 void smtp_quit() { out("221 ofmipd.local\r\n"); flush(); _exit(0); }
smtp_help()40 void smtp_help() { out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); }
smtp_noop()41 void smtp_noop() { out("250 ok\r\n"); }
smtp_vrfy()42 void smtp_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
smtp_unimpl()43 void smtp_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
err_syntax()44 void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
err_wantmail()45 void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
err_wantrcpt()46 void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
err_qqt()47 void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
err_cdb()48 void err_cdb() { out("451 unable to read cdb (#4.3.0)\r\n"); }
49
50 config_str rewrite = CONFIG_STR;
51 stralloc idappend = {0};
52
53 stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
54 stralloc rwaddr = {0};
55
addrparse(arg)56 int addrparse(arg)
57 char *arg;
58 {
59 int i;
60 char ch;
61 char terminator;
62 int flagesc;
63 int flagquoted;
64
65 terminator = '>';
66 i = str_chr(arg,'<');
67 if (arg[i])
68 arg += i + 1;
69 else { /* partner should go read rfc 821 */
70 terminator = ' ';
71 arg += str_chr(arg,':');
72 if (*arg == ':') ++arg;
73 while (*arg == ' ') ++arg;
74 }
75
76 if (*arg == '@') while (*arg) if (*arg++ == ':') break;
77
78 if (!stralloc_copys(&addr,"")) nomem();
79 flagesc = 0;
80 flagquoted = 0;
81 for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */
82 if (flagesc) {
83 if (!stralloc_append(&addr,&ch)) nomem();
84 flagesc = 0;
85 }
86 else {
87 if (!flagquoted && (ch == terminator)) break;
88 switch(ch) {
89 case '\\': flagesc = 1; break;
90 case '"': flagquoted = !flagquoted; break;
91 default: if (!stralloc_append(&addr,&ch)) nomem();
92 }
93 }
94 }
95
96 if (!rewritehost_addr(&rwaddr,addr.s,addr.len,config_data(&rewrite))) nomem();
97
98 return rwaddr.len < 900;
99 }
100
101 char *fncdb;
102 int fdcdb;
103 stralloc cdbresult = {0};
104
105 int seenmail = 0;
106 char *name; /* defined if seenmail; points into cdbresult */
107
108 stralloc mailfrom = {0};
109 stralloc rcptto = {0};
110
smtp_helo(arg)111 void smtp_helo(arg) char *arg;
112 {
113 seenmail = 0;
114 out("250 ofmipd.local\r\n");
115 }
smtp_ehlo(arg)116 void smtp_ehlo(arg) char *arg;
117 {
118 seenmail = 0;
119 out("250-ofmipd.local\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
120 }
smtp_rset()121 void smtp_rset()
122 {
123 seenmail = 0;
124 out("250 flushed\r\n");
125 }
smtp_mail(arg)126 void smtp_mail(arg) char *arg;
127 {
128 if (!addrparse(arg)) { err_syntax(); return; }
129
130 name = 0;
131 if (fncdb) {
132 uint32 dlen;
133 int r;
134
135 r = cdb_seek(fdcdb,rwaddr.s,rwaddr.len,&dlen);
136 if (r == -1) { err_cdb(); return; }
137 if (r) {
138 if (!stralloc_ready(&cdbresult,(unsigned int) dlen)) nomem();
139 cdbresult.len = dlen;
140 name = cdbresult.s;
141 if (cdb_bread(fdcdb,name,cdbresult.len) == -1) { err_cdb(); return; }
142 r = byte_chr(name,cdbresult.len,'\0');
143 if (r == cdbresult.len) { err_cdb(); return; }
144 if (!stralloc_copyb(&rwaddr,cdbresult.s + r + 1,cdbresult.len - r - 1)) nomem();
145 }
146 }
147
148 if (!stralloc_copy(&mailfrom,&rwaddr)) nomem();
149 if (!stralloc_0(&mailfrom)) nomem();
150 if (!stralloc_copys(&rcptto,"")) nomem();
151 seenmail = 1;
152 out("250 ok\r\n");
153 }
smtp_rcpt(arg)154 void smtp_rcpt(arg) char *arg; {
155 if (!seenmail) { err_wantmail(); return; }
156 if (!addrparse(arg)) { err_syntax(); return; }
157 if (!stralloc_0(&rwaddr)) nomem();
158 if (!stralloc_cats(&rcptto,"T")) nomem();
159 if (!stralloc_cats(&rcptto,rwaddr.s)) nomem();
160 if (!stralloc_0(&rcptto)) nomem();
161 out("250 ok\r\n");
162 }
163
164 struct qmail qqt;
put(buf,len)165 void put(buf,len) char *buf; int len; { qmail_put(&qqt,buf,len); }
puts(buf)166 void puts(buf) char *buf; { qmail_puts(&qqt,buf); }
167
168 stralloc tmp = {0};
169 stralloc tmp2 = {0};
170
rewritelist(list)171 void rewritelist(list)
172 stralloc *list;
173 {
174 if (!rewritehost_list(&tmp,list->s,list->len,config_data(&rewrite))) nomem();
175 if (!stralloc_copy(list,&tmp)) nomem();
176 }
177
putlist(name,list)178 void putlist(name,list)
179 char *name;
180 stralloc *list;
181 {
182 if (!list->len) return;
183 if (!mess822_quotelist(&tmp,list)) nomem();
184 if (!mess822_fold(&tmp2,&tmp,name,78)) nomem();
185 put(tmp2.s,tmp2.len);
186 }
187
188 mess822_time datastart;
189 stralloc datastamp = {0};
190
191 mess822_time date;
192 stralloc to = {0};
193 stralloc cc = {0};
194 stralloc nrudt = {0};
195 stralloc from = {0};
196 stralloc headersender = {0};
197 stralloc replyto = {0};
198 stralloc mailreplyto = {0};
199 stralloc followupto = {0};
200
201 stralloc msgid = {0};
202 stralloc top = {0};
203 stralloc bottom = {0};
204
205 mess822_header h = MESS822_HEADER;
206 mess822_action a[] = {
207 { "date", 0, 0, 0, 0, &date }
208 , { "to", 0, 0, 0, &to, 0 }
209 , { "cc", 0, 0, 0, &cc, 0 }
210 , { "notice-requested-upon-delivery-to", 0, 0, 0, &nrudt, 0 }
211 , { "from", 0, 0, 0, &from, 0 }
212 , { "sender", 0, 0, 0, &headersender, 0 }
213 , { "reply-to", 0, 0, 0, &replyto, 0 }
214 , { "mail-reply-to", 0, 0, 0, &mailreplyto, 0 }
215 , { "mail-followup-to", 0, 0, 0, &followupto, 0 }
216 , { "message-id", 0, &msgid, 0, 0, 0 }
217 , { "received", 0, &top, 0, 0, 0 }
218 , { "delivered-to", 0, &top, 0, 0, 0 }
219 , { "errors-to", 0, &top, 0, 0, 0 }
220 , { "return-receipt-to", 0, &top, 0, 0, 0 }
221 , { "resent-sender", 0, &top, 0, 0, 0 }
222 , { "resent-from", 0, &top, 0, 0, 0 }
223 , { "resent-reply-to", 0, &top, 0, 0, 0 }
224 , { "resent-to", 0, &top, 0, 0, 0 }
225 , { "resent-cc", 0, &top, 0, 0, 0 }
226 , { "resent-bcc", 0, &top, 0, 0, 0 }
227 , { "resent-date", 0, &top, 0, 0, 0 }
228 , { "resent-message-id", 0, &top, 0, 0, 0 }
229 , { "bcc", 0, 0, 0, 0, 0 }
230 , { "return-path", 0, 0, 0, 0, 0 }
231 , { "apparently-to", 0, 0, 0, 0, 0 }
232 , { "content-length", 0, 0, 0, 0, 0 }
233 , { 0, 0, &bottom, 0, 0, 0 }
234 } ;
235
finishheader()236 void finishheader()
237 {
238 if (!mess822_end(&h)) nomem();
239
240 if (name) from.len = 0;
241
242 rewritelist(&to);
243 rewritelist(&cc);
244 rewritelist(&nrudt);
245 rewritelist(&from);
246 rewritelist(&headersender);
247 rewritelist(&replyto);
248 rewritelist(&mailreplyto);
249 rewritelist(&followupto);
250
251 put(top.s,top.len);
252
253 if (!date.known) date = datastart;
254 if (!mess822_date(&tmp,&date)) nomem();
255 puts("Date: ");
256 put(tmp.s,tmp.len);
257 puts("\n");
258
259 if (!msgid.len) {
260 static int idcounter = 0;
261
262 if (!stralloc_copys(&msgid,"Message-ID: <")) nomem();
263 if (!stralloc_catlong(&msgid,date.ct.date.year)) nomem();
264 if (!stralloc_catint0(&msgid,date.ct.date.month,2)) nomem();
265 if (!stralloc_catint0(&msgid,date.ct.date.day,2)) nomem();
266 if (!stralloc_catint0(&msgid,date.ct.hour,2)) nomem();
267 if (!stralloc_catint0(&msgid,date.ct.minute,2)) nomem();
268 if (!stralloc_catint0(&msgid,date.ct.second,2)) nomem();
269 if (!stralloc_cats(&msgid,".")) nomem();
270 if (!stralloc_catint(&msgid,++idcounter)) nomem();
271 if (!stralloc_cat(&msgid,&idappend)) nomem();
272 if (!stralloc_cats(&msgid,">\n")) nomem();
273 }
274 put(msgid.s,msgid.len);
275
276 putlist("From: ",&from);
277 if (!from.len) {
278 puts("From: ");
279 if (!mess822_quote(&tmp,mailfrom.s,name)) nomem();
280 put(tmp.s,tmp.len);
281 puts("\n");
282 }
283
284 putlist("Sender: ",&headersender);
285 putlist("Reply-To: ",&replyto);
286 putlist("Mail-Reply-To: ",&mailreplyto);
287 putlist("Mail-Followup-To: ",&followupto);
288 if (!to.len && !cc.len)
289 puts("Cc: recipient list not shown: ;\n");
290 putlist("To: ",&to);
291 putlist("Cc: ",&cc);
292 putlist("Notice-Requested-Upon-Delivery-To: ",&nrudt);
293
294 put(bottom.s,bottom.len);
295 }
296
saferead(fd,buf,len)297 int saferead(fd,buf,len) int fd; char *buf; int len;
298 {
299 int r;
300 flush();
301 r = timeoutread(timeout,fd,buf,len);
302 if (r <= 0) die_read();
303 return r;
304 }
305
306 char ssinbuf[1024];
307 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
308
309 stralloc line = {0};
310 int match;
311
blast()312 void blast()
313 {
314 int flagheader = 1;
315 int i;
316
317 if (!mess822_begin(&h,a)) nomem();
318
319 for (;;) {
320 if (getln(&ssin,&line,&match,'\n') == -1) die_read();
321 if (!match) die_read();
322
323 --line.len;
324 if (line.len && (line.s[line.len - 1] == '\r')) --line.len;
325 if (line.len && (line.s[0] == '.')) {
326 --line.len;
327 if (!line.len) break;
328 for (i = 0;i < line.len;++i) line.s[i] = line.s[i + 1];
329 }
330 line.s[line.len++] = '\n';
331
332 if (flagheader)
333 if (!mess822_ok(&line)) {
334 finishheader();
335 flagheader = 0;
336 if (line.len > 1) put("\n",1);
337 }
338 if (!flagheader)
339 put(line.s,line.len);
340 else
341 if (!mess822_line(&h,&line)) nomem();
342 }
343
344 if (flagheader)
345 finishheader();
346 }
347
348 stralloc received = {0};
349
smtp_data()350 void smtp_data() {
351 struct tai now;
352 char *qqx;
353
354 tai_now(&now);
355 caltime_utc(&datastart.ct,&now,(int *) 0,(int *) 0);
356 datastart.known = 1;
357 if (!mess822_date(&datastamp,&datastart)) nomem();
358
359 if (!seenmail) { err_wantmail(); return; }
360 if (!rcptto.len) { err_wantrcpt(); return; }
361 seenmail = 0;
362 if (qmail_open(&qqt) == -1) { err_qqt(); return; }
363 out("354 go ahead\r\n");
364
365 qmail_put(&qqt,received.s,received.len);
366 qmail_put(&qqt,datastamp.s,datastamp.len);
367 qmail_puts(&qqt,"\n");
368 blast();
369 qmail_from(&qqt,mailfrom.s);
370 qmail_put(&qqt,rcptto.s,rcptto.len);
371
372 qqx = qmail_close(&qqt);
373 if (!*qqx) { out("250 ok\r\n"); return; }
374 if (*qqx == 'D') out("554 "); else out("451 ");
375 out(qqx + 1);
376 out("\r\n");
377 }
378
safecats(out,in)379 void safecats(out,in)
380 stralloc *out;
381 char *in;
382 {
383 char ch;
384 while (ch = *in++) {
385 if (ch < 33) ch = '?';
386 if (ch > 126) ch = '?';
387 if (ch == '(') ch = '?';
388 if (ch == ')') ch = '?';
389 if (ch == '@') ch = '?';
390 if (ch == '\\') ch = '?';
391 if (!stralloc_append(out,&ch)) nomem();
392 }
393 }
394
received_init()395 void received_init()
396 {
397 char *x;
398
399 if (!stralloc_copys(&received,"Received: (ofmipd ")) nomem();
400 x = env_get("TCPREMOTEINFO");
401 if (x) {
402 safecats(&received,x);
403 if (!stralloc_append(&received,"@")) nomem();
404 }
405 x = env_get("TCPREMOTEIP");
406 if (!x) x = "unknown";
407 safecats(&received,x);
408 if (!stralloc_cats(&received,"); ")) nomem();
409 }
410
411 struct commands smtpcommands[] = {
412 { "rcpt", smtp_rcpt, 0 }
413 , { "mail", smtp_mail, 0 }
414 , { "data", smtp_data, flush }
415 , { "quit", smtp_quit, flush }
416 , { "helo", smtp_helo, flush }
417 , { "ehlo", smtp_ehlo, flush }
418 , { "rset", smtp_rset, 0 }
419 , { "help", smtp_help, flush }
420 , { "noop", smtp_noop, flush }
421 , { "vrfy", smtp_vrfy, flush }
422 , { 0, smtp_unimpl, flush }
423 } ;
424
main(argc,argv)425 void main(argc,argv)
426 int argc;
427 char **argv;
428 {
429 sig_pipeignore();
430
431 fncdb = argv[1];
432 if (fncdb) {
433 fdcdb = open_read(fncdb);
434 if (fdcdb == -1) die_config();
435 }
436
437 received_init();
438 if (leapsecs_init() == -1) die_config();
439 if (chdir(auto_qmail) == -1) die_config();
440 if (rwhconfig(&rewrite,&idappend) == -1) die_config();
441
442 out("220 ofmipd.local ESMTP\r\n");
443 commands(&ssin,&smtpcommands);
444 nomem();
445 }
446