13e12c5d1SDavid du Colombier #include "common.h" 23e12c5d1SDavid du Colombier #include "smtp.h" 33e12c5d1SDavid du Colombier #include <ctype.h> 4d9306527SDavid du Colombier #include <mp.h> 5d9306527SDavid du Colombier #include <libsec.h> 6d9306527SDavid du Colombier #include <auth.h> 73e12c5d1SDavid du Colombier 87dd7cddfSDavid du Colombier static char* connect(char*); 9d9306527SDavid du Colombier static char* dotls(char*); 103cf081f0SDavid du Colombier static char* doauth(char*); 11*db7ae703SDavid du Colombier 12219b2ee8SDavid du Colombier void addhostdom(String*, char*); 13219b2ee8SDavid du Colombier String* bangtoat(char*); 147dd7cddfSDavid du Colombier String* convertheader(String*); 153e12c5d1SDavid du Colombier int dBprint(char*, ...); 167dd7cddfSDavid du Colombier int dBputc(int); 17*db7ae703SDavid du Colombier char* data(String*, Biobuf*); 18*db7ae703SDavid du Colombier char* domainify(char*, char*); 197dd7cddfSDavid du Colombier String* fixrouteaddr(String*, Node*, Node*); 20*db7ae703SDavid du Colombier char* getcrnl(String*); 21*db7ae703SDavid du Colombier int getreply(void); 22*db7ae703SDavid du Colombier char* hello(char*, int); 23*db7ae703SDavid du Colombier char* mailfrom(char*); 24*db7ae703SDavid du Colombier int printdate(Node*); 25*db7ae703SDavid du Colombier int printheader(void); 26*db7ae703SDavid du Colombier void putcrnl(char*, int); 27*db7ae703SDavid du Colombier void quit(char*); 28*db7ae703SDavid du Colombier char* rcptto(char*); 29*db7ae703SDavid du Colombier char *rewritezone(char *); 303e12c5d1SDavid du Colombier 317dd7cddfSDavid du Colombier #define Retry "Retry, Temporary Failure" 323e12c5d1SDavid du Colombier #define Giveup "Permanent Failure" 333e12c5d1SDavid du Colombier 343e12c5d1SDavid du Colombier String *reply; /* last reply */ 35219b2ee8SDavid du Colombier String *toline; 36*db7ae703SDavid du Colombier 37a8453668SDavid du Colombier int alarmscale; 38*db7ae703SDavid du Colombier int autistic; 39*db7ae703SDavid du Colombier int debug; /* true if we're debugging */ 403e12c5d1SDavid du Colombier int filter; 41*db7ae703SDavid du Colombier int insecure; 42*db7ae703SDavid du Colombier int last = 'n'; /* last character sent by putcrnl() */ 43*db7ae703SDavid du Colombier int ping; 447dd7cddfSDavid du Colombier int quitting; /* when error occurs in quit */ 45*db7ae703SDavid du Colombier int tryauth; /* Try to authenticate, if supported */ 46*db7ae703SDavid du Colombier int trysecure; /* Try to use TLS if the other side supports it */ 47*db7ae703SDavid du Colombier 487dd7cddfSDavid du Colombier char *quitrv; /* deferred return value when in quit */ 493e12c5d1SDavid du Colombier char ddomain[1024]; /* domain name of destination machine */ 503e12c5d1SDavid du Colombier char *gdomain; /* domain name of gateway */ 51bd389b36SDavid du Colombier char *uneaten; /* first character after rfc822 headers */ 527dd7cddfSDavid du Colombier char *farend; /* system we are trying to send to */ 53d9306527SDavid du Colombier char *user; /* user we are authenticating as, if authenticating */ 54219b2ee8SDavid du Colombier char hostdomain[256]; 55*db7ae703SDavid du Colombier 563e12c5d1SDavid du Colombier Biobuf bin; 573e12c5d1SDavid du Colombier Biobuf bout; 583e12c5d1SDavid du Colombier Biobuf berr; 597dd7cddfSDavid du Colombier Biobuf bfile; 603e12c5d1SDavid du Colombier 613e12c5d1SDavid du Colombier void 623e12c5d1SDavid du Colombier usage(void) 633e12c5d1SDavid du Colombier { 64*db7ae703SDavid du Colombier fprint(2, "usage: smtp [-aAdfips] [-g gw] [-h host] [-u user] " 65*db7ae703SDavid du Colombier "[.domain] net!host[!service] sender rcpt-list\n"); 66219b2ee8SDavid du Colombier exits(Giveup); 67219b2ee8SDavid du Colombier } 68219b2ee8SDavid du Colombier 697dd7cddfSDavid du Colombier int 70219b2ee8SDavid du Colombier timeout(void *x, char *msg) 71219b2ee8SDavid du Colombier { 72219b2ee8SDavid du Colombier USED(x); 737dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg); 74219b2ee8SDavid du Colombier if(strstr(msg, "alarm")){ 757dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection to %s timed out\n", farend); 767dd7cddfSDavid du Colombier if(quitting) 777dd7cddfSDavid du Colombier exits(quitrv); 787dd7cddfSDavid du Colombier exits(Retry); 79219b2ee8SDavid du Colombier } 807dd7cddfSDavid du Colombier if(strstr(msg, "closed pipe")){ 817dd7cddfSDavid du Colombier /* call _exits() to prevent Bio from trying to flush closed pipe */ 827dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection closed to %s\n", farend); 837dd7cddfSDavid du Colombier if(quitting){ 847dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "closed pipe to %s", farend); 857dd7cddfSDavid du Colombier _exits(quitrv); 867dd7cddfSDavid du Colombier } 877dd7cddfSDavid du Colombier _exits(Retry); 887dd7cddfSDavid du Colombier } 897dd7cddfSDavid du Colombier return 0; 903e12c5d1SDavid du Colombier } 913e12c5d1SDavid du Colombier 923e12c5d1SDavid du Colombier void 93a8453668SDavid du Colombier removenewline(char *p) 94a8453668SDavid du Colombier { 95a8453668SDavid du Colombier int n = strlen(p)-1; 96a8453668SDavid du Colombier 97a8453668SDavid du Colombier if(n < 0) 98a8453668SDavid du Colombier return; 99a8453668SDavid du Colombier if(p[n] == '\n') 100a8453668SDavid du Colombier p[n] = 0; 101a8453668SDavid du Colombier } 102a8453668SDavid du Colombier 103a8453668SDavid du Colombier void 1043e12c5d1SDavid du Colombier main(int argc, char **argv) 1053e12c5d1SDavid du Colombier { 1067dd7cddfSDavid du Colombier int i, ok, rcvrs; 107*db7ae703SDavid du Colombier char *addr, *rv, *trv, *host, *domain; 1087dd7cddfSDavid du Colombier char **errs; 109*db7ae703SDavid du Colombier char hellodomain[256]; 110*db7ae703SDavid du Colombier String *from, *fromm, *sender; 1113e12c5d1SDavid du Colombier 112a8453668SDavid du Colombier alarmscale = 60*1000; /* minutes */ 113fe096d1aSDavid du Colombier quotefmtinstall(); 1147dd7cddfSDavid du Colombier errs = malloc(argc*sizeof(char*)); 1153e12c5d1SDavid du Colombier reply = s_new(); 116219b2ee8SDavid du Colombier host = 0; 1173e12c5d1SDavid du Colombier ARGBEGIN{ 118d9306527SDavid du Colombier case 'a': 119d9306527SDavid du Colombier tryauth = 1; 120d9306527SDavid du Colombier trysecure = 1; 121d9306527SDavid du Colombier break; 122*db7ae703SDavid du Colombier case 'A': /* autistic: won't talk to us until we talk (Verizon) */ 123*db7ae703SDavid du Colombier autistic = 1; 1243e12c5d1SDavid du Colombier break; 1253e12c5d1SDavid du Colombier case 'd': 1263e12c5d1SDavid du Colombier debug = 1; 1273e12c5d1SDavid du Colombier break; 128*db7ae703SDavid du Colombier case 'f': 129*db7ae703SDavid du Colombier filter = 1; 130*db7ae703SDavid du Colombier break; 1313e12c5d1SDavid du Colombier case 'g': 132*db7ae703SDavid du Colombier gdomain = EARGF(usage()); 1333e12c5d1SDavid du Colombier break; 1343e12c5d1SDavid du Colombier case 'h': 135*db7ae703SDavid du Colombier host = EARGF(usage()); 1363e12c5d1SDavid du Colombier break; 137a8453668SDavid du Colombier case 'i': 138a8453668SDavid du Colombier insecure = 1; 139a8453668SDavid du Colombier break; 140a8453668SDavid du Colombier case 'p': 141a8453668SDavid du Colombier alarmscale = 10*1000; /* tens of seconds */ 142a8453668SDavid du Colombier ping = 1; 143a8453668SDavid du Colombier break; 144d9306527SDavid du Colombier case 's': 145d9306527SDavid du Colombier trysecure = 1; 146d9306527SDavid du Colombier break; 147d9306527SDavid du Colombier case 'u': 148*db7ae703SDavid du Colombier user = EARGF(usage()); 149d9306527SDavid du Colombier break; 1503e12c5d1SDavid du Colombier default: 1513e12c5d1SDavid du Colombier usage(); 1523e12c5d1SDavid du Colombier break; 1533e12c5d1SDavid du Colombier }ARGEND; 1543e12c5d1SDavid du Colombier 1553e12c5d1SDavid du Colombier Binit(&berr, 2, OWRITE); 1567dd7cddfSDavid du Colombier Binit(&bfile, 0, OREAD); 1573e12c5d1SDavid du Colombier 1583e12c5d1SDavid du Colombier /* 1593e12c5d1SDavid du Colombier * get domain and add to host name 1603e12c5d1SDavid du Colombier */ 1617dd7cddfSDavid du Colombier if(*argv && **argv=='.') { 1627dd7cddfSDavid du Colombier domain = *argv; 1637dd7cddfSDavid du Colombier argv++; argc--; 1647dd7cddfSDavid du Colombier } else 1657dd7cddfSDavid du Colombier domain = domainname_read(); 1663e12c5d1SDavid du Colombier if(host == 0) 1673e12c5d1SDavid du Colombier host = sysname_read(); 1683e12c5d1SDavid du Colombier strcpy(hostdomain, domainify(host, domain)); 1697dd7cddfSDavid du Colombier strcpy(hellodomain, domainify(sysname_read(), domain)); 1703e12c5d1SDavid du Colombier 1713e12c5d1SDavid du Colombier /* 1723e12c5d1SDavid du Colombier * get destination address 1733e12c5d1SDavid du Colombier */ 1743e12c5d1SDavid du Colombier if(*argv == 0) 1753e12c5d1SDavid du Colombier usage(); 1767dd7cddfSDavid du Colombier addr = *argv++; argc--; 1777dd7cddfSDavid du Colombier farend = addr; 1783e12c5d1SDavid du Colombier 1793e12c5d1SDavid du Colombier /* 1803e12c5d1SDavid du Colombier * get sender's machine. 1813e12c5d1SDavid du Colombier * get sender in internet style. domainify if necessary. 1823e12c5d1SDavid du Colombier */ 183219b2ee8SDavid du Colombier if(*argv == 0) 184219b2ee8SDavid du Colombier usage(); 1857dd7cddfSDavid du Colombier sender = unescapespecial(s_copy(*argv++)); 1867dd7cddfSDavid du Colombier argc--; 1877dd7cddfSDavid du Colombier fromm = s_clone(sender); 1883e12c5d1SDavid du Colombier rv = strrchr(s_to_c(fromm), '!'); 1893e12c5d1SDavid du Colombier if(rv) 1903e12c5d1SDavid du Colombier *rv = 0; 1913e12c5d1SDavid du Colombier else 1923e12c5d1SDavid du Colombier *s_to_c(fromm) = 0; 1937dd7cddfSDavid du Colombier from = bangtoat(s_to_c(sender)); 1943e12c5d1SDavid du Colombier 1953e12c5d1SDavid du Colombier /* 1963e12c5d1SDavid du Colombier * send the mail 1973e12c5d1SDavid du Colombier */ 1983e12c5d1SDavid du Colombier if(filter){ 1993e12c5d1SDavid du Colombier Binit(&bout, 1, OWRITE); 2007dd7cddfSDavid du Colombier rv = data(from, &bfile); 2013e12c5d1SDavid du Colombier if(rv != 0) 2023e12c5d1SDavid du Colombier goto error; 2037dd7cddfSDavid du Colombier exits(0); 2047dd7cddfSDavid du Colombier } 2057dd7cddfSDavid du Colombier 206ab3dc52fSDavid du Colombier /* mxdial uses its own timeout handler */ 2077dd7cddfSDavid du Colombier if((rv = connect(addr)) != 0) 2087dd7cddfSDavid du Colombier exits(rv); 209ab3dc52fSDavid du Colombier 210ab3dc52fSDavid du Colombier /* 10 minutes to get through the initial handshake */ 211ab3dc52fSDavid du Colombier atnotify(timeout, 1); 212a8453668SDavid du Colombier alarm(10*alarmscale); 213d9306527SDavid du Colombier if((rv = hello(hellodomain, 0)) != 0) 2147dd7cddfSDavid du Colombier goto error; 215a8453668SDavid du Colombier alarm(10*alarmscale); 2167dd7cddfSDavid du Colombier if((rv = mailfrom(s_to_c(from))) != 0) 2177dd7cddfSDavid du Colombier goto error; 2187dd7cddfSDavid du Colombier 2197dd7cddfSDavid du Colombier ok = 0; 2207dd7cddfSDavid du Colombier rcvrs = 0; 2217dd7cddfSDavid du Colombier /* if any rcvrs are ok, we try to send the message */ 2227dd7cddfSDavid du Colombier for(i = 0; i < argc; i++){ 2237dd7cddfSDavid du Colombier if((trv = rcptto(argv[i])) != 0){ 2247dd7cddfSDavid du Colombier /* remember worst error */ 2257dd7cddfSDavid du Colombier if(rv != Giveup) 2267dd7cddfSDavid du Colombier rv = trv; 2277dd7cddfSDavid du Colombier errs[rcvrs] = strdup(s_to_c(reply)); 228a8453668SDavid du Colombier removenewline(errs[rcvrs]); 2297dd7cddfSDavid du Colombier } else { 2307dd7cddfSDavid du Colombier ok++; 2317dd7cddfSDavid du Colombier errs[rcvrs] = 0; 2327dd7cddfSDavid du Colombier } 2337dd7cddfSDavid du Colombier rcvrs++; 2347dd7cddfSDavid du Colombier } 2357dd7cddfSDavid du Colombier 2367dd7cddfSDavid du Colombier /* if no ok rcvrs or worst error is retry, give up */ 2377dd7cddfSDavid du Colombier if(ok == 0 || rv == Retry) 2387dd7cddfSDavid du Colombier goto error; 2397dd7cddfSDavid du Colombier 240a8453668SDavid du Colombier if(ping){ 241a8453668SDavid du Colombier quit(0); 242a8453668SDavid du Colombier exits(0); 243a8453668SDavid du Colombier } 244a8453668SDavid du Colombier 2457dd7cddfSDavid du Colombier rv = data(from, &bfile); 2467dd7cddfSDavid du Colombier if(rv != 0) 2477dd7cddfSDavid du Colombier goto error; 2487dd7cddfSDavid du Colombier quit(0); 2497dd7cddfSDavid du Colombier if(rcvrs == ok) 2507dd7cddfSDavid du Colombier exits(0); 2517dd7cddfSDavid du Colombier 2527dd7cddfSDavid du Colombier /* 2537dd7cddfSDavid du Colombier * here when some but not all rcvrs failed 2547dd7cddfSDavid du Colombier */ 2557dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n", thedate(), addr); 2567dd7cddfSDavid du Colombier for(i = 0; i < rcvrs; i++){ 2577dd7cddfSDavid du Colombier if(errs[i]){ 258a8453668SDavid du Colombier syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]); 2597dd7cddfSDavid du Colombier fprint(2, " mail to %s failed: %s", argv[i], errs[i]); 2607dd7cddfSDavid du Colombier } 2617dd7cddfSDavid du Colombier } 2627dd7cddfSDavid du Colombier exits(Giveup); 2637dd7cddfSDavid du Colombier 2647dd7cddfSDavid du Colombier /* 2657dd7cddfSDavid du Colombier * here when all rcvrs failed 2667dd7cddfSDavid du Colombier */ 2673e12c5d1SDavid du Colombier error: 268a8453668SDavid du Colombier removenewline(s_to_c(reply)); 269a8453668SDavid du Colombier syslog(0, "smtp.fail", "%s to %s failed: %s", 270a8453668SDavid du Colombier ping ? "ping" : "delivery", 271a8453668SDavid du Colombier addr, s_to_c(reply)); 2727dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply)); 2737dd7cddfSDavid du Colombier if(!filter) 2747dd7cddfSDavid du Colombier quit(rv); 2753e12c5d1SDavid du Colombier exits(rv); 2763e12c5d1SDavid du Colombier } 2773e12c5d1SDavid du Colombier 2783e12c5d1SDavid du Colombier /* 2793e12c5d1SDavid du Colombier * connect to the remote host 2803e12c5d1SDavid du Colombier */ 2817dd7cddfSDavid du Colombier static char * 2823e12c5d1SDavid du Colombier connect(char* net) 2833e12c5d1SDavid du Colombier { 2843e12c5d1SDavid du Colombier char buf[256]; 2853e12c5d1SDavid du Colombier int fd; 2863e12c5d1SDavid du Colombier 2877dd7cddfSDavid du Colombier fd = mxdial(net, ddomain, gdomain); 2883e12c5d1SDavid du Colombier 2893e12c5d1SDavid du Colombier if(fd < 0){ 2909a747e4fSDavid du Colombier rerrstr(buf, sizeof(buf)); 2913ff48bf5SDavid du Colombier Bprint(&berr, "smtp: %s (%s)\n", buf, net); 2923ff48bf5SDavid du Colombier syslog(0, "smtp.fail", "%s (%s)", buf, net); 2937dd7cddfSDavid du Colombier if(strstr(buf, "illegal") 2947dd7cddfSDavid du Colombier || strstr(buf, "unknown") 295271b8d73SDavid du Colombier || strstr(buf, "can't translate")) 2963e12c5d1SDavid du Colombier return Giveup; 2973e12c5d1SDavid du Colombier else 2983e12c5d1SDavid du Colombier return Retry; 2993e12c5d1SDavid du Colombier } 3003e12c5d1SDavid du Colombier Binit(&bin, fd, OREAD); 3013e12c5d1SDavid du Colombier fd = dup(fd, -1); 3023e12c5d1SDavid du Colombier Binit(&bout, fd, OWRITE); 3033e12c5d1SDavid du Colombier return 0; 3043e12c5d1SDavid du Colombier } 3053e12c5d1SDavid du Colombier 306fe096d1aSDavid du Colombier static char smtpthumbs[] = "/sys/lib/tls/smtp"; 307fe096d1aSDavid du Colombier static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude"; 308fe096d1aSDavid du Colombier 3093e12c5d1SDavid du Colombier /* 310fe096d1aSDavid du Colombier * exchange names with remote host, attempt to 311fe096d1aSDavid du Colombier * enable encryption and optionally authenticate. 312fe096d1aSDavid du Colombier * not fatal if we can't. 3133e12c5d1SDavid du Colombier */ 314d9306527SDavid du Colombier static char * 315d9306527SDavid du Colombier dotls(char *me) 3163e12c5d1SDavid du Colombier { 317d9306527SDavid du Colombier TLSconn *c; 318d9306527SDavid du Colombier Thumbprint *goodcerts; 319d9306527SDavid du Colombier char *h; 320d9306527SDavid du Colombier int fd; 321d9306527SDavid du Colombier uchar hash[SHA1dlen]; 322d9306527SDavid du Colombier 323d9306527SDavid du Colombier c = mallocz(sizeof(*c), 1); /* Note: not freed on success */ 324d9306527SDavid du Colombier if (c == nil) 325d9306527SDavid du Colombier return Giveup; 326fe096d1aSDavid du Colombier 327d9306527SDavid du Colombier dBprint("STARTTLS\r\n"); 328fe096d1aSDavid du Colombier if (getreply() != 2) 329fe096d1aSDavid du Colombier return Giveup; 330fe096d1aSDavid du Colombier 331d9306527SDavid du Colombier fd = tlsClient(Bfildes(&bout), c); 332fe096d1aSDavid du Colombier if (fd < 0) { 333fe096d1aSDavid du Colombier syslog(0, "smtp", "tlsClient to %q: %r", ddomain); 334d9306527SDavid du Colombier return Giveup; 335dddc47c2SDavid du Colombier } 336fe096d1aSDavid du Colombier goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs); 337d9306527SDavid du Colombier if (goodcerts == nil) { 338d9306527SDavid du Colombier free(c); 339fe096d1aSDavid du Colombier close(fd); 340fe096d1aSDavid du Colombier syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs); 341fe096d1aSDavid du Colombier return Giveup; /* how to recover? TLS is started */ 342d9306527SDavid du Colombier } 343fe096d1aSDavid du Colombier 344fe096d1aSDavid du Colombier /* compute sha1 hash of remote's certificate, see if we know it */ 345d9306527SDavid du Colombier sha1(c->cert, c->certlen, hash, nil); 346d9306527SDavid du Colombier if (!okThumbprint(hash, goodcerts)) { 347fe096d1aSDavid du Colombier /* TODO? if not excluded, add hash to thumb list */ 348fe096d1aSDavid du Colombier free(c); 349fe096d1aSDavid du Colombier close(fd); 350d9306527SDavid du Colombier h = malloc(2*sizeof hash + 1); 351d9306527SDavid du Colombier if (h != nil) { 352d9306527SDavid du Colombier enc16(h, 2*sizeof hash + 1, hash, sizeof hash); 353fe096d1aSDavid du Colombier // print("x509 sha1=%s", h); 354fe096d1aSDavid du Colombier syslog(0, "smtp", 355fe096d1aSDavid du Colombier "remote cert. has bad thumbprint: x509 sha1=%s server=%q", 356fe096d1aSDavid du Colombier h, ddomain); 357d9306527SDavid du Colombier free(h); 358d9306527SDavid du Colombier } 359fe096d1aSDavid du Colombier return Giveup; /* how to recover? TLS is started */ 360d9306527SDavid du Colombier } 361d9306527SDavid du Colombier freeThumbprints(goodcerts); 362d9306527SDavid du Colombier Bterm(&bin); 363d9306527SDavid du Colombier Bterm(&bout); 364fe096d1aSDavid du Colombier 365fe096d1aSDavid du Colombier /* 366fe096d1aSDavid du Colombier * set up bin & bout to use the TLS fd, i/o upon which generates 367fe096d1aSDavid du Colombier * i/o on the original, underlying fd. 368fe096d1aSDavid du Colombier */ 369d9306527SDavid du Colombier Binit(&bin, fd, OREAD); 370d9306527SDavid du Colombier fd = dup(fd, -1); 371d9306527SDavid du Colombier Binit(&bout, fd, OWRITE); 372fe096d1aSDavid du Colombier 373fe096d1aSDavid du Colombier syslog(0, "smtp", "started TLS to %q", ddomain); 374d9306527SDavid du Colombier return(hello(me, 1)); 375d9306527SDavid du Colombier } 376d9306527SDavid du Colombier 377d9306527SDavid du Colombier static char * 3783cf081f0SDavid du Colombier doauth(char *methods) 379d9306527SDavid du Colombier { 3809863c128SDavid du Colombier char *buf, *base64; 381d9306527SDavid du Colombier int n; 3829863c128SDavid du Colombier DS ds; 3839863c128SDavid du Colombier UserPasswd *p; 384d9306527SDavid du Colombier 3859863c128SDavid du Colombier dial_string_parse(ddomain, &ds); 3863cf081f0SDavid du Colombier 387d9306527SDavid du Colombier if(user != nil) 388d9306527SDavid du Colombier p = auth_getuserpasswd(nil, 3899863c128SDavid du Colombier "proto=pass service=smtp server=%q user=%q", ds.host, user); 390d9306527SDavid du Colombier else 391d9306527SDavid du Colombier p = auth_getuserpasswd(nil, 3929863c128SDavid du Colombier "proto=pass service=smtp server=%q", ds.host); 393d9306527SDavid du Colombier if (p == nil) 394d9306527SDavid du Colombier return Giveup; 3953cf081f0SDavid du Colombier 3963cf081f0SDavid du Colombier if (strstr(methods, "LOGIN")){ 3973cf081f0SDavid du Colombier dBprint("AUTH LOGIN\r\n"); 3983cf081f0SDavid du Colombier if (getreply() != 3) 3993cf081f0SDavid du Colombier return Retry; 4003cf081f0SDavid du Colombier 4013cf081f0SDavid du Colombier n = strlen(p->user); 4023cf081f0SDavid du Colombier base64 = malloc(2*n); 4033cf081f0SDavid du Colombier if (base64 == nil) 4043cf081f0SDavid du Colombier return Retry; /* Out of memory */ 4053cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->user, n); 4063cf081f0SDavid du Colombier dBprint("%s\r\n", base64); 4073cf081f0SDavid du Colombier if (getreply() != 3) 4083cf081f0SDavid du Colombier return Retry; 4093cf081f0SDavid du Colombier 4103cf081f0SDavid du Colombier n = strlen(p->passwd); 4113cf081f0SDavid du Colombier base64 = malloc(2*n); 4123cf081f0SDavid du Colombier if (base64 == nil) 4133cf081f0SDavid du Colombier return Retry; /* Out of memory */ 4143cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->passwd, n); 4153cf081f0SDavid du Colombier dBprint("%s\r\n", base64); 4163cf081f0SDavid du Colombier if (getreply() != 2) 4173cf081f0SDavid du Colombier return Retry; 4183cf081f0SDavid du Colombier 4193cf081f0SDavid du Colombier free(base64); 4203cf081f0SDavid du Colombier } 4213cf081f0SDavid du Colombier else 4223cf081f0SDavid du Colombier if (strstr(methods, "PLAIN")){ 423d9306527SDavid du Colombier n = strlen(p->user) + strlen(p->passwd) + 3; 424d9306527SDavid du Colombier buf = malloc(n); 425d9306527SDavid du Colombier base64 = malloc(2 * n); 426fe096d1aSDavid du Colombier if (buf == nil || base64 == nil) { 427d9306527SDavid du Colombier free(buf); 428d9306527SDavid du Colombier return Retry; /* Out of memory */ 429d9306527SDavid du Colombier } 430d9306527SDavid du Colombier snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd); 431d9306527SDavid du Colombier enc64(base64, 2 * n, (uchar *)buf, n - 1); 432d9306527SDavid du Colombier free(buf); 433d9306527SDavid du Colombier dBprint("AUTH PLAIN %s\r\n", base64); 434d9306527SDavid du Colombier free(base64); 435d9306527SDavid du Colombier if (getreply() != 2) 436d9306527SDavid du Colombier return Retry; 4373cf081f0SDavid du Colombier } 4383cf081f0SDavid du Colombier else 4393cf081f0SDavid du Colombier return "No supported AUTH method"; 440d9306527SDavid du Colombier return(0); 441d9306527SDavid du Colombier } 442d9306527SDavid du Colombier 443d9306527SDavid du Colombier char * 444d9306527SDavid du Colombier hello(char *me, int encrypted) 445d9306527SDavid du Colombier { 446d9306527SDavid du Colombier int ehlo; 447d9306527SDavid du Colombier String *r; 4483cf081f0SDavid du Colombier char *ret, *s, *t; 449d9306527SDavid du Colombier 450*db7ae703SDavid du Colombier if (!encrypted) { 451*db7ae703SDavid du Colombier /* 452*db7ae703SDavid du Colombier * Verizon fails to print the smtp greeting banner when it 453*db7ae703SDavid du Colombier * answers a call. Send a no-op in the hope of making it 454*db7ae703SDavid du Colombier * talk. 455*db7ae703SDavid du Colombier */ 456*db7ae703SDavid du Colombier if (autistic) 457*db7ae703SDavid du Colombier dBprint("NOOP\r\n"); 458*db7ae703SDavid du Colombier 4593e12c5d1SDavid du Colombier switch(getreply()){ 4603e12c5d1SDavid du Colombier case 2: 4613e12c5d1SDavid du Colombier break; 4623e12c5d1SDavid du Colombier case 5: 4633e12c5d1SDavid du Colombier return Giveup; 4643e12c5d1SDavid du Colombier default: 4653e12c5d1SDavid du Colombier return Retry; 4663e12c5d1SDavid du Colombier } 467*db7ae703SDavid du Colombier } 468d9306527SDavid du Colombier 469d9306527SDavid du Colombier ehlo = 1; 470d9306527SDavid du Colombier Again: 471d9306527SDavid du Colombier if(ehlo) 472d9306527SDavid du Colombier dBprint("EHLO %s\r\n", me); 473d9306527SDavid du Colombier else 4743e12c5d1SDavid du Colombier dBprint("HELO %s\r\n", me); 4753e12c5d1SDavid du Colombier switch (getreply()) { 4763e12c5d1SDavid du Colombier case 2: 4773e12c5d1SDavid du Colombier break; 4783e12c5d1SDavid du Colombier case 5: 479d9306527SDavid du Colombier if(ehlo){ 480d9306527SDavid du Colombier ehlo = 0; 481d9306527SDavid du Colombier goto Again; 482d9306527SDavid du Colombier } 4833e12c5d1SDavid du Colombier return Giveup; 4843e12c5d1SDavid du Colombier default: 4853e12c5d1SDavid du Colombier return Retry; 4863e12c5d1SDavid du Colombier } 487d9306527SDavid du Colombier r = s_clone(reply); 488d9306527SDavid du Colombier if(r == nil) 489d9306527SDavid du Colombier return Retry; /* Out of memory or couldn't get string */ 490d9306527SDavid du Colombier 491d9306527SDavid du Colombier /* Invariant: every line has a newline, a result of getcrlf() */ 492d9306527SDavid du Colombier for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){ 493d9306527SDavid du Colombier *t = '\0'; 494d9306527SDavid du Colombier for (t = s; *t != '\0'; t++) 495d9306527SDavid du Colombier *t = toupper(*t); 496d9306527SDavid du Colombier if(!encrypted && trysecure && 497d9306527SDavid du Colombier (strcmp(s, "250-STARTTLS") == 0 || 498d9306527SDavid du Colombier strcmp(s, "250 STARTTLS") == 0)){ 499d9306527SDavid du Colombier s_free(r); 500*db7ae703SDavid du Colombier return dotls(me); 501d9306527SDavid du Colombier } 502a8453668SDavid du Colombier if(tryauth && (encrypted || insecure) && 503d9306527SDavid du Colombier (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 || 5043cf081f0SDavid du Colombier strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){ 5053cf081f0SDavid du Colombier ret = doauth(s + strlen("250 AUTH ")); 506d9306527SDavid du Colombier s_free(r); 5073cf081f0SDavid du Colombier return ret; 508d9306527SDavid du Colombier } 509d9306527SDavid du Colombier } 510d9306527SDavid du Colombier s_free(r); 5113e12c5d1SDavid du Colombier return 0; 5123e12c5d1SDavid du Colombier } 5133e12c5d1SDavid du Colombier 5143e12c5d1SDavid du Colombier /* 5153e12c5d1SDavid du Colombier * report sender to remote 5163e12c5d1SDavid du Colombier */ 5173e12c5d1SDavid du Colombier char * 5183e12c5d1SDavid du Colombier mailfrom(char *from) 5193e12c5d1SDavid du Colombier { 5207dd7cddfSDavid du Colombier if(!returnable(from)) 5217dd7cddfSDavid du Colombier dBprint("MAIL FROM:<>\r\n"); 5227dd7cddfSDavid du Colombier else 523219b2ee8SDavid du Colombier if(strchr(from, '@')) 5243e12c5d1SDavid du Colombier dBprint("MAIL FROM:<%s>\r\n", from); 525219b2ee8SDavid du Colombier else 526219b2ee8SDavid du Colombier dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain); 5273e12c5d1SDavid du Colombier switch(getreply()){ 5283e12c5d1SDavid du Colombier case 2: 5293e12c5d1SDavid du Colombier break; 5303e12c5d1SDavid du Colombier case 5: 5313e12c5d1SDavid du Colombier return Giveup; 5323e12c5d1SDavid du Colombier default: 5333e12c5d1SDavid du Colombier return Retry; 5343e12c5d1SDavid du Colombier } 5353e12c5d1SDavid du Colombier return 0; 5363e12c5d1SDavid du Colombier } 5373e12c5d1SDavid du Colombier 5383e12c5d1SDavid du Colombier /* 5393e12c5d1SDavid du Colombier * report a recipient to remote 5403e12c5d1SDavid du Colombier */ 5413e12c5d1SDavid du Colombier char * 5423e12c5d1SDavid du Colombier rcptto(char *to) 5433e12c5d1SDavid du Colombier { 544219b2ee8SDavid du Colombier String *s; 545219b2ee8SDavid du Colombier 5467dd7cddfSDavid du Colombier s = unescapespecial(bangtoat(to)); 5477dd7cddfSDavid du Colombier if(toline == 0) 548219b2ee8SDavid du Colombier toline = s_new(); 5497dd7cddfSDavid du Colombier else 550219b2ee8SDavid du Colombier s_append(toline, ", "); 551219b2ee8SDavid du Colombier s_append(toline, s_to_c(s)); 552219b2ee8SDavid du Colombier if(strchr(s_to_c(s), '@')) 553219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s>\r\n", s_to_c(s)); 554219b2ee8SDavid du Colombier else { 555219b2ee8SDavid du Colombier s_append(toline, "@"); 556219b2ee8SDavid du Colombier s_append(toline, ddomain); 557219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain); 558219b2ee8SDavid du Colombier } 559a8453668SDavid du Colombier alarm(10*alarmscale); 5603e12c5d1SDavid du Colombier switch(getreply()){ 5613e12c5d1SDavid du Colombier case 2: 5623e12c5d1SDavid du Colombier break; 5633e12c5d1SDavid du Colombier case 5: 5643e12c5d1SDavid du Colombier return Giveup; 5653e12c5d1SDavid du Colombier default: 5663e12c5d1SDavid du Colombier return Retry; 5673e12c5d1SDavid du Colombier } 5683e12c5d1SDavid du Colombier return 0; 5693e12c5d1SDavid du Colombier } 5703e12c5d1SDavid du Colombier 5719a747e4fSDavid du Colombier static char hex[] = "0123456789abcdef"; 5729a747e4fSDavid du Colombier 5733e12c5d1SDavid du Colombier /* 5743e12c5d1SDavid du Colombier * send the damn thing 5753e12c5d1SDavid du Colombier */ 5763e12c5d1SDavid du Colombier char * 5777dd7cddfSDavid du Colombier data(String *from, Biobuf *b) 5783e12c5d1SDavid du Colombier { 5797dd7cddfSDavid du Colombier char *buf, *cp; 5809a747e4fSDavid du Colombier int i, n, nbytes, bufsize, eof, r; 5817dd7cddfSDavid du Colombier String *fromline; 5829a747e4fSDavid du Colombier char errmsg[Errlen]; 5839a747e4fSDavid du Colombier char id[40]; 5843e12c5d1SDavid du Colombier 5853e12c5d1SDavid du Colombier /* 5867dd7cddfSDavid du Colombier * input the header. 5873e12c5d1SDavid du Colombier */ 5887dd7cddfSDavid du Colombier 5897dd7cddfSDavid du Colombier buf = malloc(1); 5907dd7cddfSDavid du Colombier if(buf == 0){ 5917dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory"); 5927dd7cddfSDavid du Colombier return Retry; 5937dd7cddfSDavid du Colombier } 5947dd7cddfSDavid du Colombier n = 0; 5953e12c5d1SDavid du Colombier eof = 0; 5967dd7cddfSDavid du Colombier for(;;){ 5977dd7cddfSDavid du Colombier cp = Brdline(b, '\n'); 5987dd7cddfSDavid du Colombier if(cp == nil){ 5993e12c5d1SDavid du Colombier eof = 1; 6003e12c5d1SDavid du Colombier break; 6013e12c5d1SDavid du Colombier } 6027dd7cddfSDavid du Colombier nbytes = Blinelen(b); 6037dd7cddfSDavid du Colombier buf = realloc(buf, n+nbytes+1); 6047dd7cddfSDavid du Colombier if(buf == 0){ 6057dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory"); 6067dd7cddfSDavid du Colombier return Retry; 6077dd7cddfSDavid du Colombier } 6087dd7cddfSDavid du Colombier strncpy(buf+n, cp, nbytes); 6097dd7cddfSDavid du Colombier n += nbytes; 6107dd7cddfSDavid du Colombier if(nbytes == 1) /* end of header */ 6117dd7cddfSDavid du Colombier break; 6123e12c5d1SDavid du Colombier } 6133e12c5d1SDavid du Colombier buf[n] = 0; 6147dd7cddfSDavid du Colombier bufsize = n; 6153e12c5d1SDavid du Colombier 6163e12c5d1SDavid du Colombier /* 6173e12c5d1SDavid du Colombier * parse the header, turn all addresses into @ format 6183e12c5d1SDavid du Colombier */ 6199a747e4fSDavid du Colombier yyinit(buf, n); 6203e12c5d1SDavid du Colombier yyparse(); 6213e12c5d1SDavid du Colombier 6223e12c5d1SDavid du Colombier /* 6233e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n 6243e12c5d1SDavid du Colombier */ 625a8453668SDavid du Colombier alarm(20*alarmscale); 6263e12c5d1SDavid du Colombier if(!filter){ 6273e12c5d1SDavid du Colombier dBprint("DATA\r\n"); 6283e12c5d1SDavid du Colombier switch(getreply()){ 6293e12c5d1SDavid du Colombier case 3: 6303e12c5d1SDavid du Colombier break; 6313e12c5d1SDavid du Colombier case 5: 6327dd7cddfSDavid du Colombier free(buf); 6333e12c5d1SDavid du Colombier return Giveup; 6343e12c5d1SDavid du Colombier default: 6357dd7cddfSDavid du Colombier free(buf); 6363e12c5d1SDavid du Colombier return Retry; 6373e12c5d1SDavid du Colombier } 6383e12c5d1SDavid du Colombier } 6393e12c5d1SDavid du Colombier /* 6409a747e4fSDavid du Colombier * send header. add a message-id, a sender, and a date if there 6413e12c5d1SDavid du Colombier * isn't one 6423e12c5d1SDavid du Colombier */ 6437dd7cddfSDavid du Colombier nbytes = 0; 6447dd7cddfSDavid du Colombier fromline = convertheader(from); 645bd389b36SDavid du Colombier uneaten = buf; 6469a747e4fSDavid du Colombier 647d9306527SDavid du Colombier srand(truerand()); 6489a747e4fSDavid du Colombier if(messageid == 0){ 6499a747e4fSDavid du Colombier for(i=0; i<16; i++){ 650d9306527SDavid du Colombier r = rand()&0xFF; 6519a747e4fSDavid du Colombier id[2*i] = hex[r&0xF]; 6529a747e4fSDavid du Colombier id[2*i+1] = hex[(r>>4)&0xF]; 6539a747e4fSDavid du Colombier } 6549a747e4fSDavid du Colombier id[2*i] = '\0'; 6559a747e4fSDavid du Colombier nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain); 6569a747e4fSDavid du Colombier if(debug) 6579a747e4fSDavid du Colombier Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain); 6589a747e4fSDavid du Colombier } 6599a747e4fSDavid du Colombier 6607dd7cddfSDavid du Colombier if(originator==0){ 6617dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline)); 6627dd7cddfSDavid du Colombier if(debug) 6637dd7cddfSDavid du Colombier Bprint(&berr, "From: %s\r\n", s_to_c(fromline)); 6647dd7cddfSDavid du Colombier } 6657dd7cddfSDavid du Colombier s_free(fromline); 6667dd7cddfSDavid du Colombier 667219b2ee8SDavid du Colombier if(destination == 0 && toline) 6687dd7cddfSDavid du Colombier if(*s_to_c(toline) == '@'){ /* route addr */ 6697dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline)); 6707dd7cddfSDavid du Colombier if(debug) 6717dd7cddfSDavid du Colombier Bprint(&berr, "To: <%s>\r\n", s_to_c(toline)); 6727dd7cddfSDavid du Colombier } else { 6737dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline)); 6747dd7cddfSDavid du Colombier if(debug) 6757dd7cddfSDavid du Colombier Bprint(&berr, "To: %s\r\n", s_to_c(toline)); 6767dd7cddfSDavid du Colombier } 6777dd7cddfSDavid du Colombier 6783e12c5d1SDavid du Colombier if(date==0 && udate) 6797dd7cddfSDavid du Colombier nbytes += printdate(udate); 680bd389b36SDavid du Colombier if (usys) 681bd389b36SDavid du Colombier uneaten = usys->end + 1; 6827dd7cddfSDavid du Colombier nbytes += printheader(); 683bd389b36SDavid du Colombier if (*uneaten != '\n') 684bd389b36SDavid du Colombier putcrnl("\n", 1); 6853e12c5d1SDavid du Colombier 6863e12c5d1SDavid du Colombier /* 6873e12c5d1SDavid du Colombier * send body 6883e12c5d1SDavid du Colombier */ 6897dd7cddfSDavid du Colombier 690bd389b36SDavid du Colombier putcrnl(uneaten, buf+n - uneaten); 6917dd7cddfSDavid du Colombier nbytes += buf+n - uneaten; 6927dd7cddfSDavid du Colombier if(eof == 0){ 693219b2ee8SDavid du Colombier for(;;){ 6947dd7cddfSDavid du Colombier n = Bread(b, buf, bufsize); 695219b2ee8SDavid du Colombier if(n < 0){ 6969a747e4fSDavid du Colombier rerrstr(errmsg, sizeof(errmsg)); 6977dd7cddfSDavid du Colombier s_append(s_restart(reply), errmsg); 6987dd7cddfSDavid du Colombier free(buf); 6997dd7cddfSDavid du Colombier return Retry; 700219b2ee8SDavid du Colombier } 701219b2ee8SDavid du Colombier if(n == 0) 702219b2ee8SDavid du Colombier break; 703a8453668SDavid du Colombier alarm(10*alarmscale); 7043e12c5d1SDavid du Colombier putcrnl(buf, n); 7057dd7cddfSDavid du Colombier nbytes += n; 706219b2ee8SDavid du Colombier } 7077dd7cddfSDavid du Colombier } 7087dd7cddfSDavid du Colombier free(buf); 7093e12c5d1SDavid du Colombier if(!filter){ 7103e12c5d1SDavid du Colombier if(last != '\n') 7113e12c5d1SDavid du Colombier dBprint("\r\n.\r\n"); 7123e12c5d1SDavid du Colombier else 7133e12c5d1SDavid du Colombier dBprint(".\r\n"); 714a8453668SDavid du Colombier alarm(10*alarmscale); 7153e12c5d1SDavid du Colombier switch(getreply()){ 7163e12c5d1SDavid du Colombier case 2: 7173e12c5d1SDavid du Colombier break; 7183e12c5d1SDavid du Colombier case 5: 7193e12c5d1SDavid du Colombier return Giveup; 7207dd7cddfSDavid du Colombier default: 7217dd7cddfSDavid du Colombier return Retry; 7223e12c5d1SDavid du Colombier } 7237dd7cddfSDavid du Colombier syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from), 7247dd7cddfSDavid du Colombier nbytes, s_to_c(toline));/**/ 7253e12c5d1SDavid du Colombier } 7263e12c5d1SDavid du Colombier return 0; 7273e12c5d1SDavid du Colombier } 7283e12c5d1SDavid du Colombier 7293e12c5d1SDavid du Colombier /* 7303e12c5d1SDavid du Colombier * we're leaving 7313e12c5d1SDavid du Colombier */ 7323e12c5d1SDavid du Colombier void 7337dd7cddfSDavid du Colombier quit(char *rv) 7343e12c5d1SDavid du Colombier { 7357dd7cddfSDavid du Colombier /* 60 minutes to quit */ 7367dd7cddfSDavid du Colombier quitting = 1; 7377dd7cddfSDavid du Colombier quitrv = rv; 738a8453668SDavid du Colombier alarm(60*alarmscale); 7393e12c5d1SDavid du Colombier dBprint("QUIT\r\n"); 7403e12c5d1SDavid du Colombier getreply(); 7417dd7cddfSDavid du Colombier Bterm(&bout); 7427dd7cddfSDavid du Colombier Bterm(&bfile); 7433e12c5d1SDavid du Colombier } 7443e12c5d1SDavid du Colombier 7453e12c5d1SDavid du Colombier /* 7463e12c5d1SDavid du Colombier * read a reply into a string, return the reply code 7473e12c5d1SDavid du Colombier */ 7483e12c5d1SDavid du Colombier int 7493e12c5d1SDavid du Colombier getreply(void) 7503e12c5d1SDavid du Colombier { 7513e12c5d1SDavid du Colombier char *line; 7523e12c5d1SDavid du Colombier int rv; 7533e12c5d1SDavid du Colombier 7543e12c5d1SDavid du Colombier reply = s_reset(reply); 7553e12c5d1SDavid du Colombier for(;;){ 7563e12c5d1SDavid du Colombier line = getcrnl(reply); 757499069deSDavid du Colombier if(debug) 758499069deSDavid du Colombier Bflush(&berr); 7593e12c5d1SDavid du Colombier if(line == 0) 7603e12c5d1SDavid du Colombier return -1; 7613e12c5d1SDavid du Colombier if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2])) 7623e12c5d1SDavid du Colombier return -1; 7633e12c5d1SDavid du Colombier if(line[3] != '-') 7643e12c5d1SDavid du Colombier break; 7653e12c5d1SDavid du Colombier } 7667dd7cddfSDavid du Colombier if(debug) 7677dd7cddfSDavid du Colombier Bflush(&berr); 7683e12c5d1SDavid du Colombier rv = atoi(line)/100; 7693e12c5d1SDavid du Colombier return rv; 7703e12c5d1SDavid du Colombier } 7717dd7cddfSDavid du Colombier void 7727dd7cddfSDavid du Colombier addhostdom(String *buf, char *host) 7737dd7cddfSDavid du Colombier { 7747dd7cddfSDavid du Colombier s_append(buf, "@"); 7757dd7cddfSDavid du Colombier s_append(buf, host); 7767dd7cddfSDavid du Colombier } 7773e12c5d1SDavid du Colombier 7783e12c5d1SDavid du Colombier /* 7793e12c5d1SDavid du Colombier * Convert from `bang' to `source routing' format. 7803e12c5d1SDavid du Colombier * 7813e12c5d1SDavid du Colombier * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o 7823e12c5d1SDavid du Colombier */ 7833e12c5d1SDavid du Colombier String * 784219b2ee8SDavid du Colombier bangtoat(char *addr) 7853e12c5d1SDavid du Colombier { 7863e12c5d1SDavid du Colombier String *buf; 7873e12c5d1SDavid du Colombier register int i; 7883e12c5d1SDavid du Colombier int j, d; 7893e12c5d1SDavid du Colombier char *field[128]; 7903e12c5d1SDavid du Colombier 7913e12c5d1SDavid du Colombier /* parse the '!' format address */ 7923e12c5d1SDavid du Colombier buf = s_new(); 7933e12c5d1SDavid du Colombier for(i = 0; addr; i++){ 7943e12c5d1SDavid du Colombier field[i] = addr; 7953e12c5d1SDavid du Colombier addr = strchr(addr, '!'); 7963e12c5d1SDavid du Colombier if(addr) 7973e12c5d1SDavid du Colombier *addr++ = 0; 7983e12c5d1SDavid du Colombier } 7993e12c5d1SDavid du Colombier if (i==1) { 8003e12c5d1SDavid du Colombier s_append(buf, field[0]); 8013e12c5d1SDavid du Colombier return buf; 8023e12c5d1SDavid du Colombier } 8033e12c5d1SDavid du Colombier 8043e12c5d1SDavid du Colombier /* 8053e12c5d1SDavid du Colombier * count leading domain fields (non-domains don't count) 8063e12c5d1SDavid du Colombier */ 8077dd7cddfSDavid du Colombier for(d = 0; d<i-1; d++) 8083e12c5d1SDavid du Colombier if(strchr(field[d], '.')==0) 8093e12c5d1SDavid du Colombier break; 8103e12c5d1SDavid du Colombier /* 8113e12c5d1SDavid du Colombier * if there are more than 1 leading domain elements, 8123e12c5d1SDavid du Colombier * put them in as source routing 8133e12c5d1SDavid du Colombier */ 8143e12c5d1SDavid du Colombier if(d > 1){ 815219b2ee8SDavid du Colombier addhostdom(buf, field[0]); 8163e12c5d1SDavid du Colombier for(j=1; j<d-1; j++){ 8173e12c5d1SDavid du Colombier s_append(buf, ","); 8183e12c5d1SDavid du Colombier s_append(buf, "@"); 8193e12c5d1SDavid du Colombier s_append(buf, field[j]); 8203e12c5d1SDavid du Colombier } 8213e12c5d1SDavid du Colombier s_append(buf, ":"); 8223e12c5d1SDavid du Colombier } 8233e12c5d1SDavid du Colombier 8243e12c5d1SDavid du Colombier /* 8253e12c5d1SDavid du Colombier * throw in the non-domain elements separated by '!'s 8263e12c5d1SDavid du Colombier */ 8273e12c5d1SDavid du Colombier s_append(buf, field[d]); 8283e12c5d1SDavid du Colombier for(j=d+1; j<=i-1; j++) { 8293e12c5d1SDavid du Colombier s_append(buf, "!"); 8303e12c5d1SDavid du Colombier s_append(buf, field[j]); 8313e12c5d1SDavid du Colombier } 8323e12c5d1SDavid du Colombier if(d) 833219b2ee8SDavid du Colombier addhostdom(buf, field[d-1]); 8343e12c5d1SDavid du Colombier return buf; 8353e12c5d1SDavid du Colombier } 8363e12c5d1SDavid du Colombier 8373e12c5d1SDavid du Colombier /* 8383e12c5d1SDavid du Colombier * convert header addresses to @ format. 8393e12c5d1SDavid du Colombier * if the address is a source address, and a domain is specified, 8403e12c5d1SDavid du Colombier * make sure it falls in the domain. 8413e12c5d1SDavid du Colombier */ 8427dd7cddfSDavid du Colombier String* 843219b2ee8SDavid du Colombier convertheader(String *from) 8443e12c5d1SDavid du Colombier { 8453e12c5d1SDavid du Colombier Field *f; 8467dd7cddfSDavid du Colombier Node *p, *lastp; 8473e12c5d1SDavid du Colombier String *a; 8483e12c5d1SDavid du Colombier 8497dd7cddfSDavid du Colombier if(!returnable(s_to_c(from))){ 8507dd7cddfSDavid du Colombier from = s_new(); 8517dd7cddfSDavid du Colombier s_append(from, "Postmaster"); 8527dd7cddfSDavid du Colombier addhostdom(from, hostdomain); 8537dd7cddfSDavid du Colombier } else 8547dd7cddfSDavid du Colombier if(strchr(s_to_c(from), '@') == 0){ 8557dd7cddfSDavid du Colombier a = username(from); 8567dd7cddfSDavid du Colombier if(a) { 8577dd7cddfSDavid du Colombier s_append(a, " <"); 8587dd7cddfSDavid du Colombier s_append(a, s_to_c(from)); 8597dd7cddfSDavid du Colombier addhostdom(a, hostdomain); 8607dd7cddfSDavid du Colombier s_append(a, ">"); 8617dd7cddfSDavid du Colombier from = a; 8627dd7cddfSDavid du Colombier } else { 8637dd7cddfSDavid du Colombier from = s_copy(s_to_c(from)); 8647dd7cddfSDavid du Colombier addhostdom(from, hostdomain); 865219b2ee8SDavid du Colombier } 8667dd7cddfSDavid du Colombier } else 8677dd7cddfSDavid du Colombier from = s_copy(s_to_c(from)); 8683e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){ 8697dd7cddfSDavid du Colombier lastp = 0; 8707dd7cddfSDavid du Colombier for(p = f->node; p; lastp = p, p = p->next){ 8713e12c5d1SDavid du Colombier if(!p->addr) 8723e12c5d1SDavid du Colombier continue; 873219b2ee8SDavid du Colombier a = bangtoat(s_to_c(p->s)); 8743e12c5d1SDavid du Colombier s_free(p->s); 8757dd7cddfSDavid du Colombier if(strchr(s_to_c(a), '@') == 0) 8767dd7cddfSDavid du Colombier addhostdom(a, hostdomain); 8777dd7cddfSDavid du Colombier else if(*s_to_c(a) == '@') 8787dd7cddfSDavid du Colombier a = fixrouteaddr(a, p->next, lastp); 8793e12c5d1SDavid du Colombier p->s = a; 8803e12c5d1SDavid du Colombier } 8813e12c5d1SDavid du Colombier } 8827dd7cddfSDavid du Colombier return from; 8837dd7cddfSDavid du Colombier } 8847dd7cddfSDavid du Colombier /* 8857dd7cddfSDavid du Colombier * ensure route addr has brackets around it 8867dd7cddfSDavid du Colombier */ 8877dd7cddfSDavid du Colombier String* 8887dd7cddfSDavid du Colombier fixrouteaddr(String *raddr, Node *next, Node *last) 8897dd7cddfSDavid du Colombier { 8907dd7cddfSDavid du Colombier String *a; 8917dd7cddfSDavid du Colombier 8927dd7cddfSDavid du Colombier if(last && last->c == '<' && next && next->c == '>') 8937dd7cddfSDavid du Colombier return raddr; /* properly formed already */ 8947dd7cddfSDavid du Colombier 8957dd7cddfSDavid du Colombier a = s_new(); 8967dd7cddfSDavid du Colombier s_append(a, "<"); 8977dd7cddfSDavid du Colombier s_append(a, s_to_c(raddr)); 8987dd7cddfSDavid du Colombier s_append(a, ">"); 8997dd7cddfSDavid du Colombier s_free(raddr); 9007dd7cddfSDavid du Colombier return a; 9013e12c5d1SDavid du Colombier } 9023e12c5d1SDavid du Colombier 9033e12c5d1SDavid du Colombier /* 9043e12c5d1SDavid du Colombier * print out the parsed header 9053e12c5d1SDavid du Colombier */ 9067dd7cddfSDavid du Colombier int 9073e12c5d1SDavid du Colombier printheader(void) 9083e12c5d1SDavid du Colombier { 9097dd7cddfSDavid du Colombier int n, len; 9103e12c5d1SDavid du Colombier Field *f; 9113e12c5d1SDavid du Colombier Node *p; 9123e12c5d1SDavid du Colombier char *cp; 9133e12c5d1SDavid du Colombier char c[1]; 9143e12c5d1SDavid du Colombier 9157dd7cddfSDavid du Colombier n = 0; 9163e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){ 9173e12c5d1SDavid du Colombier for(p = f->node; p; p = p->next){ 9183e12c5d1SDavid du Colombier if(p->s) 9197dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s)); 9203e12c5d1SDavid du Colombier else { 9213e12c5d1SDavid du Colombier c[0] = p->c; 9223e12c5d1SDavid du Colombier putcrnl(c, 1); 9237dd7cddfSDavid du Colombier n++; 9243e12c5d1SDavid du Colombier } 9253e12c5d1SDavid du Colombier if(p->white){ 9263e12c5d1SDavid du Colombier cp = s_to_c(p->white); 9277dd7cddfSDavid du Colombier len = strlen(cp); 9287dd7cddfSDavid du Colombier putcrnl(cp, len); 9297dd7cddfSDavid du Colombier n += len; 9303e12c5d1SDavid du Colombier } 931bd389b36SDavid du Colombier uneaten = p->end; 9323e12c5d1SDavid du Colombier } 9333e12c5d1SDavid du Colombier putcrnl("\n", 1); 9347dd7cddfSDavid du Colombier n++; 935bd389b36SDavid du Colombier uneaten++; /* skip newline */ 9363e12c5d1SDavid du Colombier } 9377dd7cddfSDavid du Colombier return n; 9383e12c5d1SDavid du Colombier } 9393e12c5d1SDavid du Colombier 9403e12c5d1SDavid du Colombier /* 9413e12c5d1SDavid du Colombier * add a domain onto an name, return the new name 9423e12c5d1SDavid du Colombier */ 9433e12c5d1SDavid du Colombier char * 9443e12c5d1SDavid du Colombier domainify(char *name, char *domain) 9453e12c5d1SDavid du Colombier { 9463e12c5d1SDavid du Colombier static String *s; 9477dd7cddfSDavid du Colombier char *p; 9483e12c5d1SDavid du Colombier 9493e12c5d1SDavid du Colombier if(domain==0 || strchr(name, '.')!=0) 9503e12c5d1SDavid du Colombier return name; 9513e12c5d1SDavid du Colombier 9523e12c5d1SDavid du Colombier s = s_reset(s); 9533e12c5d1SDavid du Colombier s_append(s, name); 9547dd7cddfSDavid du Colombier p = strchr(domain, '.'); 9557dd7cddfSDavid du Colombier if(p == 0){ 956219b2ee8SDavid du Colombier s_append(s, "."); 9577dd7cddfSDavid du Colombier p = domain; 9587dd7cddfSDavid du Colombier } 9597dd7cddfSDavid du Colombier s_append(s, p); 9603e12c5d1SDavid du Colombier return s_to_c(s); 9613e12c5d1SDavid du Colombier } 9623e12c5d1SDavid du Colombier 9633e12c5d1SDavid du Colombier /* 9643e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n 9653e12c5d1SDavid du Colombier */ 9663e12c5d1SDavid du Colombier void 9673e12c5d1SDavid du Colombier putcrnl(char *cp, int n) 9683e12c5d1SDavid du Colombier { 9693e12c5d1SDavid du Colombier int c; 9703e12c5d1SDavid du Colombier 9713e12c5d1SDavid du Colombier for(; n; n--, cp++){ 9723e12c5d1SDavid du Colombier c = *cp; 9733e12c5d1SDavid du Colombier if(c == '\n') 9747dd7cddfSDavid du Colombier dBputc('\r'); 9753e12c5d1SDavid du Colombier else if(c == '.' && last=='\n') 9767dd7cddfSDavid du Colombier dBputc('.'); 9777dd7cddfSDavid du Colombier dBputc(c); 9783e12c5d1SDavid du Colombier last = c; 9793e12c5d1SDavid du Colombier } 9803e12c5d1SDavid du Colombier } 9813e12c5d1SDavid du Colombier 9823e12c5d1SDavid du Colombier /* 9833e12c5d1SDavid du Colombier * Get a line including a crnl into a string. Convert crnl into nl. 9843e12c5d1SDavid du Colombier */ 9853e12c5d1SDavid du Colombier char * 9863e12c5d1SDavid du Colombier getcrnl(String *s) 9873e12c5d1SDavid du Colombier { 9883e12c5d1SDavid du Colombier int c; 9893e12c5d1SDavid du Colombier int count; 9903e12c5d1SDavid du Colombier 9913e12c5d1SDavid du Colombier count = 0; 9923e12c5d1SDavid du Colombier for(;;){ 9933e12c5d1SDavid du Colombier c = Bgetc(&bin); 9943e12c5d1SDavid du Colombier if(debug) 9953e12c5d1SDavid du Colombier Bputc(&berr, c); 9963e12c5d1SDavid du Colombier switch(c){ 9973e12c5d1SDavid du Colombier case -1: 9987dd7cddfSDavid du Colombier s_append(s, "connection closed unexpectedly by remote system"); 9993e12c5d1SDavid du Colombier s_terminate(s); 10003e12c5d1SDavid du Colombier return 0; 10013e12c5d1SDavid du Colombier case '\r': 10023e12c5d1SDavid du Colombier c = Bgetc(&bin); 10033e12c5d1SDavid du Colombier if(c == '\n'){ 1004499069deSDavid du Colombier case '\n': 10053e12c5d1SDavid du Colombier s_putc(s, c); 10063e12c5d1SDavid du Colombier if(debug) 10073e12c5d1SDavid du Colombier Bputc(&berr, c); 10083e12c5d1SDavid du Colombier count++; 10093e12c5d1SDavid du Colombier s_terminate(s); 10103e12c5d1SDavid du Colombier return s->ptr - count; 10113e12c5d1SDavid du Colombier } 10123e12c5d1SDavid du Colombier Bungetc(&bin); 10133e12c5d1SDavid du Colombier s_putc(s, '\r'); 10143e12c5d1SDavid du Colombier if(debug) 10153e12c5d1SDavid du Colombier Bputc(&berr, '\r'); 10163e12c5d1SDavid du Colombier count++; 10173e12c5d1SDavid du Colombier break; 10183e12c5d1SDavid du Colombier default: 10193e12c5d1SDavid du Colombier s_putc(s, c); 10203e12c5d1SDavid du Colombier count++; 10213e12c5d1SDavid du Colombier break; 10223e12c5d1SDavid du Colombier } 10233e12c5d1SDavid du Colombier } 10243e12c5d1SDavid du Colombier } 10253e12c5d1SDavid du Colombier 10263e12c5d1SDavid du Colombier /* 10273e12c5d1SDavid du Colombier * print out a parsed date 10283e12c5d1SDavid du Colombier */ 10297dd7cddfSDavid du Colombier int 10303e12c5d1SDavid du Colombier printdate(Node *p) 10313e12c5d1SDavid du Colombier { 10327dd7cddfSDavid du Colombier int n, sep = 0; 10333e12c5d1SDavid du Colombier 10347dd7cddfSDavid du Colombier n = dBprint("Date: %s,", s_to_c(p->s)); 10353e12c5d1SDavid du Colombier for(p = p->next; p; p = p->next){ 10363e12c5d1SDavid du Colombier if(p->s){ 10377dd7cddfSDavid du Colombier if(sep == 0) { 10387dd7cddfSDavid du Colombier dBputc(' '); 10397dd7cddfSDavid du Colombier n++; 10407dd7cddfSDavid du Colombier } 1041bd389b36SDavid du Colombier if (p->next) 10427dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s)); 1043bd389b36SDavid du Colombier else 10447dd7cddfSDavid du Colombier n += dBprint("%s", rewritezone(s_to_c(p->s))); 10453e12c5d1SDavid du Colombier sep = 0; 10463e12c5d1SDavid du Colombier } else { 10477dd7cddfSDavid du Colombier dBputc(p->c); 10487dd7cddfSDavid du Colombier n++; 10493e12c5d1SDavid du Colombier sep = 1; 10503e12c5d1SDavid du Colombier } 10513e12c5d1SDavid du Colombier } 10527dd7cddfSDavid du Colombier n += dBprint("\r\n"); 10537dd7cddfSDavid du Colombier return n; 10543e12c5d1SDavid du Colombier } 10553e12c5d1SDavid du Colombier 1056bd389b36SDavid du Colombier char * 1057bd389b36SDavid du Colombier rewritezone(char *z) 1058bd389b36SDavid du Colombier { 10597dd7cddfSDavid du Colombier int mindiff; 10607dd7cddfSDavid du Colombier char s; 10617dd7cddfSDavid du Colombier Tm *tm; 10627dd7cddfSDavid du Colombier static char x[7]; 1063bd389b36SDavid du Colombier 10647dd7cddfSDavid du Colombier tm = localtime(time(0)); 10657dd7cddfSDavid du Colombier mindiff = tm->tzoff/60; 1066bd389b36SDavid du Colombier 10677dd7cddfSDavid du Colombier /* if not in my timezone, don't change anything */ 10687dd7cddfSDavid du Colombier if(strcmp(tm->zone, z) != 0) 1069bd389b36SDavid du Colombier return z; 10707dd7cddfSDavid du Colombier 10717dd7cddfSDavid du Colombier if(mindiff < 0){ 10727dd7cddfSDavid du Colombier s = '-'; 10737dd7cddfSDavid du Colombier mindiff = -mindiff; 10747dd7cddfSDavid du Colombier } else 10757dd7cddfSDavid du Colombier s = '+'; 10767dd7cddfSDavid du Colombier 10777dd7cddfSDavid du Colombier sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60); 10787dd7cddfSDavid du Colombier return x; 1079bd389b36SDavid du Colombier } 1080bd389b36SDavid du Colombier 10813e12c5d1SDavid du Colombier /* 10823e12c5d1SDavid du Colombier * stolen from libc/port/print.c 10833e12c5d1SDavid du Colombier */ 10843e12c5d1SDavid du Colombier #define SIZE 4096 10853e12c5d1SDavid du Colombier int 10863e12c5d1SDavid du Colombier dBprint(char *fmt, ...) 10873e12c5d1SDavid du Colombier { 10883e12c5d1SDavid du Colombier char buf[SIZE], *out; 10897dd7cddfSDavid du Colombier va_list arg; 10903e12c5d1SDavid du Colombier int n; 10913e12c5d1SDavid du Colombier 10927dd7cddfSDavid du Colombier va_start(arg, fmt); 10939a747e4fSDavid du Colombier out = vseprint(buf, buf+SIZE, fmt, arg); 10947dd7cddfSDavid du Colombier va_end(arg); 10953e12c5d1SDavid du Colombier if(debug){ 10963e12c5d1SDavid du Colombier Bwrite(&berr, buf, (long)(out-buf)); 10973e12c5d1SDavid du Colombier Bflush(&berr); 10983e12c5d1SDavid du Colombier } 10993e12c5d1SDavid du Colombier n = Bwrite(&bout, buf, (long)(out-buf)); 11003e12c5d1SDavid du Colombier Bflush(&bout); 11013e12c5d1SDavid du Colombier return n; 11023e12c5d1SDavid du Colombier } 11037dd7cddfSDavid du Colombier 11047dd7cddfSDavid du Colombier int 11057dd7cddfSDavid du Colombier dBputc(int x) 11067dd7cddfSDavid du Colombier { 11077dd7cddfSDavid du Colombier if(debug) 11087dd7cddfSDavid du Colombier Bputc(&berr, x); 11097dd7cddfSDavid du Colombier return Bputc(&bout, x); 11107dd7cddfSDavid du Colombier } 1111