xref: /plan9/acme/mail/src/reply.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <thread.h>
5 #include <ctype.h>
6 #include <plumb.h>
7 #include "dat.h"
8 
9 static int	replyid;
10 
11 int
12 EQUAL(char *s, char *t)
13 {
14 	while(tolower(*s) == tolower(*t++))
15 		if(*s++ == '\0')
16 			return 1;
17 	return 0;
18 }
19 
20 void
21 mkreply(Message *m, char *label, char *to)
22 {
23 	Message *r;
24 	char *dir, *t;
25 
26 	r = emalloc(sizeof(Message));
27 	r->isreply = 1;
28 	if(m != nil)
29 		r->replyname = estrdup(m->name);
30 	r->next = replies.head;
31 	r->prev = nil;
32 	if(replies.head != nil)
33 		replies.head->prev = r;
34 	replies.head = r;
35 	if(replies.tail == nil)
36 		replies.tail = r;
37 	r->name = emalloc(strlen(mbox.name)+strlen(label)+10);
38 	sprint(r->name, "%s%s%d", mbox.name, label, ++replyid);
39 	r->w = newwindow();
40 	winname(r->w, r->name);
41 	wintagwrite(r->w, "|fmt Post", 5+4);
42 	r->tagposted = 1;
43 	threadcreate(mesgctl, r, STACK);
44 	winopenbody(r->w, OWRITE);
45 	if(to!=nil && to[0]!='\0')
46 		Bprint(r->w->body, "%s\n", to);
47 	if(m != nil){
48 		dir = estrstrdup(mbox.name, m->name);
49 		if(to == nil){
50 			/* Reply goes to replyto; Reply all goes to From and To and CC */
51 			if(strcmp(label, "Reply") == 0)
52 				Bprint(r->w->body, "To: %s\n", m->replyto);
53 			else{	/* Replyall */
54 				if(strlen(m->from) > 0)
55 					Bprint(r->w->body, "To: %s\n", m->from);
56 				if(strlen(m->to) > 0)
57 					Bprint(r->w->body, "To: %s\n", m->to);
58 				if(strlen(m->cc) > 0)
59 					Bprint(r->w->body, "CC: %s\n", m->cc);
60 			}
61 		}
62 		if(strlen(m->subject) > 0){
63 			t = "Subject: Re: ";
64 			if(strlen(m->subject) >= 3)
65 				if(tolower(m->subject[0])=='r' && tolower(m->subject[1])=='e' && m->subject[2]==':')
66 					t = "Subject: ";
67 			Bprint(r->w->body, "%s%s\n", t, m->subject);
68 		}
69 		Bprint(r->w->body, "Include: %sraw\n", dir);
70 		free(dir);
71 	}
72 	Bprint(r->w->body, "\n");
73 	if(m == nil)
74 		Bprint(r->w->body, "\n");
75 	winclosebody(r->w);
76 	if(m == nil)
77 		winselect(r->w, "0", 0);
78 	else
79 		winselect(r->w, "$", 0);
80 	winclean(r->w);
81 	windormant(r->w);
82 }
83 
84 void
85 delreply(Message *m)
86 {
87 	if(m->next == nil)
88 		replies.tail = m->prev;
89 	else
90 		m->next->prev = m->prev;
91 	if(m->prev == nil)
92 		replies.head = m->next;
93 	else
94 		m->prev->next = m->next;
95 	mesgfreeparts(m);
96 	free(m);
97 }
98 
99 enum
100 {
101 	NARGS		= 100,
102 	NARGCHAR	= 8*1024,
103 	EXECSTACK 	= STACK+(NARGS+1)*sizeof(char*)+NARGCHAR
104 };
105 
106 struct Exec
107 {
108 	char		**argv;
109 	int		p[2];
110 	Channel	*sync;
111 };
112 
113 /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
114 void
115 buildargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
116 {
117 	int i, n;
118 	char *s, *a;
119 
120 	s = args;
121 	for(i=0; i<NARGS; i++){
122 		a = inargv[i];
123 		if(a == nil)
124 			break;
125 		n = strlen(a)+1;
126 		if((s-args)+n >= NARGCHAR)	/* too many characters */
127 			break;
128 		argv[i] = s;
129 		memmove(s, a, n);
130 		s += n;
131 		free(a);
132 	}
133 	argv[i] = nil;
134 }
135 
136 void
137 execproc(void *v)
138 {
139 	struct Exec *e;
140 	int p[2];
141 	char *argv[NARGS+1], args[NARGCHAR];
142 
143 	e = v;
144 	p[0] = e->p[0];
145 	p[1] = e->p[1];
146 	rfork(RFFDG);
147 	sendul(e->sync, 1);
148 	buildargv(e->argv, argv, args);
149 	free(e->argv);
150 	chanfree(e->sync);
151 	free(e);
152 	dup(p[0], 0);
153 	close(p[1]);
154 	procexec(nil, "/bin/upas/marshal", argv);
155 	threadprint(2, "Mail: can't exec %s: %r\n", argv[0]);
156 	threadexits("can't exec");
157 }
158 
159 enum{
160 	ATTACH,
161 	BCC,
162 	CC,
163 	FROM,
164 	INCLUDE,
165 	TO,
166 };
167 
168 char *headers[] = {
169 	"attach:",
170 	"bcc:",
171 	"cc:",
172 	"from:",
173 	"include:",
174 	"to:",
175 	nil,
176 };
177 
178 int
179 whichheader(char *h)
180 {
181 	int i;
182 
183 	for(i=0; headers[i]!=nil; i++)
184 		if(EQUAL(h, headers[i]))
185 			return i;
186 	return -1;
187 }
188 
189 char *tolist[200];
190 char	*cclist[200];
191 char	*bcclist[200];
192 int ncc, nbcc, nto;
193 char	*attlist[200];
194 int	rfc822[200];
195 
196 int
197 addressed(char *name)
198 {
199 	int i;
200 
201 	for(i=0; i<nto; i++)
202 		if(strcmp(name, tolist[i]) == 0)
203 			return 1;
204 	for(i=0; i<ncc; i++)
205 		if(strcmp(name, cclist[i]) == 0)
206 			return 1;
207 	for(i=0; i<nbcc; i++)
208 		if(strcmp(name, bcclist[i]) == 0)
209 			return 1;
210 	return 0;
211 }
212 
213 char*
214 skipbl(char *s, char *e)
215 {
216 	while(s < e){
217 		if(*s!=' ' && *s!='\t' && *s!=',')
218 			break;
219 		s++;
220 	}
221 	return s;
222 }
223 
224 char*
225 findbl(char *s, char *e)
226 {
227 	while(s < e){
228 		if(*s==' ' || *s=='\t' || *s==',')
229 			break;
230 		s++;
231 	}
232 	return s;
233 }
234 
235 /*
236  * comma-separate possibly blank-separated strings in line; e points before newline
237  */
238 void
239 commas(char *s, char *e)
240 {
241 	char *t;
242 
243 	/* may have initial blanks */
244 	s = skipbl(s, e);
245 	while(s < e){
246 		s = findbl(s, e);
247 		if(s == e)
248 			break;
249 		t = skipbl(s, e);
250 		if(t == e)	/* no more words */
251 			break;
252 		/* patch comma */
253 		*s++ = ',';
254 		while(s < t)
255 			*s++ = ' ';
256 	}
257 }
258 
259 void
260 mesgsend(Message *m)
261 {
262 	char *s, *body, *to;
263 	int i, j, h, n, natt, p[2];
264 	struct Exec *e;
265 	Channel *sync;
266 	int first, nfld, delit;
267 	char *copy, *fld[100];
268 
269 	body = winreadbody(m->w, &n);
270 	/* assemble to: list from first line, to: line, and cc: line */
271 	nto = 0;
272 	natt = 0;
273 	ncc = 0;
274 	nbcc = 0;
275 	first = 1;
276 	to = body;
277 	for(;;){
278 		for(s=to; *s!='\n'; s++)
279 			if(*s == '\0'){
280 				free(body);
281 				return;
282 			}
283 		if(s++ == to)	/* blank line */
284 			break;
285 		/* make copy of line to tokenize */
286 		copy = emalloc(s-to);
287 		memmove(copy, to, s-to);
288 		copy[s-to-1] = '\0';
289 		nfld = tokenizec(copy, fld, nelem(fld), ", \t");
290 		if(nfld == 0){
291 			free(copy);
292 			break;
293 		}
294 		n -= s-to;
295 		switch(h = whichheader(fld[0])){
296 		case TO:
297 		case FROM:
298 			delit = (h == FROM);
299 			commas(to+strlen(fld[0]), s-1);
300 			for(i=1; i<nfld && nto<nelem(tolist); i++)
301 				if(!addressed(fld[i]))
302 					tolist[nto++] = estrdup(fld[i]);
303 			break;
304 		case BCC:
305 			delit = 1;
306 			commas(to+strlen(fld[0]), s-1);
307 			for(i=1; i<nfld && nbcc<nelem(bcclist); i++)
308 				if(!addressed(fld[i]))
309 					bcclist[nbcc++] = estrdup(fld[i]);
310 			break;
311 		case CC:
312 			delit = 0;
313 			commas(to+strlen(fld[0]), s-1);
314 			for(i=1; i<nfld && ncc<nelem(cclist); i++)
315 				if(!addressed(fld[i]))
316 					cclist[ncc++] = estrdup(fld[i]);
317 			break;
318 		case ATTACH:
319 		case INCLUDE:
320 			delit = 1;
321 			for(i=1; i<nfld && natt<nelem(attlist); i++){
322 				attlist[natt] = estrdup(fld[i]);
323 				rfc822[natt++] = (h == INCLUDE);
324 			}
325 			break;
326 		default:
327 			if(first){
328 				delit = 1;
329 				for(i=0; i<nfld && nto<nelem(tolist); i++)
330 					tolist[nto++] = estrdup(fld[i]);
331 			}else	/* ignore it */
332 				delit = 0;
333 			break;
334 		}
335 		if(delit){
336 			/* delete line from body */
337 			memmove(to, s, n+1);
338 		}else
339 			to = s;
340 		free(copy);
341 		first = 0;
342 	}
343 
344 	e = emalloc(sizeof(struct Exec));
345 	if(pipe(p) < 0)
346 		error("Mail: can't create pipe\n");
347 	e->p[0] = p[0];
348 	e->p[1] = p[1];
349 	e->argv = emalloc((1+4*natt+nto+ncc+nbcc+1)*sizeof(char*));
350 	e->argv[0] = estrdup("marshal");
351 	j = 1;
352 	for(i=0; i<natt; i++){
353 		if(rfc822[i]){
354 			e->argv[j++] = estrdup("-t");
355 			e->argv[j++] = estrdup("message/rfc822");
356 			e->argv[j++] = estrdup("-A");
357 		}else
358 			e->argv[j++] = estrdup("-a");
359 		e->argv[j++] = attlist[i];
360 	}
361 	for(i=0; i<nto; i++)
362 		e->argv[j++] = tolist[i];
363 	for(i=0; i<ncc; i++)
364 		e->argv[j++] = cclist[i];
365 	for(i=0; i<nbcc; i++)
366 		e->argv[j++] = bcclist[i];
367 	sync = chancreate(sizeof(int), 0);
368 	e->sync = sync;
369 	proccreate(execproc, e, EXECSTACK);
370 	recvul(sync);
371 	close(p[0]);
372 
373 	i = strlen(body);
374 	if(i > 0)
375 		write(p[1], body, i);
376 
377 	/* guarantee a blank line, to ensure attachments are separated from body */
378 	if(i==0 || body[i-1]!='\n')
379 		write(p[1], "\n\n", 2);
380 	else if(i>1 && body[i-2]!='\n')
381 		write(p[1], "\n", 1);
382 	close(p[1]);
383 	free(body);
384 
385 	if(m->replyname != nil)
386 		mesgmenumark(mbox.w, m->replyname, "\t[replied]");
387 	if(m->name[0] == '/')
388 		s = estrdup(m->name);
389 	else
390 		s = estrstrdup(mbox.name, m->name);
391 	s = egrow(s, "-R", nil);
392 	winname(m->w, s);
393 	free(s);
394 	winclean(m->w);
395 }
396