xref: /inferno-os/appl/cmd/sendmail.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Sendmail;
2
3include "sys.m";
4   	sys: Sys;
5include "draw.m";
6include "bufio.m";
7include "daytime.m";
8include "smtp.m";
9include "env.m";
10
11sprint, fprint : import sys;
12
13DEBUG : con 0;
14STRMAX : con 512;
15
16Sendmail : module
17{
18	PATH : con "/dis/sendmail.dis";
19
20	# argv is list of persons to send mail to (or nil if To: lines present in message)
21	# mail is read from standard input
22	# scans mail for headers (From: , To: , Cc: , Subject: , Re: ) where case is not sensitive
23	init: fn(ctxt : ref Draw->Context, argv : list of string);
24};
25
26init(nil : ref Draw->Context, args : list of string) {
27	from : string;
28	tos, cc : list of string = nil;
29
30  	sys = load Sys Sys->PATH;
31	smtp := load Smtp Smtp->PATH;
32  	if (smtp == nil)
33    		error(sprint("cannot load %s", Smtp->PATH), 1);
34	daytime := load Daytime Daytime->PATH;
35	if (daytime == nil)
36		error(sprint("cannot load %s", Daytime->PATH), 1);
37	msgl := readin();
38	for (ml := msgl; ml != nil; ml = tl ml) {
39		msg := hd ml;
40		lenm := len msg;
41		sol := 1;
42		for (i := 0; i < lenm; i++) {
43			if (sol) {
44				for (j := i; j < lenm; j++)
45					if (msg[j] == '\n')
46						break;
47				s := msg[i:j];
48				if (from == nil) {
49					from = match(s, "from");
50					if (from != nil)
51						from = extract(from);
52				}
53				if (tos == nil)
54					tos = lmatch(s, "to");
55				if (cc == nil)
56					cc = lmatch(s, "cc");
57				sol = 0;
58			}
59			if (msg[i] == '\n')
60				sol = 1;
61		}
62	}
63	if (tos != nil && tl args != nil)
64		error("recipients specified on To: line and as args - aborted", 1);
65	if (from == nil)
66		from = readfile("/dev/user");
67	from = adddom(from);
68	if (tos == nil)
69		tos = tl args;
70	(ok, err) := smtp->open(nil);
71  	if (ok < 0) {
72		smtp->close();
73    		error(sprint("smtp open failed: %s", err), 1);
74	}
75	dump(from, tos, cc, msgl);
76	msgl = "From " + from + "\t" + daytime->time() + "\n" :: msgl;
77	# msgl = "From: " + from + "\n" + "Date: " + daytime->time() + "\n" :: msgl;
78	(ok, err) = smtp->sendmail(from, tos, cc, msgl);
79	if (ok < 0) {
80		smtp->close();
81		error(sprint("send failed : %s", err), 0);
82	}
83	smtp->close();
84}
85
86readin() : list of string
87{
88	m : string;
89	ls : list of string;
90	nc : int;
91
92	bufio := load Bufio Bufio->PATH;
93	Iobuf : import bufio;
94	b := bufio->fopen(sys->fildes(0), Bufio->OREAD);
95	ls = nil;
96	m = nil;
97	nc = 0;
98	while ((s := b.gets('\n')) != nil) {
99		if (nc > STRMAX) {
100			ls = m :: ls;
101			m = nil;
102			nc = 0;
103		}
104		m += s;
105		nc += len s;
106	}
107	b.close();
108	if (m != nil)
109		ls = m :: ls;
110	return rev(ls);
111}
112
113match(s: string, pat : string) : string
114{
115	ls := len s;
116	lp := len pat;
117	if (ls < lp)
118		return nil;
119	for (i := 0; i < lp; i++) {
120		c := s[i];
121		if (c >= 'A' && c <= 'Z')
122			c += 'a'-'A';
123		if (c != pat[i])
124			return nil;
125	}
126	if (i < len s && s[i] == ':')
127		i++;
128	else if (i < len s - 1 && s[i] == ' ' && s[i+1] == ':')
129		i += 2;
130	else
131		return nil;
132	while (i < len s && (s[i] == ' ' || s[i] == '\t'))
133		i++;
134	j := ls-1;
135	while (j >= 0 && (s[j] == ' ' || s[j] == '\t' || s[j] == '\n'))
136		j--;
137	return s[i:j+1];
138}
139
140lmatch(s : string, pat : string) : list of string
141{
142	r := match(s, pat);
143	if (r != nil) {
144		(ok, lr) := sys->tokenize(r, " ,\t");
145		return lr;
146	}
147	return nil;
148}
149
150extract(s : string) : string
151{
152	ls := len s;
153	for(i := 0; i < ls; i++) {
154		if(s[i] == '<') {
155			for(j := i+1; j < ls; j++)
156				if(s[j] == '>')
157					break;
158			return s[i+1:j];
159		}
160	}
161	return s;
162}
163
164adddom(s : string) : string
165{
166	if (s == nil)
167		return nil;
168	for (i := 0; i < len s; i++)
169		if (s[i] == '@')
170			return s;
171	# better to get it from environment if possible
172	env := load Env Env->PATH;
173	if (env != nil && (dom := env->getenv("DOMAIN")) != nil) {
174		ldom := len dom;
175		if (dom[ldom - 1] == '\n')
176			dom = dom[0:ldom - 1];
177		return s + "@" + dom;
178	}
179	d := readfile("/usr/" + s + "/mail/domain");
180	if (d != nil) {
181		ld := len d;
182		if (d[ld - 1] == '\n')
183			d = d[0:ld - 1];
184		return s + "@" + d;
185	}
186	return s;
187}
188
189readfile(f : string) : string
190{
191  	fd := sys->open(f, sys->OREAD);
192  	if(fd == nil)
193    		return nil;
194  	buf := array[128] of byte;
195  	n := sys->read(fd, buf, len buf);
196  	if(n < 0)
197    		return nil;
198  	return string buf[0:n];
199}
200
201rev(l1 : list of string) : list of string
202{
203	l2 : list of string = nil;
204
205	for ( ; l1 != nil; l1 = tl l1)
206		l2 = hd l1 :: l2;
207	return l2;
208}
209
210lprint(fd : ref Sys->FD, ls : list of string)
211{
212	for ( ; ls != nil; ls = tl ls)
213		fprint(fd, "%s ", hd ls);
214	fprint(fd, "\n");
215}
216
217cfd : ref Sys->FD;
218
219opencons()
220{
221	if (cfd == nil)
222		cfd = sys->open("/dev/cons", Sys->OWRITE);
223}
224
225dump(from : string, tos : list of string, cc : list of string, msgl : list of string)
226{
227	if (DEBUG) {
228		opencons();
229		fprint(cfd, "from\n");
230		fprint(cfd, "%s\n", from);
231		fprint(cfd, "to\n");
232		lprint(cfd, tos);
233		fprint(cfd, "cc\n");
234		lprint(cfd, cc);
235		fprint(cfd, "message\n");
236		for ( ; msgl != nil; msgl = tl msgl) {
237			fprint(cfd, "%s", hd msgl);
238			fprint(cfd, "xxxx\n");
239		}
240	}
241}
242
243error(s : string, ex : int)
244{
245	if (DEBUG) {
246		opencons();
247		fprint(cfd, "sendmail: %s\n", s);
248	}
249	fprint(sys->fildes(2), "sendmail: %s\n", s);
250	if (ex)
251		exit;
252}
253