123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119 |
- #include "common.h"
- #include "smtp.h"
- #include <ctype.h>
- #include <mp.h>
- #include <libsec.h>
- #include <auth.h>
- static char* connect(char*);
- static char* dotls(char*);
- static char* doauth(char*);
- void addhostdom(String*, char*);
- String* bangtoat(char*);
- String* convertheader(String*);
- int dBprint(char*, ...);
- int dBputc(int);
- char* data(String*, Biobuf*);
- char* domainify(char*, char*);
- String* fixrouteaddr(String*, Node*, Node*);
- char* getcrnl(String*);
- int getreply(void);
- char* hello(char*, int);
- char* mailfrom(char*);
- int printdate(Node*);
- int printheader(void);
- void putcrnl(char*, int);
- void quit(char*);
- char* rcptto(char*);
- char *rewritezone(char *);
- #define Retry "Retry, Temporary Failure"
- #define Giveup "Permanent Failure"
- String *reply; /* last reply */
- String *toline;
- int alarmscale;
- int autistic;
- int debug; /* true if we're debugging */
- int filter;
- int insecure;
- int last = 'n'; /* last character sent by putcrnl() */
- int ping;
- int quitting; /* when error occurs in quit */
- int tryauth; /* Try to authenticate, if supported */
- int trysecure; /* Try to use TLS if the other side supports it */
- char *quitrv; /* deferred return value when in quit */
- char ddomain[1024]; /* domain name of destination machine */
- char *gdomain; /* domain name of gateway */
- char *uneaten; /* first character after rfc822 headers */
- char *farend; /* system we are trying to send to */
- char *user; /* user we are authenticating as, if authenticating */
- char hostdomain[256];
- Biobuf bin;
- Biobuf bout;
- Biobuf berr;
- Biobuf bfile;
- static int bustedmx;
- void
- usage(void)
- {
- fprint(2, "usage: smtp [-aAdfips] [-b busted-mx] [-g gw] [-h host] "
- "[-u user] [.domain] net!host[!service] sender rcpt-list\n");
- exits(Giveup);
- }
- int
- timeout(void *x, char *msg)
- {
- USED(x);
- syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg);
- if(strstr(msg, "alarm")){
- fprint(2, "smtp timeout: connection to %s timed out\n", farend);
- if(quitting)
- exits(quitrv);
- exits(Retry);
- }
- if(strstr(msg, "closed pipe")){
- /* call _exits() to prevent Bio from trying to flush closed pipe */
- fprint(2, "smtp timeout: connection closed to %s\n", farend);
- if(quitting){
- syslog(0, "smtp.fail", "closed pipe to %s", farend);
- _exits(quitrv);
- }
- _exits(Retry);
- }
- return 0;
- }
- void
- removenewline(char *p)
- {
- int n = strlen(p)-1;
- if(n < 0)
- return;
- if(p[n] == '\n')
- p[n] = 0;
- }
- void
- main(int argc, char **argv)
- {
- int i, ok, rcvrs;
- char *addr, *rv, *trv, *host, *domain;
- char **errs;
- char hellodomain[256];
- String *from, *fromm, *sender;
- alarmscale = 60*1000; /* minutes */
- quotefmtinstall();
- errs = malloc(argc*sizeof(char*));
- reply = s_new();
- host = 0;
- ARGBEGIN{
- case 'a':
- tryauth = 1;
- trysecure = 1;
- break;
- case 'A': /* autistic: won't talk to us until we talk (Verizon) */
- autistic = 1;
- break;
- case 'b':
- if (bustedmx >= Maxbustedmx)
- sysfatal("more than %d busted mxs given", Maxbustedmx);
- bustedmxs[bustedmx++] = EARGF(usage());
- break;
- case 'd':
- debug = 1;
- break;
- case 'f':
- filter = 1;
- break;
- case 'g':
- gdomain = EARGF(usage());
- break;
- case 'h':
- host = EARGF(usage());
- break;
- case 'i':
- insecure = 1;
- break;
- case 'p':
- alarmscale = 10*1000; /* tens of seconds */
- ping = 1;
- break;
- case 's':
- trysecure = 1;
- break;
- case 'u':
- user = EARGF(usage());
- break;
- default:
- usage();
- break;
- }ARGEND;
- Binit(&berr, 2, OWRITE);
- Binit(&bfile, 0, OREAD);
- /*
- * get domain and add to host name
- */
- if(*argv && **argv=='.') {
- domain = *argv;
- argv++; argc--;
- } else
- domain = domainname_read();
- if(host == 0)
- host = sysname_read();
- strcpy(hostdomain, domainify(host, domain));
- strcpy(hellodomain, domainify(sysname_read(), domain));
- /*
- * get destination address
- */
- if(*argv == 0)
- usage();
- addr = *argv++; argc--;
- farend = addr;
- /*
- * get sender's machine.
- * get sender in internet style. domainify if necessary.
- */
- if(*argv == 0)
- usage();
- sender = unescapespecial(s_copy(*argv++));
- argc--;
- fromm = s_clone(sender);
- rv = strrchr(s_to_c(fromm), '!');
- if(rv)
- *rv = 0;
- else
- *s_to_c(fromm) = 0;
- from = bangtoat(s_to_c(sender));
- /*
- * send the mail
- */
- if(filter){
- Binit(&bout, 1, OWRITE);
- rv = data(from, &bfile);
- if(rv != 0)
- goto error;
- exits(0);
- }
- /* mxdial uses its own timeout handler */
- if((rv = connect(addr)) != 0)
- exits(rv);
- /* 10 minutes to get through the initial handshake */
- atnotify(timeout, 1);
- alarm(10*alarmscale);
- if((rv = hello(hellodomain, 0)) != 0)
- goto error;
- alarm(10*alarmscale);
- if((rv = mailfrom(s_to_c(from))) != 0)
- goto error;
- ok = 0;
- rcvrs = 0;
- /* if any rcvrs are ok, we try to send the message */
- for(i = 0; i < argc; i++){
- if((trv = rcptto(argv[i])) != 0){
- /* remember worst error */
- if(rv != Giveup)
- rv = trv;
- errs[rcvrs] = strdup(s_to_c(reply));
- removenewline(errs[rcvrs]);
- } else {
- ok++;
- errs[rcvrs] = 0;
- }
- rcvrs++;
- }
- /* if no ok rcvrs or worst error is retry, give up */
- if(ok == 0 || rv == Retry)
- goto error;
- if(ping){
- quit(0);
- exits(0);
- }
- rv = data(from, &bfile);
- if(rv != 0)
- goto error;
- quit(0);
- if(rcvrs == ok)
- exits(0);
- /*
- * here when some but not all rcvrs failed
- */
- fprint(2, "%s connect to %s:\n", thedate(), addr);
- for(i = 0; i < rcvrs; i++){
- if(errs[i]){
- syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);
- fprint(2, " mail to %s failed: %s", argv[i], errs[i]);
- }
- }
- exits(Giveup);
- /*
- * here when all rcvrs failed
- */
- error:
- removenewline(s_to_c(reply));
- syslog(0, "smtp.fail", "%s to %s failed: %s",
- ping ? "ping" : "delivery",
- addr, s_to_c(reply));
- fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
- if(!filter)
- quit(rv);
- exits(rv);
- }
- /*
- * connect to the remote host
- */
- static char *
- connect(char* net)
- {
- char buf[256];
- int fd;
- fd = mxdial(net, ddomain, gdomain);
- if(fd < 0){
- rerrstr(buf, sizeof(buf));
- Bprint(&berr, "smtp: %s (%s)\n", buf, net);
- syslog(0, "smtp.fail", "%s (%s)", buf, net);
- if(strstr(buf, "illegal")
- || strstr(buf, "unknown")
- || strstr(buf, "can't translate"))
- return Giveup;
- else
- return Retry;
- }
- Binit(&bin, fd, OREAD);
- fd = dup(fd, -1);
- Binit(&bout, fd, OWRITE);
- return 0;
- }
- static char smtpthumbs[] = "/sys/lib/tls/smtp";
- static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude";
- /*
- * exchange names with remote host, attempt to
- * enable encryption and optionally authenticate.
- * not fatal if we can't.
- */
- static char *
- dotls(char *me)
- {
- TLSconn *c;
- Thumbprint *goodcerts;
- char *h;
- int fd;
- uchar hash[SHA1dlen];
- c = mallocz(sizeof(*c), 1); /* Note: not freed on success */
- if (c == nil)
- return Giveup;
- dBprint("STARTTLS\r\n");
- if (getreply() != 2)
- return Giveup;
- fd = tlsClient(Bfildes(&bout), c);
- if (fd < 0) {
- syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
- return Giveup;
- }
- goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
- if (goodcerts == nil) {
- free(c);
- close(fd);
- syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
- return Giveup; /* how to recover? TLS is started */
- }
- /* compute sha1 hash of remote's certificate, see if we know it */
- sha1(c->cert, c->certlen, hash, nil);
- if (!okThumbprint(hash, goodcerts)) {
- /* TODO? if not excluded, add hash to thumb list */
- free(c);
- close(fd);
- h = malloc(2*sizeof hash + 1);
- if (h != nil) {
- enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
- // print("x509 sha1=%s", h);
- syslog(0, "smtp",
- "remote cert. has bad thumbprint: x509 sha1=%s server=%q",
- h, ddomain);
- free(h);
- }
- return Giveup; /* how to recover? TLS is started */
- }
- freeThumbprints(goodcerts);
- Bterm(&bin);
- Bterm(&bout);
- /*
- * set up bin & bout to use the TLS fd, i/o upon which generates
- * i/o on the original, underlying fd.
- */
- Binit(&bin, fd, OREAD);
- fd = dup(fd, -1);
- Binit(&bout, fd, OWRITE);
- syslog(0, "smtp", "started TLS to %q", ddomain);
- return(hello(me, 1));
- }
- static char *
- doauth(char *methods)
- {
- char *buf, *base64;
- int n;
- DS ds;
- UserPasswd *p;
- dial_string_parse(ddomain, &ds);
- if(user != nil)
- p = auth_getuserpasswd(nil,
- "proto=pass service=smtp server=%q user=%q", ds.host, user);
- else
- p = auth_getuserpasswd(nil,
- "proto=pass service=smtp server=%q", ds.host);
- if (p == nil)
- return Giveup;
- if (strstr(methods, "LOGIN")){
- dBprint("AUTH LOGIN\r\n");
- if (getreply() != 3)
- return Retry;
- n = strlen(p->user);
- base64 = malloc(2*n);
- if (base64 == nil)
- return Retry; /* Out of memory */
- enc64(base64, 2*n, (uchar *)p->user, n);
- dBprint("%s\r\n", base64);
- if (getreply() != 3)
- return Retry;
- n = strlen(p->passwd);
- base64 = malloc(2*n);
- if (base64 == nil)
- return Retry; /* Out of memory */
- enc64(base64, 2*n, (uchar *)p->passwd, n);
- dBprint("%s\r\n", base64);
- if (getreply() != 2)
- return Retry;
- free(base64);
- }
- else
- if (strstr(methods, "PLAIN")){
- n = strlen(p->user) + strlen(p->passwd) + 3;
- buf = malloc(n);
- base64 = malloc(2 * n);
- if (buf == nil || base64 == nil) {
- free(buf);
- return Retry; /* Out of memory */
- }
- snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);
- enc64(base64, 2 * n, (uchar *)buf, n - 1);
- free(buf);
- dBprint("AUTH PLAIN %s\r\n", base64);
- free(base64);
- if (getreply() != 2)
- return Retry;
- }
- else
- return "No supported AUTH method";
- return(0);
- }
- char *
- hello(char *me, int encrypted)
- {
- int ehlo;
- String *r;
- char *ret, *s, *t;
- if (!encrypted) {
- /*
- * Verizon fails to print the smtp greeting banner when it
- * answers a call. Send a no-op in the hope of making it
- * talk.
- */
- if (autistic) {
- dBprint("NOOP\r\n");
- getreply(); /* consume the smtp greeting */
- /* next reply will be response to noop */
- }
- switch(getreply()){
- case 2:
- break;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
- }
- ehlo = 1;
- Again:
- if(ehlo)
- dBprint("EHLO %s\r\n", me);
- else
- dBprint("HELO %s\r\n", me);
- switch (getreply()) {
- case 2:
- break;
- case 5:
- if(ehlo){
- ehlo = 0;
- goto Again;
- }
- return Giveup;
- default:
- return Retry;
- }
- r = s_clone(reply);
- if(r == nil)
- return Retry; /* Out of memory or couldn't get string */
- /* Invariant: every line has a newline, a result of getcrlf() */
- for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
- *t = '\0';
- for (t = s; *t != '\0'; t++)
- *t = toupper(*t);
- if(!encrypted && trysecure &&
- (strcmp(s, "250-STARTTLS") == 0 ||
- strcmp(s, "250 STARTTLS") == 0)){
- s_free(r);
- return dotls(me);
- }
- if(tryauth && (encrypted || insecure) &&
- (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
- strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
- ret = doauth(s + strlen("250 AUTH "));
- s_free(r);
- return ret;
- }
- }
- s_free(r);
- return 0;
- }
- /*
- * report sender to remote
- */
- char *
- mailfrom(char *from)
- {
- if(!returnable(from))
- dBprint("MAIL FROM:<>\r\n");
- else
- if(strchr(from, '@'))
- dBprint("MAIL FROM:<%s>\r\n", from);
- else
- dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
- switch(getreply()){
- case 2:
- break;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
- return 0;
- }
- /*
- * report a recipient to remote
- */
- char *
- rcptto(char *to)
- {
- String *s;
- s = unescapespecial(bangtoat(to));
- if(toline == 0)
- toline = s_new();
- else
- s_append(toline, ", ");
- s_append(toline, s_to_c(s));
- if(strchr(s_to_c(s), '@'))
- dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
- else {
- s_append(toline, "@");
- s_append(toline, ddomain);
- dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
- }
- alarm(10*alarmscale);
- switch(getreply()){
- case 2:
- break;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
- return 0;
- }
- static char hex[] = "0123456789abcdef";
- /*
- * send the damn thing
- */
- char *
- data(String *from, Biobuf *b)
- {
- char *buf, *cp;
- int i, n, nbytes, bufsize, eof, r;
- String *fromline;
- char errmsg[Errlen];
- char id[40];
- /*
- * input the header.
- */
- buf = malloc(1);
- if(buf == 0){
- s_append(s_restart(reply), "out of memory");
- return Retry;
- }
- n = 0;
- eof = 0;
- for(;;){
- cp = Brdline(b, '\n');
- if(cp == nil){
- eof = 1;
- break;
- }
- nbytes = Blinelen(b);
- buf = realloc(buf, n+nbytes+1);
- if(buf == 0){
- s_append(s_restart(reply), "out of memory");
- return Retry;
- }
- strncpy(buf+n, cp, nbytes);
- n += nbytes;
- if(nbytes == 1) /* end of header */
- break;
- }
- buf[n] = 0;
- bufsize = n;
- /*
- * parse the header, turn all addresses into @ format
- */
- yyinit(buf, n);
- yyparse();
- /*
- * print message observing '.' escapes and using \r\n for \n
- */
- alarm(20*alarmscale);
- if(!filter){
- dBprint("DATA\r\n");
- switch(getreply()){
- case 3:
- break;
- case 5:
- free(buf);
- return Giveup;
- default:
- free(buf);
- return Retry;
- }
- }
- /*
- * send header. add a message-id, a sender, and a date if there
- * isn't one
- */
- nbytes = 0;
- fromline = convertheader(from);
- uneaten = buf;
- srand(truerand());
- if(messageid == 0){
- for(i=0; i<16; i++){
- r = rand()&0xFF;
- id[2*i] = hex[r&0xF];
- id[2*i+1] = hex[(r>>4)&0xF];
- }
- id[2*i] = '\0';
- nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
- if(debug)
- Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
- }
- if(originator==0){
- nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
- if(debug)
- Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
- }
- s_free(fromline);
- if(destination == 0 && toline)
- if(*s_to_c(toline) == '@'){ /* route addr */
- nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline));
- if(debug)
- Bprint(&berr, "To: <%s>\r\n", s_to_c(toline));
- } else {
- nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline));
- if(debug)
- Bprint(&berr, "To: %s\r\n", s_to_c(toline));
- }
- if(date==0 && udate)
- nbytes += printdate(udate);
- if (usys)
- uneaten = usys->end + 1;
- nbytes += printheader();
- if (*uneaten != '\n')
- putcrnl("\n", 1);
- /*
- * send body
- */
- putcrnl(uneaten, buf+n - uneaten);
- nbytes += buf+n - uneaten;
- if(eof == 0){
- for(;;){
- n = Bread(b, buf, bufsize);
- if(n < 0){
- rerrstr(errmsg, sizeof(errmsg));
- s_append(s_restart(reply), errmsg);
- free(buf);
- return Retry;
- }
- if(n == 0)
- break;
- alarm(10*alarmscale);
- putcrnl(buf, n);
- nbytes += n;
- }
- }
- free(buf);
- if(!filter){
- if(last != '\n')
- dBprint("\r\n.\r\n");
- else
- dBprint(".\r\n");
- alarm(10*alarmscale);
- switch(getreply()){
- case 2:
- break;
- case 5:
- return Giveup;
- default:
- return Retry;
- }
- syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
- nbytes, s_to_c(toline));/**/
- }
- return 0;
- }
- /*
- * we're leaving
- */
- void
- quit(char *rv)
- {
- /* 60 minutes to quit */
- quitting = 1;
- quitrv = rv;
- alarm(60*alarmscale);
- dBprint("QUIT\r\n");
- getreply();
- Bterm(&bout);
- Bterm(&bfile);
- }
- /*
- * read a reply into a string, return the reply code
- */
- int
- getreply(void)
- {
- char *line;
- int rv;
- reply = s_reset(reply);
- for(;;){
- line = getcrnl(reply);
- if(debug)
- Bflush(&berr);
- if(line == 0)
- return -1;
- if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
- return -1;
- if(line[3] != '-')
- break;
- }
- if(debug)
- Bflush(&berr);
- rv = atoi(line)/100;
- return rv;
- }
- void
- addhostdom(String *buf, char *host)
- {
- s_append(buf, "@");
- s_append(buf, host);
- }
- /*
- * Convert from `bang' to `source routing' format.
- *
- * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o
- */
- String *
- bangtoat(char *addr)
- {
- String *buf;
- register int i;
- int j, d;
- char *field[128];
- /* parse the '!' format address */
- buf = s_new();
- for(i = 0; addr; i++){
- field[i] = addr;
- addr = strchr(addr, '!');
- if(addr)
- *addr++ = 0;
- }
- if (i==1) {
- s_append(buf, field[0]);
- return buf;
- }
- /*
- * count leading domain fields (non-domains don't count)
- */
- for(d = 0; d<i-1; d++)
- if(strchr(field[d], '.')==0)
- break;
- /*
- * if there are more than 1 leading domain elements,
- * put them in as source routing
- */
- if(d > 1){
- addhostdom(buf, field[0]);
- for(j=1; j<d-1; j++){
- s_append(buf, ",");
- s_append(buf, "@");
- s_append(buf, field[j]);
- }
- s_append(buf, ":");
- }
- /*
- * throw in the non-domain elements separated by '!'s
- */
- s_append(buf, field[d]);
- for(j=d+1; j<=i-1; j++) {
- s_append(buf, "!");
- s_append(buf, field[j]);
- }
- if(d)
- addhostdom(buf, field[d-1]);
- return buf;
- }
- /*
- * convert header addresses to @ format.
- * if the address is a source address, and a domain is specified,
- * make sure it falls in the domain.
- */
- String*
- convertheader(String *from)
- {
- Field *f;
- Node *p, *lastp;
- String *a;
- if(!returnable(s_to_c(from))){
- from = s_new();
- s_append(from, "Postmaster");
- addhostdom(from, hostdomain);
- } else
- if(strchr(s_to_c(from), '@') == 0){
- a = username(from);
- if(a) {
- s_append(a, " <");
- s_append(a, s_to_c(from));
- addhostdom(a, hostdomain);
- s_append(a, ">");
- from = a;
- } else {
- from = s_copy(s_to_c(from));
- addhostdom(from, hostdomain);
- }
- } else
- from = s_copy(s_to_c(from));
- for(f = firstfield; f; f = f->next){
- lastp = 0;
- for(p = f->node; p; lastp = p, p = p->next){
- if(!p->addr)
- continue;
- a = bangtoat(s_to_c(p->s));
- s_free(p->s);
- if(strchr(s_to_c(a), '@') == 0)
- addhostdom(a, hostdomain);
- else if(*s_to_c(a) == '@')
- a = fixrouteaddr(a, p->next, lastp);
- p->s = a;
- }
- }
- return from;
- }
- /*
- * ensure route addr has brackets around it
- */
- String*
- fixrouteaddr(String *raddr, Node *next, Node *last)
- {
- String *a;
- if(last && last->c == '<' && next && next->c == '>')
- return raddr; /* properly formed already */
- a = s_new();
- s_append(a, "<");
- s_append(a, s_to_c(raddr));
- s_append(a, ">");
- s_free(raddr);
- return a;
- }
- /*
- * print out the parsed header
- */
- int
- printheader(void)
- {
- int n, len;
- Field *f;
- Node *p;
- char *cp;
- char c[1];
- n = 0;
- for(f = firstfield; f; f = f->next){
- for(p = f->node; p; p = p->next){
- if(p->s)
- n += dBprint("%s", s_to_c(p->s));
- else {
- c[0] = p->c;
- putcrnl(c, 1);
- n++;
- }
- if(p->white){
- cp = s_to_c(p->white);
- len = strlen(cp);
- putcrnl(cp, len);
- n += len;
- }
- uneaten = p->end;
- }
- putcrnl("\n", 1);
- n++;
- uneaten++; /* skip newline */
- }
- return n;
- }
- /*
- * add a domain onto an name, return the new name
- */
- char *
- domainify(char *name, char *domain)
- {
- static String *s;
- char *p;
- if(domain==0 || strchr(name, '.')!=0)
- return name;
- s = s_reset(s);
- s_append(s, name);
- p = strchr(domain, '.');
- if(p == 0){
- s_append(s, ".");
- p = domain;
- }
- s_append(s, p);
- return s_to_c(s);
- }
- /*
- * print message observing '.' escapes and using \r\n for \n
- */
- void
- putcrnl(char *cp, int n)
- {
- int c;
- for(; n; n--, cp++){
- c = *cp;
- if(c == '\n')
- dBputc('\r');
- else if(c == '.' && last=='\n')
- dBputc('.');
- dBputc(c);
- last = c;
- }
- }
- /*
- * Get a line including a crnl into a string. Convert crnl into nl.
- */
- char *
- getcrnl(String *s)
- {
- int c;
- int count;
- count = 0;
- for(;;){
- c = Bgetc(&bin);
- if(debug)
- Bputc(&berr, c);
- switch(c){
- case -1:
- s_append(s, "connection closed unexpectedly by remote system");
- s_terminate(s);
- return 0;
- case '\r':
- c = Bgetc(&bin);
- if(c == '\n'){
- case '\n':
- s_putc(s, c);
- if(debug)
- Bputc(&berr, c);
- count++;
- s_terminate(s);
- return s->ptr - count;
- }
- Bungetc(&bin);
- s_putc(s, '\r');
- if(debug)
- Bputc(&berr, '\r');
- count++;
- break;
- default:
- s_putc(s, c);
- count++;
- break;
- }
- }
- }
- /*
- * print out a parsed date
- */
- int
- printdate(Node *p)
- {
- int n, sep = 0;
- n = dBprint("Date: %s,", s_to_c(p->s));
- for(p = p->next; p; p = p->next){
- if(p->s){
- if(sep == 0) {
- dBputc(' ');
- n++;
- }
- if (p->next)
- n += dBprint("%s", s_to_c(p->s));
- else
- n += dBprint("%s", rewritezone(s_to_c(p->s)));
- sep = 0;
- } else {
- dBputc(p->c);
- n++;
- sep = 1;
- }
- }
- n += dBprint("\r\n");
- return n;
- }
- char *
- rewritezone(char *z)
- {
- int mindiff;
- char s;
- Tm *tm;
- static char x[7];
- tm = localtime(time(0));
- mindiff = tm->tzoff/60;
- /* if not in my timezone, don't change anything */
- if(strcmp(tm->zone, z) != 0)
- return z;
- if(mindiff < 0){
- s = '-';
- mindiff = -mindiff;
- } else
- s = '+';
- sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
- return x;
- }
- /*
- * stolen from libc/port/print.c
- */
- #define SIZE 4096
- int
- dBprint(char *fmt, ...)
- {
- char buf[SIZE], *out;
- va_list arg;
- int n;
- va_start(arg, fmt);
- out = vseprint(buf, buf+SIZE, fmt, arg);
- va_end(arg);
- if(debug){
- Bwrite(&berr, buf, (long)(out-buf));
- Bflush(&berr);
- }
- n = Bwrite(&bout, buf, (long)(out-buf));
- Bflush(&bout);
- return n;
- }
- int
- dBputc(int x)
- {
- if(debug)
- Bputc(&berr, x);
- return Bputc(&bout, x);
- }
|