xref: /plan9-contrib/sys/src/cmd/upas/send/message.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include "common.h"
2 #include "send.h"
3 
4 /* global to this file */
5 static Reprog *rfprog;
6 static Reprog *fprog;
7 
8 #define VMLIMIT (64*1024)
9 #define MSGLIMIT (128*1024*1024)
10 
11 extern void
12 default_from(message *mp)
13 {
14 	char *cp;
15 
16 	cp = getenv("upasname");
17 	if(cp)
18 		s_append(mp->sender, cp);
19 	else
20 		s_append(mp->sender, getlog());
21 	s_append(mp->date, thedate());
22 }
23 
24 extern message *
25 m_new(void)
26 {
27 	message *mp;
28 
29 	mp = (message *)malloc(sizeof(message));
30 	if (mp == 0) {
31 		perror("message:");
32 		exit(1);
33 	}
34 	mp->sender = s_new();
35 	mp->replyaddr = s_new();
36 	mp->date = s_new();
37 	mp->body = s_new();
38 	mp->size = 0;
39 	mp->fd = -1;
40 	return mp;
41 }
42 
43 extern void
44 m_free(message *mp)
45 {
46 	if(mp->fd >= 0){
47 		close(mp->fd);
48 		remove(s_to_c(mp->tmp));
49 		s_free(mp->tmp);
50 	}
51 	s_free(mp->sender);
52 	s_free(mp->date);
53 	s_free(mp->body);
54 	free((char *)mp);
55 }
56 
57 /* read a message into a temp file , return an open fd to it */
58 static int
59 m_read_to_file(Biobuf *fp, message *mp)
60 {
61 	int fd;
62 	int n;
63 	String *file;
64 	char buf[4*1024];
65 
66 	file = s_new();
67 	/*
68 	 *  create and unlink temp file
69 	 */
70 	abspath("tmp/mtXXXXXX", MAILROOT, file);
71 	mktemp(s_to_c(file));
72 	if((fd = syscreate(s_to_c(file), 0600))<0){
73 		s_free(mp->tmp);
74 		return -1;
75 	}
76 	mp->tmp = file;
77 
78 	/*
79 	 *  read the rest into the temp file
80 	 */
81 	while((n = Bread(fp, buf, sizeof(buf))) > 0){
82 		if(write(fd, buf, n) != n){
83 			close(fd);
84 			return -1;
85 		}
86 		mp->size += n;
87 		if(mp->size > MSGLIMIT){
88 			mp->size = -1;
89 			break;
90 		}
91 	}
92 
93 	mp->fd = fd;
94 	return 0;
95 }
96 
97 /* read in a message, interpret the 'From' header */
98 extern message *
99 m_read(Biobuf *fp, int rmail)
100 {
101 	message *mp;
102 	Resub subexp[10];
103 	int first;
104 	int n;
105 
106 
107 	mp = m_new();
108 
109 	/* parse From lines if remote */
110 	if (rmail) {
111 		/* get remote address */
112 		String *sender=s_new();
113 
114 		if (rfprog == 0)
115 			rfprog = regcomp(REMFROMRE);
116 		first = 1;
117 		while(s_read_line(fp, s_restart(mp->body)) != 0) {
118 			memset(subexp, 0, sizeof(subexp));
119 			if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
120 				if(first == 0)
121 					break;
122 				if (fprog == 0)
123 					fprog = regcomp(FROMRE);
124 				memset(subexp, 0, sizeof(subexp));
125 				if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
126 					break;
127 				USE(s_restart(mp->body));
128 				append_match(subexp, s_restart(sender), SENDERMATCH);
129 				append_match(subexp, s_restart(mp->date), DATEMATCH);
130 				break;
131 			}
132 			append_match(subexp, s_restart(sender), REMSENDERMATCH);
133 			append_match(subexp, s_restart(mp->date), REMDATEMATCH);
134 			if(subexp[REMSYSMATCH].sp!=subexp[REMSYSMATCH].ep){
135 				append_match(subexp, mp->sender, REMSYSMATCH);
136 				s_append(mp->sender, "!");
137 			}
138 			first = 0;
139 		}
140 		s_append(mp->sender, s_to_c(sender));
141 		s_free(sender);
142 	}
143 	if (*s_to_c(mp->sender)=='\0')
144 		default_from(mp);
145 
146 	/*
147 	 *  read up to VMLIMIT bytes (more or less) into main memory.
148 	 *  if message is longer put the rest in a tmp file.
149 	 */
150 	mp->size = mp->body->ptr - mp->body->base;
151 	n = s_read(fp, mp->body, VMLIMIT);
152 	if(n < 0){
153 		perror("m_read");
154 		exit(1);
155 	}
156 	mp->size += n;
157 	if(n == VMLIMIT){
158 		if(m_read_to_file(fp, mp) < 0){
159 			perror("m_read");
160 			exit(1);
161 		}
162 	}
163 
164 	/*
165 	 *  ignore 0 length messages from a terminal
166 	 */
167 	if (!rmail && mp->size == 0)
168 		return 0;
169 
170 	return mp;
171 }
172 
173 /* return a piece of message starting at `offset' */
174 extern int
175 m_get(message *mp, long offset, char **pp)
176 {
177 	static char buf[4*1024];
178 
179 	/*
180 	 *  are we past eof?
181 	 */
182 	if(offset >= mp->size)
183 		return 0;
184 
185 	/*
186 	 *  are we in the virtual memory portion?
187 	 */
188 	if(offset < mp->body->ptr - mp->body->base){
189 		*pp = mp->body->base + offset;
190 		return mp->body->ptr - mp->body->base - offset;
191 	}
192 
193 	/*
194 	 *  read it from the temp file
195 	 */
196 	offset -= mp->body->ptr - mp->body->base;
197 	if(mp->fd < 0)
198 		return -1;
199 	if(seek(mp->fd, offset, 0)<0)
200 		return -1;
201 	*pp = buf;
202 	return read(mp->fd, buf, sizeof buf);
203 }
204 
205 /* output the message body without ^From escapes */
206 static int
207 m_noescape(message *mp, Biobuf *fp)
208 {
209 	long offset;
210 	int n;
211 	char *p;
212 
213 	for(offset = 0; offset < mp->size; offset += n){
214 		n = m_get(mp, offset, &p);
215 		if(n <= 0){
216 			Bflush(fp);
217 			return -1;
218 		}
219 		if(Bwrite(fp, p, n) < 0)
220 			return -1;
221 	}
222 	return Bflush(fp);
223 }
224 
225 /*
226  *  Output the message body with '^From ' escapes.
227  *  Ensures that any line starting with a 'From ' gets a ' ' stuck
228  *  in front of it.
229  */
230 static int
231 m_escape(message *mp, Biobuf *fp)
232 {
233 	char *p, *np;
234 	char *end;
235 	long offset;
236 	int m, n;
237 	char *start;
238 
239 	for(offset = 0; offset < mp->size; offset += n){
240 		n = m_get(mp, offset, &start);
241 		if(n < 0){
242 			Bflush(fp);
243 			return -1;
244 		}
245 
246 		p = start;
247 		for(end = p+n; p < end; p += m){
248 			np = memchr(p, '\n', end-p);
249 			if(np == 0){
250 				Bwrite(fp, p, end-p);
251 				break;
252 			}
253 			m = np - p + 1;
254 			if(m > 5 && strncmp(p, "From ", 5) == 0)
255 				Bputc(fp, ' ');
256 			Bwrite(fp, p, m);
257 		}
258 	}
259 	Bflush(fp);
260 	return 0;
261 }
262 
263 /* output a message */
264 extern int
265 m_print(message *mp, Biobuf *fp, char *remote, int mbox)
266 {
267 	if (remote != 0){
268 		if(print_remote_header(fp,s_to_c(mp->sender),s_to_c(mp->date),remote) < 0)
269 			return -1;
270 	} else {
271 		if(print_header(fp, s_to_c(mp->sender), s_to_c(mp->date)) < 0)
272 			return -1;
273 	}
274 
275 	if (!mbox)
276 		return m_noescape(mp, fp);
277 	return m_escape(mp, fp);
278 }
279 
280 /* print just the message body */
281 extern int
282 m_bprint(message *mp, Biobuf *fp)
283 {
284 	return m_noescape(mp, fp);
285 }
286