diff --git Makefile Makefile index 80a4193..7a95c9e 100644 --- Makefile +++ Makefile @@ -1447,7 +1447,7 @@ substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \ ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ - str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` -lidn2 qmail-remote.0: \ qmail-remote.8 diff --git qmail-remote.c qmail-remote.c index c8746bf..f58111c 100644 --- qmail-remote.c +++ qmail-remote.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "sig.h" #include "stralloc.h" #include "substdio.h" @@ -42,7 +43,9 @@ stralloc helohost = {0}; stralloc routes = {0}; struct constmap maproutes; stralloc host = {0}; +stralloc asciihost = {0}; stralloc sender = {0}; +int smtputf8 = 0; saa reciplist = {0}; @@ -156,6 +159,7 @@ char smtpfrombuf[128]; substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof smtpfrombuf); stralloc smtptext = {0}; +stralloc firstpart = {0}; void get(ch) char *ch; @@ -308,6 +312,8 @@ void blast() int r; char ch; + substdio_put(&smtpto,firstpart.s,firstpart.len); + for (;;) { r = substdio_get(&ssin,&ch,1); if (r == 0) break; @@ -530,6 +536,102 @@ int tls_init() stralloc recip = {0}; +int containsutf8(p, l) unsigned char * p; int l; +{ + int i = 0; + while (i 127) return 1; + return 0; +} + +int utf8message; + +void checkutf8message() +{ + int pos; + int i; + int r; + char ch; + int state; + + if (containsutf8(sender.s, sender.len)) { utf8message = 1; return; } + for (i = 0;i < reciplist.len;++i) + if (containsutf8(reciplist.sa[i].s, reciplist.sa[i].len)) { + utf8message = 1; + return; + } + + state = 0; + pos = 0; + for (;;) { + r = substdio_get(&ssin,&ch,1); + if (r == 0) break; + if (r == -1) temp_read(); + + if (ch == '\n' && !stralloc_cats(&firstpart,"\r")) temp_nomem(); + if (!stralloc_append(&firstpart,&ch)) temp_nomem(); + + if (ch == '\r') + continue; + if (ch == '\t') + ch = ' '; + + switch (state) { + case 6: /* in Received, at LF but before WITH clause */ + if (ch == ' ') { state = 3; pos = 1; continue; } + state = 0; + /* FALL THROUGH */ + + case 0: /* start of header field */ + if (ch == '\n') return; + state = 1; + pos = 0; + /* FALL THROUGH */ + + case 1: /* partway through "Received:" */ + if (ch != "RECEIVED:"[pos] && ch != "received:"[pos]) { state = 2; continue; } + if (++pos == 9) { state = 3; pos = 0; } + continue; + + case 2: /* other header field */ + if (ch == '\n') state = 0; + continue; + + case 3: /* in Received, before WITH clause or partway though " with " */ + if (ch == '\n') { state = 6; continue; } + if (ch != " WITH "[pos] && ch != " with "[pos]) { pos = 0; continue; } + if (++pos == 6) { state = 4; pos = 0; } + continue; + + case 4: /* in Received, having seen with, before the argument */ + if (pos == 0 && (ch == ' ' || ch == '\t')) continue; + if (ch != "UTF8"[pos] && ch != "utf8"[pos]) { state = 5; continue; } + if(++pos == 4) { utf8message = 1; state = 5; continue; } + continue; + + case 5: /* after the RECEIVED WITH argument */ + /* blast() assumes that it copies whole lines */ + if (ch == '\n') return; + state = 1; + pos = 0; + continue; + } + } +} + +void checkutf8server() +{ + stralloc *sa; + unsigned int len; + + sa = ehlokw.sa; + len = ehlokw.len; + + for (; len && case_diffs(sa->s, "SMTPUTF8"); ++sa, --len); + + if (len) smtputf8 = 1; +} + void smtp() { unsigned long code; @@ -583,9 +685,15 @@ void smtp() } #endif + checkutf8message(); + checkutf8server(); + if (utf8message && !smtputf8) quit("DConnected to "," but server does not support unicode in email addresses"); + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); - substdio_puts(&smtpto,">\r\n"); + substdio_puts(&smtpto,">"); + if (utf8message) substdio_puts(&smtpto," SMTPUTF8"); + substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); code = smtpcode(); if (code >= 500) quit("DConnected to "," but sender was rejected"); @@ -714,6 +822,15 @@ char **argv; relayhost[i] = 0; } if (!stralloc_copys(&host,relayhost)) temp_nomem(); + } else { + char * ascii = 0; + host.s[host.len] = '\0'; + switch (idn2_lookup_u8(host.s, (uint8_t**)&ascii, IDN2_NFC_INPUT)) { + case IDN2_OK: break; + case IDN2_MALLOC: temp_nomem(); + default: perm_dns(); + } + if (!stralloc_copys(&asciihost, ascii)) temp_nomem(); } @@ -735,7 +852,7 @@ char **argv; random = now() + (getpid() << 16); - switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) { + switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&asciihost,random)) { case DNS_MEM: temp_nomem(); case DNS_SOFT: temp_dns(); case DNS_HARD: perm_dns(); diff --git qmail-smtpd.c qmail-smtpd.c index 54df00c..dca7aed 100644 --- qmail-smtpd.c +++ qmail-smtpd.c @@ -221,6 +221,28 @@ int seenmail = 0; int flagbarf; /* defined if seenmail */ stralloc mailfrom = {0}; stralloc rcptto = {0}; +stralloc mfparms = {0}; +int smtputf8 = 0; + +void mailfrom_parms(arg) char *arg; +{ + int i; + int len; + + len = str_len(arg); + if (!stralloc_copys(&mfparms,"")) die_nomem(); + i = byte_chr(arg,len,'>'); + if (i > 4 && i < len) { + while (len) { + arg++; len--; + if (*arg == ' ' || *arg == '\0' ) { + if (case_starts(mfparms.s,"SMTPUTF8")) smtputf8 = 1; + } + else + if (!stralloc_catb(&mfparms,arg,1)) die_nomem(); + } + } +} void smtp_helo(arg) char *arg; { @@ -229,7 +251,7 @@ void smtp_helo(arg) char *arg; } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250-SMTPUTF8\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset(arg) char *arg; @@ -240,6 +262,7 @@ void smtp_rset(arg) char *arg; void smtp_mail(arg) char *arg; { if (!addrparse(arg)) { err_syntax(); return; } + mailfrom_parms(arg); flagbarf = bmfcheck(); seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); @@ -377,8 +400,8 @@ void smtp_data(arg) char *arg; { if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + + received(&qqt,smtputf8 ? "UTF8SMTP" : "SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt);