1 5cdb1798 2005-10-29 devnull #include "common.h"
2 5cdb1798 2005-10-29 devnull #include "smtp.h"
3 5cdb1798 2005-10-29 devnull #include <ctype.h>
4 5cdb1798 2005-10-29 devnull #include <mp.h>
5 5cdb1798 2005-10-29 devnull #include <libsec.h>
6 5cdb1798 2005-10-29 devnull #include <auth.h>
7 5cdb1798 2005-10-29 devnull #include <ndb.h>
9 5cdb1798 2005-10-29 devnull static char* connect(char*);
10 5cdb1798 2005-10-29 devnull static char* dotls(char*);
11 5cdb1798 2005-10-29 devnull static char* doauth(char*);
12 5cdb1798 2005-10-29 devnull char* hello(char*, int);
13 5cdb1798 2005-10-29 devnull char* mailfrom(char*);
14 5cdb1798 2005-10-29 devnull char* rcptto(char*);
15 5cdb1798 2005-10-29 devnull char* data(String*, Biobuf*);
16 5cdb1798 2005-10-29 devnull void quit(char*);
17 5cdb1798 2005-10-29 devnull int getreply(void);
18 5cdb1798 2005-10-29 devnull void addhostdom(String*, char*);
19 5cdb1798 2005-10-29 devnull String* bangtoat(char*);
20 5cdb1798 2005-10-29 devnull String* convertheader(String*);
21 5cdb1798 2005-10-29 devnull int printheader(void);
22 5cdb1798 2005-10-29 devnull char* domainify(char*, char*);
23 5cdb1798 2005-10-29 devnull void putcrnl(char*, int);
24 5cdb1798 2005-10-29 devnull char* getcrnl(String*);
25 5cdb1798 2005-10-29 devnull int printdate(Node*);
26 5cdb1798 2005-10-29 devnull char *rewritezone(char *);
27 5cdb1798 2005-10-29 devnull int dBprint(char*, ...);
28 5cdb1798 2005-10-29 devnull int dBputc(int);
29 5cdb1798 2005-10-29 devnull String* fixrouteaddr(String*, Node*, Node*);
30 5cdb1798 2005-10-29 devnull char* expand_addr(char* a);
31 5cdb1798 2005-10-29 devnull int ping;
32 5cdb1798 2005-10-29 devnull int insecure;
34 5cdb1798 2005-10-29 devnull #define Retry "Retry, Temporary Failure"
35 5cdb1798 2005-10-29 devnull #define Giveup "Permanent Failure"
37 5cdb1798 2005-10-29 devnull int debug; /* true if we're debugging */
38 5cdb1798 2005-10-29 devnull String *reply; /* last reply */
39 5cdb1798 2005-10-29 devnull String *toline;
40 5cdb1798 2005-10-29 devnull int alarmscale;
41 5cdb1798 2005-10-29 devnull int last = 'n'; /* last character sent by putcrnl() */
42 5cdb1798 2005-10-29 devnull int filter;
43 5cdb1798 2005-10-29 devnull int trysecure; /* Try to use TLS if the other side supports it */
44 5cdb1798 2005-10-29 devnull int tryauth; /* Try to authenticate, if supported */
45 5cdb1798 2005-10-29 devnull int quitting; /* when error occurs in quit */
46 5cdb1798 2005-10-29 devnull char *quitrv; /* deferred return value when in quit */
47 5cdb1798 2005-10-29 devnull char ddomain[1024]; /* domain name of destination machine */
48 5cdb1798 2005-10-29 devnull char *gdomain; /* domain name of gateway */
49 5cdb1798 2005-10-29 devnull char *uneaten; /* first character after rfc822 headers */
50 5cdb1798 2005-10-29 devnull char *farend; /* system we are trying to send to */
51 5cdb1798 2005-10-29 devnull char *user; /* user we are authenticating as, if authenticating */
52 5cdb1798 2005-10-29 devnull char hostdomain[256];
53 5cdb1798 2005-10-29 devnull Biobuf bin;
54 5cdb1798 2005-10-29 devnull Biobuf bout;
55 5cdb1798 2005-10-29 devnull Biobuf berr;
56 5cdb1798 2005-10-29 devnull Biobuf bfile;
59 5cdb1798 2005-10-29 devnull usage(void)
61 5cdb1798 2005-10-29 devnull fprint(2, "usage: smtp [-adips] [-uuser] [-hhost] [.domain] net!host[!service] sender rcpt-list\n");
62 5cdb1798 2005-10-29 devnull exits(Giveup);
66 5cdb1798 2005-10-29 devnull timeout(void *x, char *msg)
69 5cdb1798 2005-10-29 devnull syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg);
70 5cdb1798 2005-10-29 devnull if(strstr(msg, "alarm")){
71 5cdb1798 2005-10-29 devnull fprint(2, "smtp timeout: connection to %s timed out\n", farend);
72 5cdb1798 2005-10-29 devnull if(quitting)
73 5cdb1798 2005-10-29 devnull exits(quitrv);
74 5cdb1798 2005-10-29 devnull exits(Retry);
76 5cdb1798 2005-10-29 devnull if(strstr(msg, "closed pipe")){
77 5cdb1798 2005-10-29 devnull /* call _exits() to prevent Bio from trying to flush closed pipe */
78 5cdb1798 2005-10-29 devnull fprint(2, "smtp timeout: connection closed to %s\n", farend);
79 5cdb1798 2005-10-29 devnull if(quitting){
80 5cdb1798 2005-10-29 devnull syslog(0, "smtp.fail", "closed pipe to %s", farend);
81 5cdb1798 2005-10-29 devnull _exits(quitrv);
83 5cdb1798 2005-10-29 devnull _exits(Retry);
85 5cdb1798 2005-10-29 devnull return 0;
89 5cdb1798 2005-10-29 devnull removenewline(char *p)
91 5cdb1798 2005-10-29 devnull int n = strlen(p)-1;
93 5cdb1798 2005-10-29 devnull if(n < 0)
95 5cdb1798 2005-10-29 devnull if(p[n] == '\n')
96 5cdb1798 2005-10-29 devnull p[n] = 0;
100 5cdb1798 2005-10-29 devnull threadmain(int argc, char **argv)
102 5cdb1798 2005-10-29 devnull char hellodomain[256];
103 5cdb1798 2005-10-29 devnull char *host, *domain;
104 5cdb1798 2005-10-29 devnull String *from;
105 5cdb1798 2005-10-29 devnull String *fromm;
106 5cdb1798 2005-10-29 devnull String *sender;
107 5cdb1798 2005-10-29 devnull char *addr;
108 5cdb1798 2005-10-29 devnull char *rv, *trv;
109 5cdb1798 2005-10-29 devnull int i, ok, rcvrs;
110 5cdb1798 2005-10-29 devnull char **errs;
112 5cdb1798 2005-10-29 devnull alarmscale = 60*1000; /* minutes */
113 5cdb1798 2005-10-29 devnull quotefmtinstall();
114 5cdb1798 2005-10-29 devnull errs = malloc(argc*sizeof(char*));
115 5cdb1798 2005-10-29 devnull reply = s_new();
116 5cdb1798 2005-10-29 devnull host = 0;
117 5cdb1798 2005-10-29 devnull ARGBEGIN{
118 5cdb1798 2005-10-29 devnull case 'a':
119 5cdb1798 2005-10-29 devnull tryauth = 1;
120 5cdb1798 2005-10-29 devnull trysecure = 1;
122 5cdb1798 2005-10-29 devnull case 'f':
123 5cdb1798 2005-10-29 devnull filter = 1;
125 5cdb1798 2005-10-29 devnull case 'd':
126 5cdb1798 2005-10-29 devnull debug = 1;
128 5cdb1798 2005-10-29 devnull case 'g':
129 5cdb1798 2005-10-29 devnull gdomain = ARGF();
131 5cdb1798 2005-10-29 devnull case 'h':
132 5cdb1798 2005-10-29 devnull host = ARGF();
134 5cdb1798 2005-10-29 devnull case 'i':
135 5cdb1798 2005-10-29 devnull insecure = 1;
137 5cdb1798 2005-10-29 devnull case 'p':
138 5cdb1798 2005-10-29 devnull alarmscale = 10*1000; /* tens of seconds */
139 5cdb1798 2005-10-29 devnull ping = 1;
141 5cdb1798 2005-10-29 devnull case 's':
142 5cdb1798 2005-10-29 devnull trysecure = 1;
144 5cdb1798 2005-10-29 devnull case 'u':
145 5cdb1798 2005-10-29 devnull user = ARGF();
147 5cdb1798 2005-10-29 devnull default:
148 5cdb1798 2005-10-29 devnull usage();
150 5cdb1798 2005-10-29 devnull }ARGEND;
152 5cdb1798 2005-10-29 devnull Binit(&berr, 2, OWRITE);
153 5cdb1798 2005-10-29 devnull Binit(&bfile, 0, OREAD);
156 5cdb1798 2005-10-29 devnull * get domain and add to host name
158 5cdb1798 2005-10-29 devnull if(*argv && **argv=='.') {
159 5cdb1798 2005-10-29 devnull domain = *argv;
160 5cdb1798 2005-10-29 devnull argv++; argc--;
162 5cdb1798 2005-10-29 devnull domain = domainname_read();
163 5cdb1798 2005-10-29 devnull if(host == 0)
164 5cdb1798 2005-10-29 devnull host = sysname_read();
165 5cdb1798 2005-10-29 devnull strcpy(hostdomain, domainify(host, domain));
166 5cdb1798 2005-10-29 devnull strcpy(hellodomain, domainify(sysname_read(), domain));
169 5cdb1798 2005-10-29 devnull * get destination address
171 5cdb1798 2005-10-29 devnull if(*argv == 0)
172 5cdb1798 2005-10-29 devnull usage();
173 5cdb1798 2005-10-29 devnull addr = *argv++; argc--;
174 5cdb1798 2005-10-29 devnull // expand $smtp if necessary
175 5cdb1798 2005-10-29 devnull addr = expand_addr(addr);
176 5cdb1798 2005-10-29 devnull farend = addr;
179 5cdb1798 2005-10-29 devnull * get sender's machine.
180 5cdb1798 2005-10-29 devnull * get sender in internet style. domainify if necessary.
182 5cdb1798 2005-10-29 devnull if(*argv == 0)
183 5cdb1798 2005-10-29 devnull usage();
184 5cdb1798 2005-10-29 devnull sender = unescapespecial(s_copy(*argv++));
186 5cdb1798 2005-10-29 devnull fromm = s_clone(sender);
187 5cdb1798 2005-10-29 devnull rv = strrchr(s_to_c(fromm), '!');
189 5cdb1798 2005-10-29 devnull *rv = 0;
191 5cdb1798 2005-10-29 devnull *s_to_c(fromm) = 0;
192 5cdb1798 2005-10-29 devnull from = bangtoat(s_to_c(sender));
195 5cdb1798 2005-10-29 devnull * send the mail
197 5cdb1798 2005-10-29 devnull if(filter){
198 5cdb1798 2005-10-29 devnull Binit(&bout, 1, OWRITE);
199 5cdb1798 2005-10-29 devnull rv = data(from, &bfile);
200 5cdb1798 2005-10-29 devnull if(rv != 0)
201 5cdb1798 2005-10-29 devnull goto error;
202 5cdb1798 2005-10-29 devnull exits(0);
205 5cdb1798 2005-10-29 devnull /* 10 minutes to get through the initial handshake */
206 5cdb1798 2005-10-29 devnull atnotify(timeout, 1);
208 5cdb1798 2005-10-29 devnull alarm(10*alarmscale);
209 5cdb1798 2005-10-29 devnull if((rv = connect(addr)) != 0)
210 5cdb1798 2005-10-29 devnull exits(rv);
211 5cdb1798 2005-10-29 devnull alarm(10*alarmscale);
212 5cdb1798 2005-10-29 devnull if((rv = hello(hellodomain, 0)) != 0)
213 5cdb1798 2005-10-29 devnull goto error;
214 5cdb1798 2005-10-29 devnull alarm(10*alarmscale);
215 5cdb1798 2005-10-29 devnull if((rv = mailfrom(s_to_c(from))) != 0)
216 5cdb1798 2005-10-29 devnull goto error;
219 5cdb1798 2005-10-29 devnull rcvrs = 0;
220 5cdb1798 2005-10-29 devnull /* if any rcvrs are ok, we try to send the message */
221 5cdb1798 2005-10-29 devnull for(i = 0; i < argc; i++){
222 5cdb1798 2005-10-29 devnull if((trv = rcptto(argv[i])) != 0){
223 5cdb1798 2005-10-29 devnull /* remember worst error */
224 5cdb1798 2005-10-29 devnull if(rv != Giveup)
225 5cdb1798 2005-10-29 devnull rv = trv;
226 5cdb1798 2005-10-29 devnull errs[rcvrs] = strdup(s_to_c(reply));
227 5cdb1798 2005-10-29 devnull removenewline(errs[rcvrs]);
228 5cdb1798 2005-10-29 devnull } else {
230 5cdb1798 2005-10-29 devnull errs[rcvrs] = 0;
232 5cdb1798 2005-10-29 devnull rcvrs++;
235 5cdb1798 2005-10-29 devnull /* if no ok rcvrs or worst error is retry, give up */
236 5cdb1798 2005-10-29 devnull if(ok == 0 || rv == Retry)
237 5cdb1798 2005-10-29 devnull goto error;
239 5cdb1798 2005-10-29 devnull if(ping){
240 5cdb1798 2005-10-29 devnull quit(0);
241 5cdb1798 2005-10-29 devnull exits(0);
244 5cdb1798 2005-10-29 devnull rv = data(from, &bfile);
245 5cdb1798 2005-10-29 devnull if(rv != 0)
246 5cdb1798 2005-10-29 devnull goto error;
247 5cdb1798 2005-10-29 devnull quit(0);
248 5cdb1798 2005-10-29 devnull if(rcvrs == ok)
249 5cdb1798 2005-10-29 devnull exits(0);
252 5cdb1798 2005-10-29 devnull * here when some but not all rcvrs failed
254 5cdb1798 2005-10-29 devnull fprint(2, "%s connect to %s:\n", thedate(), addr);
255 5cdb1798 2005-10-29 devnull for(i = 0; i < rcvrs; i++){
256 5cdb1798 2005-10-29 devnull if(errs[i]){
257 5cdb1798 2005-10-29 devnull syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);
258 5cdb1798 2005-10-29 devnull fprint(2, " mail to %s failed: %s", argv[i], errs[i]);
261 5cdb1798 2005-10-29 devnull exits(Giveup);
264 5cdb1798 2005-10-29 devnull * here when all rcvrs failed
267 5cdb1798 2005-10-29 devnull removenewline(s_to_c(reply));
268 5cdb1798 2005-10-29 devnull syslog(0, "smtp.fail", "%s to %s failed: %s",
269 5cdb1798 2005-10-29 devnull ping ? "ping" : "delivery",
270 5cdb1798 2005-10-29 devnull addr, s_to_c(reply));
271 5cdb1798 2005-10-29 devnull fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
272 5cdb1798 2005-10-29 devnull if(!filter)
273 5cdb1798 2005-10-29 devnull quit(rv);
274 5cdb1798 2005-10-29 devnull exits(rv);
278 5cdb1798 2005-10-29 devnull * connect to the remote host
280 5cdb1798 2005-10-29 devnull static char *
281 5cdb1798 2005-10-29 devnull connect(char* net)
283 5cdb1798 2005-10-29 devnull char buf[256];
286 5cdb1798 2005-10-29 devnull fd = mxdial(net, ddomain, gdomain);
288 5cdb1798 2005-10-29 devnull if(fd < 0){
289 5cdb1798 2005-10-29 devnull rerrstr(buf, sizeof(buf));
290 5cdb1798 2005-10-29 devnull Bprint(&berr, "smtp: %s (%s)\n", buf, net);
291 5cdb1798 2005-10-29 devnull syslog(0, "smtp.fail", "%s (%s)", buf, net);
292 5cdb1798 2005-10-29 devnull if(strstr(buf, "illegal")
293 5cdb1798 2005-10-29 devnull || strstr(buf, "unknown")
294 5cdb1798 2005-10-29 devnull || strstr(buf, "can't translate"))
295 5cdb1798 2005-10-29 devnull return Giveup;
297 5cdb1798 2005-10-29 devnull return Retry;
299 5cdb1798 2005-10-29 devnull Binit(&bin, fd, OREAD);
300 5cdb1798 2005-10-29 devnull fd = dup(fd, -1);
301 5cdb1798 2005-10-29 devnull Binit(&bout, fd, OWRITE);
302 5cdb1798 2005-10-29 devnull return 0;
305 5cdb1798 2005-10-29 devnull static char smtpthumbs[] = "/sys/lib/tls/smtp";
306 5cdb1798 2005-10-29 devnull static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude";
309 5cdb1798 2005-10-29 devnull * exchange names with remote host, attempt to
310 5cdb1798 2005-10-29 devnull * enable encryption and optionally authenticate.
311 5cdb1798 2005-10-29 devnull * not fatal if we can't.
313 5cdb1798 2005-10-29 devnull static char *
314 5cdb1798 2005-10-29 devnull dotls(char *me)
316 5cdb1798 2005-10-29 devnull TLSconn *c;
317 5cdb1798 2005-10-29 devnull Thumbprint *goodcerts;
318 5cdb1798 2005-10-29 devnull char *h;
320 5cdb1798 2005-10-29 devnull uchar hash[SHA1dlen];
322 5cdb1798 2005-10-29 devnull c = mallocz(sizeof(*c), 1); /* Note: not freed on success */
323 5cdb1798 2005-10-29 devnull if (c == nil)
324 5cdb1798 2005-10-29 devnull return Giveup;
326 5cdb1798 2005-10-29 devnull dBprint("STARTTLS\r\n");
327 5cdb1798 2005-10-29 devnull if (getreply() != 2)
328 5cdb1798 2005-10-29 devnull return Giveup;
330 5cdb1798 2005-10-29 devnull fd = tlsClient(Bfildes(&bout), c);
331 5cdb1798 2005-10-29 devnull if (fd < 0) {
332 5cdb1798 2005-10-29 devnull syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
333 5cdb1798 2005-10-29 devnull return Giveup;
335 5cdb1798 2005-10-29 devnull goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
336 5cdb1798 2005-10-29 devnull if (goodcerts == nil) {
337 5cdb1798 2005-10-29 devnull free(c);
338 5cdb1798 2005-10-29 devnull close(fd);
339 5cdb1798 2005-10-29 devnull syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
340 5cdb1798 2005-10-29 devnull return Giveup; /* how to recover? TLS is started */
343 5cdb1798 2005-10-29 devnull /* compute sha1 hash of remote's certificate, see if we know it */
344 5cdb1798 2005-10-29 devnull sha1(c->cert, c->certlen, hash, nil);
345 5cdb1798 2005-10-29 devnull if (!okThumbprint(hash, goodcerts)) {
346 5cdb1798 2005-10-29 devnull /* TODO? if not excluded, add hash to thumb list */
347 5cdb1798 2005-10-29 devnull free(c);
348 5cdb1798 2005-10-29 devnull close(fd);
349 5cdb1798 2005-10-29 devnull h = malloc(2*sizeof hash + 1);
350 5cdb1798 2005-10-29 devnull if (h != nil) {
351 5cdb1798 2005-10-29 devnull enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
352 5cdb1798 2005-10-29 devnull // print("x509 sha1=%s", h);
353 5cdb1798 2005-10-29 devnull syslog(0, "smtp",
354 5cdb1798 2005-10-29 devnull "remote cert. has bad thumbprint: x509 sha1=%s server=%q",
355 5cdb1798 2005-10-29 devnull h, ddomain);
356 5cdb1798 2005-10-29 devnull free(h);
358 5cdb1798 2005-10-29 devnull return Giveup; /* how to recover? TLS is started */
360 5cdb1798 2005-10-29 devnull freeThumbprints(goodcerts);
361 5cdb1798 2005-10-29 devnull Bterm(&bin);
362 5cdb1798 2005-10-29 devnull Bterm(&bout);
365 5cdb1798 2005-10-29 devnull * set up bin & bout to use the TLS fd, i/o upon which generates
366 5cdb1798 2005-10-29 devnull * i/o on the original, underlying fd.
368 5cdb1798 2005-10-29 devnull Binit(&bin, fd, OREAD);
369 5cdb1798 2005-10-29 devnull fd = dup(fd, -1);
370 5cdb1798 2005-10-29 devnull Binit(&bout, fd, OWRITE);
372 5cdb1798 2005-10-29 devnull syslog(0, "smtp", "started TLS to %q", ddomain);
373 5cdb1798 2005-10-29 devnull return(hello(me, 1));
376 5cdb1798 2005-10-29 devnull static char *
377 5cdb1798 2005-10-29 devnull doauth(char *methods)
379 5cdb1798 2005-10-29 devnull char *buf, *base64;
382 5cdb1798 2005-10-29 devnull UserPasswd *p;
384 5cdb1798 2005-10-29 devnull dial_string_parse(ddomain, &ds);
386 5cdb1798 2005-10-29 devnull if(user != nil)
387 5cdb1798 2005-10-29 devnull p = auth_getuserpasswd(nil,
388 5cdb1798 2005-10-29 devnull "proto=pass service=smtp server=%q user=%q", ds.host, user);
390 5cdb1798 2005-10-29 devnull p = auth_getuserpasswd(nil,
391 5cdb1798 2005-10-29 devnull "proto=pass service=smtp server=%q", ds.host);
392 5cdb1798 2005-10-29 devnull if (p == nil)
393 5cdb1798 2005-10-29 devnull return Giveup;
395 5cdb1798 2005-10-29 devnull if (strstr(methods, "LOGIN")){
396 5cdb1798 2005-10-29 devnull dBprint("AUTH LOGIN\r\n");
397 5cdb1798 2005-10-29 devnull if (getreply() != 3)
398 5cdb1798 2005-10-29 devnull return Retry;
400 5cdb1798 2005-10-29 devnull n = strlen(p->user);
401 5cdb1798 2005-10-29 devnull base64 = malloc(2*n);
402 5cdb1798 2005-10-29 devnull if (base64 == nil)
403 5cdb1798 2005-10-29 devnull return Retry; /* Out of memory */
404 5cdb1798 2005-10-29 devnull enc64(base64, 2*n, (uchar *)p->user, n);
405 5cdb1798 2005-10-29 devnull dBprint("%s\r\n", base64);
406 5cdb1798 2005-10-29 devnull if (getreply() != 3)
407 5cdb1798 2005-10-29 devnull return Retry;
409 5cdb1798 2005-10-29 devnull n = strlen(p->passwd);
410 5cdb1798 2005-10-29 devnull base64 = malloc(2*n);
411 5cdb1798 2005-10-29 devnull if (base64 == nil)
412 5cdb1798 2005-10-29 devnull return Retry; /* Out of memory */
413 5cdb1798 2005-10-29 devnull enc64(base64, 2*n, (uchar *)p->passwd, n);
414 5cdb1798 2005-10-29 devnull dBprint("%s\r\n", base64);
415 5cdb1798 2005-10-29 devnull if (getreply() != 2)
416 5cdb1798 2005-10-29 devnull return Retry;
418 5cdb1798 2005-10-29 devnull free(base64);
421 5cdb1798 2005-10-29 devnull if (strstr(methods, "PLAIN")){
422 5cdb1798 2005-10-29 devnull n = strlen(p->user) + strlen(p->passwd) + 3;
423 5cdb1798 2005-10-29 devnull buf = malloc(n);
424 5cdb1798 2005-10-29 devnull base64 = malloc(2 * n);
425 5cdb1798 2005-10-29 devnull if (buf == nil || base64 == nil) {
426 5cdb1798 2005-10-29 devnull free(buf);
427 5cdb1798 2005-10-29 devnull return Retry; /* Out of memory */
429 5cdb1798 2005-10-29 devnull snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);
430 5cdb1798 2005-10-29 devnull enc64(base64, 2 * n, (uchar *)buf, n - 1);
431 5cdb1798 2005-10-29 devnull free(buf);
432 5cdb1798 2005-10-29 devnull dBprint("AUTH PLAIN %s\r\n", base64);
433 5cdb1798 2005-10-29 devnull free(base64);
434 5cdb1798 2005-10-29 devnull if (getreply() != 2)
435 5cdb1798 2005-10-29 devnull return Retry;
438 5cdb1798 2005-10-29 devnull return "No supported AUTH method";
439 5cdb1798 2005-10-29 devnull return(0);
443 5cdb1798 2005-10-29 devnull hello(char *me, int encrypted)
445 5cdb1798 2005-10-29 devnull int ehlo;
446 5cdb1798 2005-10-29 devnull String *r;
447 5cdb1798 2005-10-29 devnull char *ret, *s, *t;
449 5cdb1798 2005-10-29 devnull if (!encrypted)
450 5cdb1798 2005-10-29 devnull switch(getreply()){
454 5cdb1798 2005-10-29 devnull return Giveup;
455 5cdb1798 2005-10-29 devnull default:
456 5cdb1798 2005-10-29 devnull return Retry;
459 5cdb1798 2005-10-29 devnull ehlo = 1;
461 5cdb1798 2005-10-29 devnull if(ehlo)
462 5cdb1798 2005-10-29 devnull dBprint("EHLO %s\r\n", me);
464 5cdb1798 2005-10-29 devnull dBprint("HELO %s\r\n", me);
465 5cdb1798 2005-10-29 devnull switch (getreply()) {
469 5cdb1798 2005-10-29 devnull if(ehlo){
470 5cdb1798 2005-10-29 devnull ehlo = 0;
471 5cdb1798 2005-10-29 devnull goto Again;
473 5cdb1798 2005-10-29 devnull return Giveup;
474 5cdb1798 2005-10-29 devnull default:
475 5cdb1798 2005-10-29 devnull return Retry;
477 5cdb1798 2005-10-29 devnull r = s_clone(reply);
478 5cdb1798 2005-10-29 devnull if(r == nil)
479 5cdb1798 2005-10-29 devnull return Retry; /* Out of memory or couldn't get string */
481 5cdb1798 2005-10-29 devnull /* Invariant: every line has a newline, a result of getcrlf() */
482 5cdb1798 2005-10-29 devnull for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
483 5cdb1798 2005-10-29 devnull *t = '\0';
484 5cdb1798 2005-10-29 devnull for (t = s; *t != '\0'; t++)
485 5cdb1798 2005-10-29 devnull *t = toupper(*t);
486 5cdb1798 2005-10-29 devnull if(!encrypted && trysecure &&
487 5cdb1798 2005-10-29 devnull (strcmp(s, "250-STARTTLS") == 0 ||
488 5cdb1798 2005-10-29 devnull strcmp(s, "250 STARTTLS") == 0)){
489 5cdb1798 2005-10-29 devnull s_free(r);
490 5cdb1798 2005-10-29 devnull return(dotls(me));
492 5cdb1798 2005-10-29 devnull if(tryauth && (encrypted || insecure) &&
493 5cdb1798 2005-10-29 devnull (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
494 5cdb1798 2005-10-29 devnull strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
495 5cdb1798 2005-10-29 devnull ret = doauth(s + strlen("250 AUTH "));
496 5cdb1798 2005-10-29 devnull s_free(r);
497 5cdb1798 2005-10-29 devnull return ret;
500 5cdb1798 2005-10-29 devnull s_free(r);
501 5cdb1798 2005-10-29 devnull return 0;
505 5cdb1798 2005-10-29 devnull * report sender to remote
508 5cdb1798 2005-10-29 devnull mailfrom(char *from)
510 5cdb1798 2005-10-29 devnull if(!returnable(from))
511 5cdb1798 2005-10-29 devnull dBprint("MAIL FROM:<>\r\n");
513 5cdb1798 2005-10-29 devnull if(strchr(from, '@'))
514 5cdb1798 2005-10-29 devnull dBprint("MAIL FROM:<%s>\r\n", from);
516 5cdb1798 2005-10-29 devnull dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
517 5cdb1798 2005-10-29 devnull switch(getreply()){
521 5cdb1798 2005-10-29 devnull return Giveup;
522 5cdb1798 2005-10-29 devnull default:
523 5cdb1798 2005-10-29 devnull return Retry;
525 5cdb1798 2005-10-29 devnull return 0;
529 5cdb1798 2005-10-29 devnull * report a recipient to remote
532 5cdb1798 2005-10-29 devnull rcptto(char *to)
534 5cdb1798 2005-10-29 devnull String *s;
536 5cdb1798 2005-10-29 devnull s = unescapespecial(bangtoat(to));
537 5cdb1798 2005-10-29 devnull if(toline == 0)
538 5cdb1798 2005-10-29 devnull toline = s_new();
540 5cdb1798 2005-10-29 devnull s_append(toline, ", ");
541 5cdb1798 2005-10-29 devnull s_append(toline, s_to_c(s));
542 5cdb1798 2005-10-29 devnull if(strchr(s_to_c(s), '@'))
543 5cdb1798 2005-10-29 devnull dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
545 5cdb1798 2005-10-29 devnull s_append(toline, "@");
546 5cdb1798 2005-10-29 devnull s_append(toline, ddomain);
547 5cdb1798 2005-10-29 devnull dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
549 5cdb1798 2005-10-29 devnull alarm(10*alarmscale);
550 5cdb1798 2005-10-29 devnull switch(getreply()){
554 5cdb1798 2005-10-29 devnull return Giveup;
555 5cdb1798 2005-10-29 devnull default:
556 5cdb1798 2005-10-29 devnull return Retry;
558 5cdb1798 2005-10-29 devnull return 0;
561 5cdb1798 2005-10-29 devnull static char hex[] = "0123456789abcdef";
564 5cdb1798 2005-10-29 devnull * send the damn thing
567 5cdb1798 2005-10-29 devnull data(String *from, Biobuf *b)
569 5cdb1798 2005-10-29 devnull char *buf, *cp;
570 5cdb1798 2005-10-29 devnull int i, n, nbytes, bufsize, eof, r;
571 5cdb1798 2005-10-29 devnull String *fromline;
572 5cdb1798 2005-10-29 devnull char errmsg[Errlen];
573 5cdb1798 2005-10-29 devnull char id[40];
576 5cdb1798 2005-10-29 devnull * input the header.
579 5cdb1798 2005-10-29 devnull buf = malloc(1);
580 5cdb1798 2005-10-29 devnull if(buf == 0){
581 5cdb1798 2005-10-29 devnull s_append(s_restart(reply), "out of memory");
582 5cdb1798 2005-10-29 devnull return Retry;
585 5cdb1798 2005-10-29 devnull eof = 0;
586 5cdb1798 2005-10-29 devnull for(;;){
587 5cdb1798 2005-10-29 devnull cp = Brdline(b, '\n');
588 5cdb1798 2005-10-29 devnull if(cp == nil){
589 5cdb1798 2005-10-29 devnull eof = 1;
592 5cdb1798 2005-10-29 devnull nbytes = Blinelen(b);
593 5cdb1798 2005-10-29 devnull buf = realloc(buf, n+nbytes+1);
594 5cdb1798 2005-10-29 devnull if(buf == 0){
595 5cdb1798 2005-10-29 devnull s_append(s_restart(reply), "out of memory");
596 5cdb1798 2005-10-29 devnull return Retry;
598 5cdb1798 2005-10-29 devnull strncpy(buf+n, cp, nbytes);
599 5cdb1798 2005-10-29 devnull n += nbytes;
600 5cdb1798 2005-10-29 devnull if(nbytes == 1) /* end of header */
603 5cdb1798 2005-10-29 devnull buf[n] = 0;
604 5cdb1798 2005-10-29 devnull bufsize = n;
607 5cdb1798 2005-10-29 devnull * parse the header, turn all addresses into @ format
609 5cdb1798 2005-10-29 devnull yyinit(buf, n);
610 5cdb1798 2005-10-29 devnull yyparse();
613 5cdb1798 2005-10-29 devnull * print message observing '.' escapes and using \r\n for \n
615 5cdb1798 2005-10-29 devnull alarm(20*alarmscale);
616 5cdb1798 2005-10-29 devnull if(!filter){
617 5cdb1798 2005-10-29 devnull dBprint("DATA\r\n");
618 5cdb1798 2005-10-29 devnull switch(getreply()){
622 5cdb1798 2005-10-29 devnull free(buf);
623 5cdb1798 2005-10-29 devnull return Giveup;
624 5cdb1798 2005-10-29 devnull default:
625 5cdb1798 2005-10-29 devnull free(buf);
626 5cdb1798 2005-10-29 devnull return Retry;
630 5cdb1798 2005-10-29 devnull * send header. add a message-id, a sender, and a date if there
631 5cdb1798 2005-10-29 devnull * isn't one
633 5cdb1798 2005-10-29 devnull nbytes = 0;
634 5cdb1798 2005-10-29 devnull fromline = convertheader(from);
635 5cdb1798 2005-10-29 devnull uneaten = buf;
637 5cdb1798 2005-10-29 devnull srand(truerand());
638 5cdb1798 2005-10-29 devnull if(messageid == 0){
639 5cdb1798 2005-10-29 devnull for(i=0; i<16; i++){
640 5cdb1798 2005-10-29 devnull r = rand()&0xFF;
641 5cdb1798 2005-10-29 devnull id[2*i] = hex[r&0xF];
642 5cdb1798 2005-10-29 devnull id[2*i+1] = hex[(r>>4)&0xF];
644 5cdb1798 2005-10-29 devnull id[2*i] = '\0';
645 5cdb1798 2005-10-29 devnull nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
646 5cdb1798 2005-10-29 devnull if(debug)
647 5cdb1798 2005-10-29 devnull Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
650 5cdb1798 2005-10-29 devnull if(originator==0){
651 5cdb1798 2005-10-29 devnull nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
652 5cdb1798 2005-10-29 devnull if(debug)
653 5cdb1798 2005-10-29 devnull Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
655 5cdb1798 2005-10-29 devnull s_free(fromline);
657 5cdb1798 2005-10-29 devnull if(destination == 0 && toline)
658 5cdb1798 2005-10-29 devnull if(*s_to_c(toline) == '@'){ /* route addr */
659 5cdb1798 2005-10-29 devnull nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline));
660 5cdb1798 2005-10-29 devnull if(debug)
661 5cdb1798 2005-10-29 devnull Bprint(&berr, "To: <%s>\r\n", s_to_c(toline));
662 5cdb1798 2005-10-29 devnull } else {
663 5cdb1798 2005-10-29 devnull nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline));
664 5cdb1798 2005-10-29 devnull if(debug)
665 5cdb1798 2005-10-29 devnull Bprint(&berr, "To: %s\r\n", s_to_c(toline));
668 5cdb1798 2005-10-29 devnull if(date==0 && udate)
669 5cdb1798 2005-10-29 devnull nbytes += printdate(udate);
670 5cdb1798 2005-10-29 devnull if (usys)
671 5cdb1798 2005-10-29 devnull uneaten = usys->end + 1;
672 5cdb1798 2005-10-29 devnull nbytes += printheader();
673 5cdb1798 2005-10-29 devnull if (*uneaten != '\n')
674 5cdb1798 2005-10-29 devnull putcrnl("\n", 1);
677 5cdb1798 2005-10-29 devnull * send body
680 5cdb1798 2005-10-29 devnull putcrnl(uneaten, buf+n - uneaten);
681 5cdb1798 2005-10-29 devnull nbytes += buf+n - uneaten;
682 5cdb1798 2005-10-29 devnull if(eof == 0){
683 5cdb1798 2005-10-29 devnull for(;;){
684 5cdb1798 2005-10-29 devnull n = Bread(b, buf, bufsize);
685 5cdb1798 2005-10-29 devnull if(n < 0){
686 5cdb1798 2005-10-29 devnull rerrstr(errmsg, sizeof(errmsg));
687 5cdb1798 2005-10-29 devnull s_append(s_restart(reply), errmsg);
688 5cdb1798 2005-10-29 devnull free(buf);
689 5cdb1798 2005-10-29 devnull return Retry;
691 5cdb1798 2005-10-29 devnull if(n == 0)
693 5cdb1798 2005-10-29 devnull alarm(10*alarmscale);
694 5cdb1798 2005-10-29 devnull putcrnl(buf, n);
695 5cdb1798 2005-10-29 devnull nbytes += n;
698 5cdb1798 2005-10-29 devnull free(buf);
699 5cdb1798 2005-10-29 devnull if(!filter){
700 5cdb1798 2005-10-29 devnull if(last != '\n')
701 5cdb1798 2005-10-29 devnull dBprint("\r\n.\r\n");
703 5cdb1798 2005-10-29 devnull dBprint(".\r\n");
704 5cdb1798 2005-10-29 devnull alarm(10*alarmscale);
705 5cdb1798 2005-10-29 devnull switch(getreply()){
709 5cdb1798 2005-10-29 devnull return Giveup;
710 5cdb1798 2005-10-29 devnull default:
711 5cdb1798 2005-10-29 devnull return Retry;
713 5cdb1798 2005-10-29 devnull syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
714 5cdb1798 2005-10-29 devnull nbytes, s_to_c(toline));/**/
716 5cdb1798 2005-10-29 devnull return 0;
720 5cdb1798 2005-10-29 devnull * we're leaving
723 5cdb1798 2005-10-29 devnull quit(char *rv)
725 5cdb1798 2005-10-29 devnull /* 60 minutes to quit */
726 5cdb1798 2005-10-29 devnull quitting = 1;
727 5cdb1798 2005-10-29 devnull quitrv = rv;
728 5cdb1798 2005-10-29 devnull alarm(60*alarmscale);
729 5cdb1798 2005-10-29 devnull dBprint("QUIT\r\n");
730 5cdb1798 2005-10-29 devnull getreply();
731 5cdb1798 2005-10-29 devnull Bterm(&bout);
732 5cdb1798 2005-10-29 devnull Bterm(&bfile);
736 5cdb1798 2005-10-29 devnull * read a reply into a string, return the reply code
739 5cdb1798 2005-10-29 devnull getreply(void)
741 5cdb1798 2005-10-29 devnull char *line;
744 5cdb1798 2005-10-29 devnull reply = s_reset(reply);
745 5cdb1798 2005-10-29 devnull for(;;){
746 5cdb1798 2005-10-29 devnull line = getcrnl(reply);
747 5cdb1798 2005-10-29 devnull if(line == 0)
748 5cdb1798 2005-10-29 devnull return -1;
749 5cdb1798 2005-10-29 devnull if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
750 5cdb1798 2005-10-29 devnull return -1;
751 5cdb1798 2005-10-29 devnull if(line[3] != '-')
754 5cdb1798 2005-10-29 devnull if(debug)
755 5cdb1798 2005-10-29 devnull Bflush(&berr);
756 5cdb1798 2005-10-29 devnull rv = atoi(line)/100;
757 5cdb1798 2005-10-29 devnull return rv;
760 5cdb1798 2005-10-29 devnull addhostdom(String *buf, char *host)
762 5cdb1798 2005-10-29 devnull s_append(buf, "@");
763 5cdb1798 2005-10-29 devnull s_append(buf, host);
767 5cdb1798 2005-10-29 devnull * Convert from `bang' to `source routing' format.
769 5cdb1798 2005-10-29 devnull * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o
771 5cdb1798 2005-10-29 devnull String *
772 5cdb1798 2005-10-29 devnull bangtoat(char *addr)
774 5cdb1798 2005-10-29 devnull String *buf;
775 5cdb1798 2005-10-29 devnull register int i;
776 5cdb1798 2005-10-29 devnull int j, d;
777 5cdb1798 2005-10-29 devnull char *field[128];
779 5cdb1798 2005-10-29 devnull /* parse the '!' format address */
780 5cdb1798 2005-10-29 devnull buf = s_new();
781 5cdb1798 2005-10-29 devnull for(i = 0; addr; i++){
782 5cdb1798 2005-10-29 devnull field[i] = addr;
783 5cdb1798 2005-10-29 devnull addr = strchr(addr, '!');
784 5cdb1798 2005-10-29 devnull if(addr)
785 5cdb1798 2005-10-29 devnull *addr++ = 0;
787 5cdb1798 2005-10-29 devnull if (i==1) {
788 5cdb1798 2005-10-29 devnull s_append(buf, field[0]);
789 5cdb1798 2005-10-29 devnull return buf;
793 5cdb1798 2005-10-29 devnull * count leading domain fields (non-domains don't count)
795 5cdb1798 2005-10-29 devnull for(d = 0; d<i-1; d++)
796 5cdb1798 2005-10-29 devnull if(strchr(field[d], '.')==0)
799 5cdb1798 2005-10-29 devnull * if there are more than 1 leading domain elements,
800 5cdb1798 2005-10-29 devnull * put them in as source routing
802 5cdb1798 2005-10-29 devnull if(d > 1){
803 5cdb1798 2005-10-29 devnull addhostdom(buf, field[0]);
804 5cdb1798 2005-10-29 devnull for(j=1; j<d-1; j++){
805 5cdb1798 2005-10-29 devnull s_append(buf, ",");
806 5cdb1798 2005-10-29 devnull s_append(buf, "@");
807 5cdb1798 2005-10-29 devnull s_append(buf, field[j]);
809 5cdb1798 2005-10-29 devnull s_append(buf, ":");
813 5cdb1798 2005-10-29 devnull * throw in the non-domain elements separated by '!'s
815 5cdb1798 2005-10-29 devnull s_append(buf, field[d]);
816 5cdb1798 2005-10-29 devnull for(j=d+1; j<=i-1; j++) {
817 5cdb1798 2005-10-29 devnull s_append(buf, "!");
818 5cdb1798 2005-10-29 devnull s_append(buf, field[j]);
821 5cdb1798 2005-10-29 devnull addhostdom(buf, field[d-1]);
822 5cdb1798 2005-10-29 devnull return buf;
826 5cdb1798 2005-10-29 devnull * convert header addresses to @ format.
827 5cdb1798 2005-10-29 devnull * if the address is a source address, and a domain is specified,
828 5cdb1798 2005-10-29 devnull * make sure it falls in the domain.
831 5cdb1798 2005-10-29 devnull convertheader(String *from)
833 5cdb1798 2005-10-29 devnull Field *f;
834 5cdb1798 2005-10-29 devnull Node *p, *lastp;
835 5cdb1798 2005-10-29 devnull String *a;
837 5cdb1798 2005-10-29 devnull if(!returnable(s_to_c(from))){
838 5cdb1798 2005-10-29 devnull from = s_new();
839 5cdb1798 2005-10-29 devnull s_append(from, "Postmaster");
840 5cdb1798 2005-10-29 devnull addhostdom(from, hostdomain);
842 5cdb1798 2005-10-29 devnull if(strchr(s_to_c(from), '@') == 0){
843 5cdb1798 2005-10-29 devnull a = username(from);
845 5cdb1798 2005-10-29 devnull s_append(a, " <");
846 5cdb1798 2005-10-29 devnull s_append(a, s_to_c(from));
847 5cdb1798 2005-10-29 devnull addhostdom(a, hostdomain);
848 5cdb1798 2005-10-29 devnull s_append(a, ">");
849 5cdb1798 2005-10-29 devnull from = a;
850 5cdb1798 2005-10-29 devnull } else {
851 5cdb1798 2005-10-29 devnull from = s_copy(s_to_c(from));
852 5cdb1798 2005-10-29 devnull addhostdom(from, hostdomain);
855 5cdb1798 2005-10-29 devnull from = s_copy(s_to_c(from));
856 5cdb1798 2005-10-29 devnull for(f = firstfield; f; f = f->next){
857 5cdb1798 2005-10-29 devnull lastp = 0;
858 5cdb1798 2005-10-29 devnull for(p = f->node; p; lastp = p, p = p->next){
859 5cdb1798 2005-10-29 devnull if(!p->addr)
860 5cdb1798 2005-10-29 devnull continue;
861 5cdb1798 2005-10-29 devnull a = bangtoat(s_to_c(p->s));
862 5cdb1798 2005-10-29 devnull s_free(p->s);
863 5cdb1798 2005-10-29 devnull if(strchr(s_to_c(a), '@') == 0)
864 5cdb1798 2005-10-29 devnull addhostdom(a, hostdomain);
865 5cdb1798 2005-10-29 devnull else if(*s_to_c(a) == '@')
866 5cdb1798 2005-10-29 devnull a = fixrouteaddr(a, p->next, lastp);
867 5cdb1798 2005-10-29 devnull p->s = a;
870 5cdb1798 2005-10-29 devnull return from;
873 5cdb1798 2005-10-29 devnull * ensure route addr has brackets around it
876 5cdb1798 2005-10-29 devnull fixrouteaddr(String *raddr, Node *next, Node *last)
878 5cdb1798 2005-10-29 devnull String *a;
880 5cdb1798 2005-10-29 devnull if(last && last->c == '<' && next && next->c == '>')
881 5cdb1798 2005-10-29 devnull return raddr; /* properly formed already */
883 5cdb1798 2005-10-29 devnull a = s_new();
884 5cdb1798 2005-10-29 devnull s_append(a, "<");
885 5cdb1798 2005-10-29 devnull s_append(a, s_to_c(raddr));
886 5cdb1798 2005-10-29 devnull s_append(a, ">");
887 5cdb1798 2005-10-29 devnull s_free(raddr);
888 5cdb1798 2005-10-29 devnull return a;
892 5cdb1798 2005-10-29 devnull * print out the parsed header
895 5cdb1798 2005-10-29 devnull printheader(void)
897 5cdb1798 2005-10-29 devnull int n, len;
898 5cdb1798 2005-10-29 devnull Field *f;
899 5cdb1798 2005-10-29 devnull Node *p;
900 5cdb1798 2005-10-29 devnull char *cp;
901 5cdb1798 2005-10-29 devnull char c[1];
904 5cdb1798 2005-10-29 devnull for(f = firstfield; f; f = f->next){
905 5cdb1798 2005-10-29 devnull for(p = f->node; p; p = p->next){
906 5cdb1798 2005-10-29 devnull if(p->s)
907 5cdb1798 2005-10-29 devnull n += dBprint("%s", s_to_c(p->s));
909 5cdb1798 2005-10-29 devnull c[0] = p->c;
910 5cdb1798 2005-10-29 devnull putcrnl(c, 1);
913 5cdb1798 2005-10-29 devnull if(p->white){
914 5cdb1798 2005-10-29 devnull cp = s_to_c(p->white);
915 5cdb1798 2005-10-29 devnull len = strlen(cp);
916 5cdb1798 2005-10-29 devnull putcrnl(cp, len);
917 5cdb1798 2005-10-29 devnull n += len;
919 5cdb1798 2005-10-29 devnull uneaten = p->end;
921 5cdb1798 2005-10-29 devnull putcrnl("\n", 1);
923 5cdb1798 2005-10-29 devnull uneaten++; /* skip newline */
925 5cdb1798 2005-10-29 devnull return n;
929 5cdb1798 2005-10-29 devnull * add a domain onto an name, return the new name
932 5cdb1798 2005-10-29 devnull domainify(char *name, char *domain)
934 5cdb1798 2005-10-29 devnull static String *s;
935 5cdb1798 2005-10-29 devnull char *p;
937 5cdb1798 2005-10-29 devnull if(domain==0 || strchr(name, '.')!=0)
938 5cdb1798 2005-10-29 devnull return name;
940 5cdb1798 2005-10-29 devnull s = s_reset(s);
941 5cdb1798 2005-10-29 devnull s_append(s, name);
942 5cdb1798 2005-10-29 devnull p = strchr(domain, '.');
943 5cdb1798 2005-10-29 devnull if(p == 0){
944 5cdb1798 2005-10-29 devnull s_append(s, ".");
945 5cdb1798 2005-10-29 devnull p = domain;
947 5cdb1798 2005-10-29 devnull s_append(s, p);
948 5cdb1798 2005-10-29 devnull return s_to_c(s);
952 5cdb1798 2005-10-29 devnull * print message observing '.' escapes and using \r\n for \n
955 5cdb1798 2005-10-29 devnull putcrnl(char *cp, int n)
959 5cdb1798 2005-10-29 devnull for(; n; n--, cp++){
960 5cdb1798 2005-10-29 devnull c = *cp;
961 5cdb1798 2005-10-29 devnull if(c == '\n')
962 5cdb1798 2005-10-29 devnull dBputc('\r');
963 5cdb1798 2005-10-29 devnull else if(c == '.' && last=='\n')
964 5cdb1798 2005-10-29 devnull dBputc('.');
965 5cdb1798 2005-10-29 devnull dBputc(c);
966 5cdb1798 2005-10-29 devnull last = c;
971 5cdb1798 2005-10-29 devnull * Get a line including a crnl into a string. Convert crnl into nl.
974 5cdb1798 2005-10-29 devnull getcrnl(String *s)
977 5cdb1798 2005-10-29 devnull int count;
979 5cdb1798 2005-10-29 devnull count = 0;
980 5cdb1798 2005-10-29 devnull for(;;){
981 5cdb1798 2005-10-29 devnull c = Bgetc(&bin);
982 5cdb1798 2005-10-29 devnull if(debug)
983 5cdb1798 2005-10-29 devnull Bputc(&berr, c);
984 5cdb1798 2005-10-29 devnull switch(c){
985 5cdb1798 2005-10-29 devnull case -1:
986 5cdb1798 2005-10-29 devnull s_append(s, "connection closed unexpectedly by remote system");
987 5cdb1798 2005-10-29 devnull s_terminate(s);
988 5cdb1798 2005-10-29 devnull return 0;
989 5cdb1798 2005-10-29 devnull case '\r':
990 5cdb1798 2005-10-29 devnull c = Bgetc(&bin);
991 5cdb1798 2005-10-29 devnull if(c == '\n'){
992 5cdb1798 2005-10-29 devnull s_putc(s, c);
993 5cdb1798 2005-10-29 devnull if(debug)
994 5cdb1798 2005-10-29 devnull Bputc(&berr, c);
995 5cdb1798 2005-10-29 devnull count++;
996 5cdb1798 2005-10-29 devnull s_terminate(s);
997 5cdb1798 2005-10-29 devnull return s->ptr - count;
999 5cdb1798 2005-10-29 devnull Bungetc(&bin);
1000 5cdb1798 2005-10-29 devnull s_putc(s, '\r');
1001 5cdb1798 2005-10-29 devnull if(debug)
1002 5cdb1798 2005-10-29 devnull Bputc(&berr, '\r');
1003 5cdb1798 2005-10-29 devnull count++;
1005 5cdb1798 2005-10-29 devnull default:
1006 5cdb1798 2005-10-29 devnull s_putc(s, c);
1007 5cdb1798 2005-10-29 devnull count++;
1011 5cdb1798 2005-10-29 devnull return 0;
1015 5cdb1798 2005-10-29 devnull * print out a parsed date
1018 5cdb1798 2005-10-29 devnull printdate(Node *p)
1020 5cdb1798 2005-10-29 devnull int n, sep = 0;
1022 5cdb1798 2005-10-29 devnull n = dBprint("Date: %s,", s_to_c(p->s));
1023 5cdb1798 2005-10-29 devnull for(p = p->next; p; p = p->next){
1024 5cdb1798 2005-10-29 devnull if(p->s){
1025 5cdb1798 2005-10-29 devnull if(sep == 0) {
1026 5cdb1798 2005-10-29 devnull dBputc(' ');
1029 5cdb1798 2005-10-29 devnull if (p->next)
1030 5cdb1798 2005-10-29 devnull n += dBprint("%s", s_to_c(p->s));
1032 5cdb1798 2005-10-29 devnull n += dBprint("%s", rewritezone(s_to_c(p->s)));
1033 5cdb1798 2005-10-29 devnull sep = 0;
1034 5cdb1798 2005-10-29 devnull } else {
1035 5cdb1798 2005-10-29 devnull dBputc(p->c);
1037 5cdb1798 2005-10-29 devnull sep = 1;
1040 5cdb1798 2005-10-29 devnull n += dBprint("\r\n");
1041 5cdb1798 2005-10-29 devnull return n;
1045 5cdb1798 2005-10-29 devnull rewritezone(char *z)
1047 5cdb1798 2005-10-29 devnull int mindiff;
1048 5cdb1798 2005-10-29 devnull char s;
1049 5cdb1798 2005-10-29 devnull Tm *tm;
1050 5cdb1798 2005-10-29 devnull static char x[7];
1052 5cdb1798 2005-10-29 devnull tm = localtime(time(0));
1053 5cdb1798 2005-10-29 devnull mindiff = tm->tzoff/60;
1055 5cdb1798 2005-10-29 devnull /* if not in my timezone, don't change anything */
1056 5cdb1798 2005-10-29 devnull if(strcmp(tm->zone, z) != 0)
1057 5cdb1798 2005-10-29 devnull return z;
1059 5cdb1798 2005-10-29 devnull if(mindiff < 0){
1060 5cdb1798 2005-10-29 devnull s = '-';
1061 5cdb1798 2005-10-29 devnull mindiff = -mindiff;
1063 5cdb1798 2005-10-29 devnull s = '+';
1065 5cdb1798 2005-10-29 devnull sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
1066 5cdb1798 2005-10-29 devnull return x;
1070 5cdb1798 2005-10-29 devnull * stolen from libc/port/print.c
1072 5cdb1798 2005-10-29 devnull #define SIZE 4096
1074 5cdb1798 2005-10-29 devnull dBprint(char *fmt, ...)
1076 5cdb1798 2005-10-29 devnull char buf[SIZE], *out;
1077 5cdb1798 2005-10-29 devnull va_list arg;
1080 5cdb1798 2005-10-29 devnull va_start(arg, fmt);
1081 5cdb1798 2005-10-29 devnull out = vseprint(buf, buf+SIZE, fmt, arg);
1082 5cdb1798 2005-10-29 devnull va_end(arg);
1083 5cdb1798 2005-10-29 devnull if(debug){
1084 5cdb1798 2005-10-29 devnull Bwrite(&berr, buf, (long)(out-buf));
1085 5cdb1798 2005-10-29 devnull Bflush(&berr);
1087 5cdb1798 2005-10-29 devnull n = Bwrite(&bout, buf, (long)(out-buf));
1088 5cdb1798 2005-10-29 devnull Bflush(&bout);
1089 5cdb1798 2005-10-29 devnull return n;
1093 5cdb1798 2005-10-29 devnull dBputc(int x)
1095 5cdb1798 2005-10-29 devnull if(debug)
1096 5cdb1798 2005-10-29 devnull Bputc(&berr, x);
1097 5cdb1798 2005-10-29 devnull return Bputc(&bout, x);
1101 5cdb1798 2005-10-29 devnull expand_addr(char* a)
1103 5cdb1798 2005-10-29 devnull Ndb *db;
1104 5cdb1798 2005-10-29 devnull Ndbs s;
1105 5cdb1798 2005-10-29 devnull char *sys, *ret, *proto, *host;
1107 5cdb1798 2005-10-29 devnull proto = strtok(a,"!");
1108 5cdb1798 2005-10-29 devnull if ( strcmp(proto,"net") != 0 ) {
1109 5cdb1798 2005-10-29 devnull fprint(2,"unknown proto %s\n",proto);
1111 5cdb1798 2005-10-29 devnull host = strtok(0,"!");
1112 5cdb1798 2005-10-29 devnull if ( strcmp(host,"$smtp") == 0 ) {
1113 5cdb1798 2005-10-29 devnull sys = sysname();
1114 5cdb1798 2005-10-29 devnull db = ndbopen(unsharp("#9/ndb/local"));
1115 5cdb1798 2005-10-29 devnull host = ndbgetvalue(db, &s, "sys", sys, "smtp", nil);
1117 5cdb1798 2005-10-29 devnull ret = malloc(strlen(proto)+strlen(host)+2);
1118 5cdb1798 2005-10-29 devnull sprint(ret,"%s!%s",proto,host);
1120 5cdb1798 2005-10-29 devnull return ret;