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